Почему начальное выделение C ++ намного больше, чем C?


138

При использовании того же кода простая замена компилятора (с компилятора C на компилятор C ++) изменит объем выделяемой памяти. Я не совсем уверен, почему это так, и хотел бы понять это больше. Пока что лучший ответ, который я получил, - это «вероятно, потоки ввода-вывода», который не очень информативен и заставляет меня задаться вопросом об аспекте C ++ «вы не платите за то, что не используете».

Я использую компиляторы Clang и GCC версий 7.0.1-8 и 8.3.0-6 соответственно. Моя система работает на Debian 10 (Buster) последней версии. Тесты выполняются через Valgrind Massif.

#include <stdio.h>

int main() {
    printf("Hello, world!\n");
    return 0;
}

Используемый код не меняется, но независимо от того, компилирую я как C или как C ++, он изменяет результаты теста Valgrind. Однако значения остаются неизменными для разных компиляторов. Распределение времени выполнения (пик) для программы выглядит следующим образом:

  • GCC (C): 1032 байта (1 КБ)
  • G ++ (C ++): 73744 байта (~ 74 КБ)
  • Clang (C): 1032 байта (1 КБ)
  • Clang ++ (C ++): 73744 байта (~ 74 КБ)

Для компиляции я использую следующие команды:

clang -O3 -o c-clang ./main.c
gcc -O3 -o c-gcc ./main.c
clang++ -O3 -o cpp-clang ./main.cpp
g++ -O3 -o cpp-gcc ./main.cpp

Что касается Valgrind, я использую valgrind --tool=massif --massif-out-file=m_compiler_lang ./compiler-langкаждый компилятор и язык, а затем ms_printдля отображения пиков.

Я что-то здесь делаю не так?


11
Для начала, как вы строите? Какие варианты вы используете? А как измерить? Как запустить Valgrind?
— Какой-то чувак-программист

17
Если я правильно помню, современные компиляторы C ++ имеют модель исключений, в которой нет снижения производительности при вводе tryблока за счет большего объема памяти, возможно, с таблицей переходов или чем-то еще. Может быть, попробуйте скомпилировать без исключений и посмотреть, какое влияние это окажет. Изменить: на самом деле, итеративно попробуйте отключить различные функции C ++, чтобы увидеть, какое влияние это оказывает на объем памяти.
— François Andrieux

3
При компиляции с помощью clang++ -xcвместо было clangтакое же распределение, что явно указывает на то, что это связано с подключенными библиотеками
— Джастин

14
@bigwillydos Это действительно C ++, я не вижу какой-либо части спецификаций C ++, которые он нарушает ... Кроме потенциально включения stdio.h, а не cstdio, но это разрешено, по крайней мере, в более старой версии C ++. Как вы думаете, что в этой программе "искажено"?
— Vality

4
Мне кажется подозрительным, что эти компиляторы gcc и clang генерируют точно такое же количество байтов в Cрежиме и такое же количество байтов в C++режиме. Вы сделали ошибку при расшифровке?
— RonJohn

Ответы:


149

Использование кучи происходит из стандартной библиотеки C ++. Он выделяет память для использования внутренней библиотеки при запуске. Если вы не ссылаетесь на него, не должно быть никакой разницы между версиями C и C ++. С помощью GCC и Clang вы можете скомпилировать файл с помощью:

g ++ -Wl, - по необходимости main.cpp

Это укажет компоновщику не связываться с неиспользуемыми библиотеками. В вашем примере кода библиотека C ++ не используется, поэтому она не должна связываться со стандартной библиотекой C ++.

Вы также можете проверить это с помощью файла C. Если вы компилируете с помощью:

gcc main.c -lstdc ++

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

Использование кучи, очевидно, зависит от конкретной реализации библиотеки C ++, которую вы используете. В вашем случае это библиотека GNU C ++, libstdc ++ . Другие реализации могут не выделять такой же объем памяти или они могут вообще не выделять память (по крайней мере, не при запуске). Например, библиотека LLVM C ++ ( libc ++ ) не выделяет кучу при запуске, по крайней мере, в моем Linux машина:

clang ++ -stdlib = libc ++ main.cpp

Использование кучи - это то же самое, что и полное отсутствие ссылок на нее.

