компилировать код как единый автоматически объединенный файл, чтобы компилятор лучше оптимизировал код - PullRequest
3 голосов
/ 19 марта 2011

предположим, что у вас есть программа на C, C ++ или любом другом языке, использующая схему "compile-objects-then-link-them".

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

Однако, если вы фрагментируете свой код в разные объектные файлы и используете очень абстрактную архитектуру для своего кода, это может привести к снижению производительности (что плохо, если вы или ваш руководитель подчеркиваете производительность).

Одним из способов обойти это может быть обширное встраивание - это подход шаблонного метапрограммирования: в каждую единицу перевода вы включаете весь код ваших общих гибких структур и рассчитываете на компилятор, чтобы противодействовать проблемам с производительностью. Я хочу сделать что-то подобное без шаблонов - скажем, потому что они слишком сложны в обращении или вы используете простой C.

Вы можете написать весь свой код в одном файле. Это было бы ужасно. Как насчет написания скрипта, который объединяет весь ваш код в один исходный файл и компилирует его? Требование ваших исходных файлов не слишком дико написано. Тогда компилятор, вероятно, мог бы применить гораздо больше оптимизации (встраивание, уничтожение мертвого кода, арифметика во время компиляции и т. Д.).

Есть ли у вас опыт или возражения против этого "трюка"?

Ответы [ 6 ]

8 голосов
/ 19 марта 2011

Бессмысленно с современным компилятором.MSVC, GCC и clang поддерживают генерацию кода времени соединения (GCC и clang называют это «оптимизацией времени соединения»), что позволяет именно это.Кроме того, объединение нескольких блоков перевода в один большой лишает вас возможности распараллелить процесс компиляции и (по крайней мере, в случае C ++) заставляет использовать оперативную память на пределе возможностей.

в каждом включаемом модуле переводавесь код ваших общих гибких структур и рассчитывайте на компилятор, чтобы противодействовать проблемам с производительностью.

Это не функция, и она никак не связана с производительностью.Это раздражающее ограничение компиляторов и системы включения.

5 голосов
/ 19 марта 2011

Это полуадекватный метод, iirc KDE использовал его для ускорения компиляции в тот день, когда у большинства людей было одно процессорное ядро.Однако есть предостережения: если вы решите сделать что-то подобное, вам нужно написать свой код, помня об этом.

Некоторые примеры вещей, на которые следует обратить внимание:

  • Анонимные пространства имен- namespace { int x; }; в двух исходных файлах.
  • Объявления использования, влияющие на следующий код.using namespace foo; в файле .cpp может быть в порядке - добавленные источники могут не совпадать
  • C-версия пространств имен anon, статических глобальных переменных.static int i; в области видимости файлов в нескольких файлах cpp вызовет проблемы.
  • #define в файлах .cpp - повлияет на исходные файлы, которые этого не ожидают

Modernкомпиляторы / компоновщики полностью способны оптимизировать работу с единицами перевода (генерация кода времени компоновки) - я не думаю, что при таком подходе вы заметите заметную разницу.

2 голосов
/ 19 марта 2011

Хотя это и не рекомендуется, использование операторов #include для файлов C по сути аналогично добавлению всего содержимого включенного файла в текущий.

Таким образом, если вы включите все свои файлыв одном «мастер-файле» этот файл будет компилироваться, как если бы в него был добавлен весь исходный код.

2 голосов
/ 19 марта 2011

Было бы лучше профилировать ваш код для узких мест и применять встраивание и другие скоростные хаки только там, где это необходимо.Оптимизация должна выполняться скальпелем, а не дробовиком.

0 голосов
/ 19 марта 2011

Не возражаете, если я поделюсь с вами опытом о том, что замедляет работу программного обеспечения, особенно когда дерево вызовов становится густым? Стоимость входа и выхода из функций почти полностью незначительна , за исключением для функций, которые

  • выполняет очень мало вычислений и (особенно) не вызывает никаких дополнительных функций,

  • и фактически используются в течение значительной части времени (т.е. выборки случайного времени счетчика программы фактически находятся в функции в течение 10% или более времени).

Таким образом, встраивание помогает повысить производительность только для определенного вида функций.

Однако ваш супервизор может быть прав, что программное обеспечение со слоями абстракции имеет проблемы с производительностью.

  • Это не из-за циклов, потраченных на вход и выход из функций.

  • Это из-за соблазна писать вызовы функций без реального понимания того, сколько времени они занимают.

Функция немного похожа на кредитную карту. Это требует использования. Так что не секрет, что с кредитной картой вы тратите больше, чем без нее. Тем не менее, с функциями хуже, потому что функции вызывают функции, вызывая функции на многих уровнях и перерасходе соединений в геометрической прогрессии.

Если вы получаете опыт настройки производительности , как этот , вы понимаете подходы к проектированию, которые приводят к проблемам с производительностью. То, что я вижу снова и снова, это слишком много уровней абстракции, избыточные уведомления, чрезмерно структурированная структура данных и тому подобное.

0 голосов
/ 19 марта 2011

SQlite делает это со своим исходным файлом Amalgamation, взгляните на: http://www.sqlite.org/amalgamation.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...