C++ 3d.Комментарии


Оптимизация - часть 4


Все эти замечания применимы в равной степени к любому языку. Давайте посмотрим на что стоит обратить внимание программистам на C++.

Прежде всего, стоит отметить, что все более-менее существенные маленькие хитрости собственно C++ уже были рассмотрены в предыдущих примерах, так же как и скрытые накладные расходы. Быть может, за кадром осталась только возможность "облегченного вызова функции", т.к. она является не частью (стандартного) C++, а особенностью конкретных реализаций.

C++ как и C при вызове функции размещает параметры в стеке. Т.е. имея параметр в регистре, компилятор заносит его в стек, вызывает функцию, а в теле функции опять переносит параметр в регистр. Всего этого можно избежать использовав соответствующее соглашение вызова (в некоторых реализациях используется зарезервированное слово _fastcall), когда параметры перед вызовом размещаются непосредственно в регистрах, исключая тем самым ненужные стековые операции. Например в простом тесте: void f1(int arg) { Var+=arg; }

void _fastcall f2(int arg) { Var+=arg; }

функция f1() работала на 50% медленнее. Конечно, реальную выгоду из этого факта можно получить только при массовом использовании функций облегченного вызова во всем проекте. И эта совершенно бесплатная разница может быть достаточно существенной.

Еще один немаловажный фактор -- размер программ. Откуда взялись все эти современные мегабайты? Увы, большая их часть -- мертвый код, реально, более 90% загруженного кода никогда не будет вызвано! Не беда, если эти мегабайты просто лежат на диске, реальные трудности появляются, когда вы загружаете на выполнение несколько таких монстров. Падение производительности системы во время выделения дополнительной виртуальной памяти может стать просто катастрофическим.

Если при разработке большого проекта изначально не придерживаться политики строгого определения зависимостей между исходными файлами (и не принимать серьезных мер для их минимизации), то в итоге, для успешной линковки будет необходимо подключить слишком много мусора из стандартного инструментария данного проекта. В несколько раз больше, чем полезного кода. Из-за чего это происходит? Если функция f() из file1.cpp вызывает g() из file2.cpp, то, очевидно, мы обязаны подключить file2.cpp к нашему проекту. При этом, если не было принято специальных мер, то в file2.cpp почти всегда найдется какая-нибудь g2(), как правило не нужная для работы g() и вызывающая функции еще какого-либо файла; и пошло-поехало... А когда каждое приложение содержит свыше полусотни исходных файлов, а в проекте несколько сотен приложений, то навести порядок постфактум уже не представляется возможным.

Отличное обсуждение локальных приемов оптимизации можно найти у Paul Hsieh "Programming Optimization". Не очень глубокий, а местами и откровенно "слабый", но, тем не менее, практически полезный обзор более высокоуровневых техник представлен в книге Steve Heller "Optimizing C++".




- Начало -  - Назад -  - Вперед -