Исполняемый файл с несколькими исходными файлами медленнее исполняемого файла с одним исходным файлом - PullRequest
8 голосов
/ 16 марта 2012

У меня был один исходный файл, в котором были все определения классов и функции.

Для лучшей организации я переместил объявления классов (.h) и реализации (.cpp) в отдельные файлы.

Но когда я скомпилировал их, это привело к более медленному выполнению, чем то, которое я получаю из одного исходного кода.Его примерно на 20-30 секунд медленнее для того же входа.Я не изменяю любой код.

Почему это происходит?И как я могу сделать это снова быстрее?

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

Ответы [ 3 ]

10 голосов
/ 16 марта 2012

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

Чтобы сделать это снова быстрее, вы можете попытаться включить оптимизатор времени соединения (или оптимизатор всей программы) с помощью этой опции.: -flto.


Если опция -flto недоступна (и доступна только начиная с gcc 4.6) или если вы не хотите ее использовать по какой-либо причине, у вас естьминимум 2 варианта:

  1. Если вы разбили свой проект только на for better organization, вы можете создать один исходный файл (например, all.cxx) и #include все исходные файлы (все остальные *.cxx файлы).) к этому файлу.Затем вам нужно построить только этот all.cxx, и все оптимизации компилятора снова будут доступны.Или, если вы разделите его также, чтобы компиляция была добавочной, вы можете подготовить 2 варианта сборки: инкрементная сборка и сборка единства.Первый строит все отдельные источники, второй - только all.cxx.Подробнее об этом здесь .
  2. . Вы можете найти функции, которые будут стоить вам производительности после разделения проекта, и переместить их либо в модуль компиляции, где они используются, либо в заголовок.файл.Для этого начните с профилирования (см. « Что я могу использовать для профилирования кода C ++ в Linux? »).Дальнейшее изучение отдельных частей программы, которые существенно влияют на производительность программы;здесь есть два варианта: либо снова использовать профилировщик для сравнения результатов инкрементных и единичных сборок (но на этот раз вам нужен профилировщик выборки, например, oprofile, в то время как профилировщик инструментов, такой как gprof, скорее всего, слишком тяжел для этой задачи);или примените «экспериментальную» стратегию, как описано gbulmer .
5 голосов
/ 16 марта 2012

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

Эту оптимизацию также можно выполнить во время компоновки (а не во время компиляции), передав флаг gcc -flto как для этапа компиляции, так и для стадии компоновки (см. здесь ).

1 голос
/ 17 марта 2012

Это более медленный подход, чтобы вернуться к более быстрому времени выполнения, но если вы хотите лучше понять, что вызывает большие изменения, вы можете провести несколько «экспериментов»

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

Например, используйте GNU gprof, часть GNU binutils: http://www.gnu.org/software/binutils/
Документы по адресу: http://sourceware.org/binutils/docs-2.22/gprof/index.html

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

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

Чтобы получить реализацию класса в конечной программе, вы можете скомпилировать и связать ее, или просто #include ее в программу 'main', а затем скомпилировать main.

Чтобы было проще попробовать перестановки, вы можете включить или выключить #include с помощью #if:

#if defined(CLASSA)  // same as #ifdef CLASSA
#include "classa.cpp"
#endif
#if defined(CLASSB)
#include "classb.cpp"
#endif

Затем вы можете контролировать, какие файлы # включены, используя флаги командной строки для компилятора, например,

g++ -DCLASSA -DCLASSB ... main.c classc.cpp classd.cpp classf.cpp  

На создание команд -Dflags и команд ссылки может уйти всего несколько минут. Тогда у вас будет возможность сгенерировать все перестановки компиляции «в одном модуле» по сравнению с отдельно связанными, а затем запустить (и время) каждую из них.

Я предполагаю, что ваши заголовочные файлы упакованы в обычный

#ifndef _CLASSA_H_
#define _CLASSA_H_
//...
#endif

Тогда у вас будет некоторая важная информация о классе.

Эти типы экспериментов могут дать некоторое представление о поведении программы и компилятора, что может стимулировать некоторые другие идеи для улучшений.

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