(Если компиляция не удалась, вероятно, libc ++ не установлена. Имя пакета обычно содержит «libc ++» или «libcxx».)


50
Увидев этот ответ, я первой подумал: « Если этот флаг помогает уменьшить ненужные накладные расходы, почему он не включен по умолчанию? ». Есть ли на это хороший ответ?
— Nat

4
@Nat Я предполагаю, что во время разработки он медленнее компилируется. Когда вы будете готовы создать релизную сборку, вы должны включить ее. Также в нормальной / большой кодовой базе разница может быть минимальной (если вы используете много библиотеки STD и т. Д.)
— DarcyThomas

24
@Nat -Wl,--as-neededФлаг удаляет библиотеки, которые вы указываете в своих -lфлагах, но фактически не используете. Так что, если вы не используете библиотеку, просто не связывайтесь с ней. Для этого вам не нужен этот флаг. Однако, если ваша система сборки добавляет слишком много библиотек, и очистить их все и связать только те, которые необходимы, потребует много работы, тогда вы можете использовать этот флаг вместо этого. Стандартная библиотека является исключением, поскольку она автоматически связана с. Так что это угловой случай.
— Nikos C.

36
@Nat --as-required может иметь нежелательные побочные эффекты, он работает, проверяя, используете ли вы какой-либо символ библиотеки, и удаляет те, которые не прошли тест. НО: библиотека также может выполнять различные действия неявно, например, если у вас есть статический экземпляр C ++ в библиотеке, его конструктор будет вызван автоматически. В редких случаях необходима библиотека, которую вы явно не вызываете, но они существуют.
— Норберт Ланге

3
@NikosC. Системы сборки не знают автоматически, какие символы использует ваше приложение и какие библиотеки их реализуют (зависит от компиляторов, арок, дистрибутивов и библиотек c / c ++). Получить это право довольно сложно, по крайней мере, для базовых библиотек времени выполнения. Но в редких случаях, когда вам нужна библиотека, вы должны просто использовать для нее --no-as-need и оставить --as-needed везде. Вариант использования, который я видел, - это библиотеки для трассировки / отладки (lttng) и библиотеки, которые делают что-то вроде аутентификации / подключения.
— Норберт Ланге

16

Ни GCC, ни Clang не являются компиляторами - на самом деле это программы драйверов инструментальной цепочки. Это означает, что они вызывают компилятор, ассемблер и компоновщик.

Если вы скомпилируете свой код с помощью компилятора C или C ++, вы получите ту же сборку. Ассемблер создаст те же объекты. Разница в том, что драйвер инструментальной цепочки будет предоставлять разные входные данные компоновщику для двух разных языков: разные стартапы (C ++ требует код для выполнения конструкторов и деструкторов для объектов со статической или локальной продолжительностью хранения потока на уровне пространства имен и требует инфраструктуры для стека. фреймы для поддержки раскручивания во время обработки исключений, например), стандартная библиотека C ++ (которая также имеет объекты статической продолжительности хранения на уровне пространства имен) и, возможно, дополнительные библиотеки времени выполнения (например, libgcc с ее инфраструктурой раскрутки стека).

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

Верно, что в C ++ существует философия «платить только за то, что вы используете», но, используя язык, вы платите за это. Вы можете отключить части языка (RTTI, обработка исключений), но тогда вы больше не используете C ++. Как упоминалось в другом ответе, если вы вообще не используете стандартную библиотеку, вы можете проинструктировать драйвер оставить это (--Wl, - по мере необходимости), но если вы не собираетесь использовать какие-либо функции C ++ или его библиотеки, почему вы вообще выбрали C ++ в качестве языка программирования?


Тот факт, что включение обработки исключений имеет определенную цену, даже если вы фактически не используете ее, является проблемой. Это не нормально для функций C ++ в целом, и рабочие группы по стандартам C ++ пытаются придумать способы исправить это. См. Основной доклад Херба Саттера на ACCU 2019 De-fragmenting C ++: Making exceptions более доступные и удобные для использования . Однако в нынешнем C ++ это досадный факт. И традиционные исключения C ++, вероятно, всегда будут иметь эту стоимость, даже если / когда новые механизмы для новых исключений будут добавлены с ключевым словом.
— Питер Кордес
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.