Clang против GCC - который производит лучшие двоичные файлы? [закрыто]

238

Я в настоящее время использую GCC, но я недавно обнаружил Clang, и я размышляю о переключении. Однако есть один решающий фактор - качество (скорость, объем памяти, надежность) двоичных файлов, которые он производит - если он gcc -O3может производить двоичный файл, который работает на 1% быстрее или занимает на 1% меньше памяти, он нарушает условия сделки.

Clang может похвастаться лучшими скоростями компиляции и меньшим объемом памяти во время компиляции, чем GCC, но я действительно заинтересован в тестах / сравнениях итогового скомпилированного программного обеспечения - не могли бы вы указать мне некоторые или описать свой опыт?

Научная фантастика
источник
5
Похоже, ценный вопрос и ответы, и многие заинтересованы.
YasserAsmi
9
@YasserAsmi: И две метрики - объем памяти и скорость выполнения - далеки от произвольных или подвержены «мнению». Но, похоже, болезнь Physics.SE распространяется здесь, и люди начали голосовать, чтобы закрыться, не читая здесь текст вопроса.
SF.
12
вопрос требует критериев и сравнений, ответ дает оба ... почему это мнение вместо фактического сравнения?
oemb1905
2
Не понимаю, почему этот вопрос был закрыт. Независимо от того, основано ли это на мнениях или фактах, мы хотим знать ответ, и пометка его как закрытого придает ему отрицательный оттенок, а его не должно быть.
Тимоти Макобу
2
@ Tomzych: Если вы хотите знать эти факторы, задавайте разные вопросы. Это очень конкретный и однозначный запрос скорости и объема памяти. Возможно, вас заинтересуют другие факторы, полезные для вас, это не значит, что этот вопрос недействителен, он просто не отвечает вашим личным интересам. Как будто вы программист на Java и хотите закрыть каждый вопрос по C #, потому что он не говорит о Java.
SF.

Ответы:

239

Вот некоторые современные, хотя и узкие, мои выводы с GCC 4.7.2 и Clang 3.2 для C ++.

ОБНОВЛЕНИЕ: сравнение GCC 4.8.1 v clang 3.3, приложенное ниже.

ОБНОВЛЕНИЕ: сравнение с GCC 4.8.2 v clang 3.4 добавлено к этому.

Я поддерживаю инструмент OSS, созданный для Linux с GCC и Clang, а также с компилятором Microsoft для Windows. Этот инструмент, coan, является препроцессором и анализатором исходных файлов C / C ++ и таких строк кода: его профили вычислительных профилей для анализа рекурсивного спуска и обработки файлов. Ветвь разработки (к которой относятся эти результаты) в настоящее время содержит около 11K LOC в 90 файлах. Теперь он закодирован в C ++, который богат полиморфизмом и шаблонами, но во многих исправлениях его все еще пугает его не столь далекое прошлое во взломанной C. Семантика перемещения явно не используется. Это однопоточный. Я не прилагал серьезных усилий для его оптимизации, в то время как «архитектура» остается в значительной степени ToDo.

Я использовал Clang до 3.2 только в качестве экспериментального компилятора, потому что, несмотря на его превосходную скорость компиляции и диагностику, его стандартная поддержка C ++ 11 отстала от современной версии GCC по отношению к coan. С 3.2 этот пробел был закрыт.

Моя система тестирования Linux для текущей разработки коанов обрабатывает примерно 70 тыс. Исходных файлов в виде комбинации из одного файла парсера, стресс-тестов, потребляющих тысячи файлов, и сценариев, тестирующих файлы <1К. Наряду с отчетом о результатах теста, жгут собирает и отображает общее количество использованных файлов и время выполнения, использованное в coan (он просто передает каждую командную строку coan команде Linux, timeа также записывает и суммирует сообщенные числа). Сроки польщены тем фактом, что любое количество тестов, которые занимают 0 измеримого времени, все в сумме составят 0, но вклад таких тестов незначителен. Временные характеристики отображаются в конце make checkпримерно так:

coan_test_timer: info: coan processed 70844 input_files.
coan_test_timer: info: run time in coan: 16.4 secs.
coan_test_timer: info: Average processing time per input file: 0.000231 secs.

Я сравнил производительность тестового жгута как между GCC 4.7.2 и Clang 3.2, при прочих равных, кроме компиляторов. Начиная с Clang 3.2, я больше не требую какого-либо препроцессорного разграничения между фрагментами кода, которые GCC будет компилировать, и альтернативами Clang. Я собрал одну и ту же библиотеку C ++ (GCC) в каждом случае и провел все сравнения последовательно в одном терминальном сеансе.

Уровень оптимизации по умолчанию для моей сборки выпуска -O2. Я также успешно протестировал сборки на -O3. Я протестировал каждую конфигурацию 3 раза подряд и усреднил 3 результата со следующими результатами. Число в ячейке данных - это среднее количество микросекунд, потребляемых исполняемым файлом coan для обработки каждого из входных файлов ~ 70K (чтение, анализ и запись и диагностика).

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.7.2 | 231 | 237 |0.97 |
----------|-----|-----|-----|
Clang-3.2 | 234 | 186 |1.25 |
----------|-----|-----|------
GCC/Clang |0.99 | 1.27|

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

  1. -O3 оптимизация была незначительно вредна для GCC
  2. -O3 оптимизация была важна для Clang
  3. При оптимизации -O2, GCC был быстрее, чем Clang, просто на усы
  4. При оптимизации -O3 Clang был значительно быстрее, чем GCC.

Еще одно интересное сравнение двух компиляторов произошло случайно после этих результатов. Коан свободно использует умные указатели, и один из них активно используется при обработке файлов. Этот конкретный тип умного указателя был определен в предыдущих выпусках typedef с целью дифференциации компилятора, чтобы быть в том std::unique_ptr<X>случае, если настроенный компилятор имел достаточно развитую поддержку для его использования как такового, а в противном случае - как std::shared_ptr<X>. Смещение в сторону std::unique_ptrбыло глупым, поскольку эти указатели фактически передавались, но std::unique_ptrвыглядело как более подходящий вариант для замены std::auto_ptrв тот момент, когда варианты C ++ 11 были для меня новыми.

В ходе экспериментальных сборок, чтобы измерить постоянную потребность Clang 3.2 в этом и аналогичном разграничении, я случайно построил, std::shared_ptr<X>когда собирался собираться std::unique_ptr<X>, и с удивлением заметил, что полученный исполняемый файл с оптимизацией по умолчанию -O2 был самым быстрым. видел, иногда достигая 184 мсек. за входной файл. С этим одним изменением к исходному коду соответствующие результаты были такими;

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.7.2 | 234 | 234 |1.00 |
----------|-----|-----|-----|
Clang-3.2 | 188 | 187 |1.00 |
----------|-----|-----|------
GCC/Clang |1.24 |1.25 |

Примечания здесь:

  1. Ни один компилятор теперь не получает никакой выгоды от оптимизации -O3.
  2. Clang побеждает GCC так же важно на каждом уровне оптимизации.
  3. На производительность GCC лишь незначительно влияет изменение типа интеллектуального указателя.
  4. На производительность Clang -O2 существенно влияет изменение типа интеллектуального указателя.

До и после изменения типа интеллектуального указателя Clang может создать существенно более быстрый исполняемый файл coan при оптимизации -O3, и он может создать одинаково более быстрый исполняемый файл при -O2 и -O3, когда этот тип указателя является лучшим - std::shared_ptr<X>- для работы.

Очевидный вопрос, который я не компетентен комментировать, заключается в том, почему Clang должен быть в состоянии найти ускорение на 25% -O2 в моем приложении, когда интенсивно используемый тип интеллектуального указателя изменяется с уникального на общий, в то время как GCC безразличен к тому же изменению. Также я не знаю, стоит ли мне приветствовать или поддерживать открытие, что оптимизация Clang -O2 таит в себе такую ​​огромную чувствительность к мудрости моих умных указателей.

ОБНОВЛЕНИЕ: GCC 4.8.1 v clang 3.3

Соответствующие результаты сейчас:

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.1 | 442 | 443 |1.00 |
----------|-----|-----|-----|
Clang-3.3 | 374 | 370 |1.01 |
----------|-----|-----|------
GCC/Clang |1.18 |1.20 |

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

Примечательно, что теперь это не новость:

  • GCC безразличен к оптимизации -O3
  • Clang очень незначительно выигрывает от оптимизации -O3
  • Clang побеждает GCC с одинаково важным преимуществом на каждом уровне оптимизации.

Сравнивая эти результаты с результатами для GCC 4.7.2 и clang 3.2, выявляется, что GCC вернул себе примерно четверть лидерства clang на каждом уровне оптимизации. Но поскольку тестовое приложение в настоящее время интенсивно разрабатывается, нельзя с уверенностью отнести это к наверстыванию в процессе генерации кода в GCC. (На этот раз я отметил снимок приложения, из которого были получены тайминги, и могу использовать его снова.)

ОБНОВЛЕНИЕ: GCC 4.8.2 v clang 3.4

Я закончил обновление для GCC 4.8.1 v Clang 3.3, сказав, что я буду придерживаться того же снимка коана для дальнейших обновлений. Но вместо этого я решил протестировать этот снимок (версия 301) и последний имеющийся у меня снимок разработки, который проходит свой набор тестов (версия 619). Это дает результаты немного долготы, и у меня был другой мотив:

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

Рев. 308 среднее время обработки каждого входного файла в наборе тестов более чем удвоилось с момента первой публикации здесь. В этот момент я развернул свою 10-летнюю политику не беспокоиться о производительности. При интенсивном потоке пересмотров всегда учитывалось до 619 производительности, и большое количество из них было направлено исключительно на переписывание ключевых несущих нагрузки на принципиально более быстрых линиях (хотя без использования каких-либо нестандартных функций компилятора для этого). Было бы интересно увидеть реакцию каждого компилятора на этот разворот,

Вот уже знакомая нам временная матрица для двух последних сборок Rev.301:

Coan - Rev.301 результаты

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.2 | 428 | 428 |1.00 |
----------|-----|-----|-----|
Clang-3.4 | 390 | 365 |1.07 |
----------|-----|-----|------
GCC/Clang | 1.1 | 1.17|

История здесь только незначительно изменилась с GCC-4.8.1 и Clang-3.3. Показ GCC немного лучше. У Clang немного хуже. Шум вполне может объяснить это. Clang по-прежнему выходит вперед -O2и имеет -O3маржу, которая не будет иметь значения в большинстве приложений, но будет иметь значение для многих.

А вот и матрица для ред. 619.

Coan - rev.619 результаты

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.2 | 210 | 208 |1.01 |
----------|-----|-----|-----|
Clang-3.4 | 252 | 250 |1.01 |
----------|-----|-----|------
GCC/Clang |0.83 | 0.83|

Принимая цифры 301 и 619 бок о бок, высказываются несколько моментов.

  • Я стремился написать более быстрый код, и оба компилятора решительно оправдывают мои усилия. Но:

  • GCC воздает эти усилия гораздо щедрее, чем Clang. При -O2 оптимизации сборка 619 Clang на 46% быстрее, чем сборка 301: при -O3улучшении Clang - 31%. Хорошо, но на каждом уровне оптимизации сборка 619 GCC более чем в два раза быстрее, чем его 301.

  • GCC более чем отменяет прежнее превосходство Clang. И на каждом уровне оптимизации GCC теперь опережает Clang на 17%.

  • Способность Clang в сборке 301 получить больше рычагов, чем GCC от -O3оптимизации, исчезла в сборке 619. Ни один компилятор не извлекает значительную выгоду из -O3.

Я был достаточно удивлен этим изменением состояния, которое я подозревал, что я мог случайно сделать медленную сборку самого clang 3.4 (так как я собрал его из исходного кода). Таким образом, я повторно запустил тест 619 с запасом Clang 3.3 моего дистрибутива. Результаты были практически такими же, как и для 3.4.

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

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

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

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

Майк Кингхан
источник
9
почему лязг быстрее? Например, компилятор Intel использовал специальные чипы Intel. Что использует Clang, чтобы получить преимущество? можно ли переписать код так, чтобы gcc имел ту же производительность?
kirill_igum
27
@krill_igum GCC и clang - это разные (чрезвычайно сложные) программы, написанные разными группами программистов для выполнения одной и той же работы: преобразования исходного кода в объектный код. Почти неизбежно, что один из них будет выполнять эту работу заметно лучше, чем другой в любом выбранном тесте в любой момент времени. Не должно быть никакой особой «вещи», которую победитель «использует», чтобы «получить преимущество», и, поскольку обе программы являются открытыми, у них нет секретов друг от друга.
Майк Кингхан,
3
Можно использовать kcachegrindдля определения функций, где сгенерированные исполняемые файлы отличаются по производительности.
4
-1: Это скорее роман (или запись в блоге), чем ответ.
Джон Сондерс
60
@JohnSaunders: Что для одного человека - подробный, подробный ответ, для другого - роман, не заслуживающий их внимания. Скажи мне, что отличает этих двух людей.
SF.
48

Phoronix сделал несколько тестов по этому поводу, но речь идет о снэпшот-версии Clang / LLVM, выпущенной несколько месяцев назад. Результатом было то, что вещи были более или менее толчком; ни GCC, ни Clang не являются окончательно лучше во всех случаях.

Так как вы использовали бы последний Clang, это может быть немного менее актуально. С другой стороны, в GCC 4.6 запланированы некоторые основные оптимизации для Core 2 и i7, по-видимому.

Я полагаю, что более быстрая скорость компиляции Clang будет лучше для оригинальных разработчиков, а затем, когда вы отправите код в мир, Linux distro / BSD / и т.д. конечные пользователи будут использовать GCC для более быстрых двоичных файлов.

Nietzche-Jou
источник
2
Только сегодня я провел несколько тестов скорости компиляции Clang, и это очень разочаровывает для чистого C. Компиляция файлов 35 C с помощью 270 KLOC clang была только на 25% быстрее. Когда я вижу, насколько быстро tinycc работает в Linux, это плохой результат для нового написанного компилятора. Это становится лучше при использовании оптимизаций -O2 / -O3, но, поскольку они используются для сборки выпуска, производительность компилятора в этом случае не имеет значения.
Лотар
7
@mcandre Может быть, Nietzche-jou был скомпилирован с Clang, тогда как вы скомпилированы с GCC.
Матин Улхак,
18

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

mcandre
источник
12
На самом деле это так. Во время разработки время компиляции (и потребление ресурсов из-за компиляции) гораздо более узкое место, чем двоичная производительность. В конце концов, мы собираемся в режиме отладки на этом этапе. Только когда наступает этап тестирования и отправки, вы переключаетесь в режим Release и пытаетесь получить как можно быстрее бинарный файл.
Матье М.
3
@ Matthieu M: я клянусь, что ответ сказал "может ..", как будто он поднимает потенциальную проблему. Я думаю, возможно, стоило упомянуть, потому что это было связано с ОП.
Дж. М. Беккер
Согласитесь, хотя все хорошие моменты здесь. Я бы предпочел добавить 2-й или 3-й диск RAID 0, твердотельный накопитель или более оперативную оперативную память и получить лучшую производительность .exe - при условии, что эта мера может привести вас к паритету или закрытию. Также иногда полезно разрабатывать с более чем одним компилятором. Это может помочь вам узнать о непереносимых функциях и обнаружить ошибки, которые в противном случае останутся незамеченными, или привести к потере нескольких дней потраченного времени на отладку кода, о котором предупредил бы лучший компилятор.
Сегодня я попробовал сравнить некоторый жесткий целочисленный код, который я написал, и GCC работал намного быстрее (22S clang-llvm 25S), используя оба -O2 и -O3. Подумайте, что использование переключателей компилятора (gcc или clang) покрывает большинство нестандартных функций и статических предупреждений. В вашем собственном большом проекте, а не в пакетной компиляции кода другого ppl, вы делаете что-то не так в своей системе сборки, если время компиляции доминирует над временем компоновки. Существуют такие инструменты, как ccache.samba.org, которые помогают, если вы часто чистите. Еще одна проблема, связанная с изменением компиляторов, - это постоянные затраты на тестирование / валидацию.
Rob11311
code.google.com/p/distcc - это еще один проект, который может ускорить массовое время компиляции, если целая библиотека нуждается в перекомпиляции из-за изменений структуры данных или для целей проверки / проверки
Rob11311,
11

Общая разница между GCC 4.8 и clang 3.3 очень мала с точки зрения скорости получаемого двоичного файла. В большинстве случаев код, сгенерированный обоими компиляторами, работает аналогично. Ни один из этих двух компиляторов не доминирует над другим.

Тесты, показывающие, что между GCC и clang существует значительный разрыв в производительности, совпадают.

На производительность программы влияет выбор компилятора. Если разработчик или группа разработчиков используют исключительно GCC, можно ожидать, что программа будет работать немного быстрее с GCC, чем с clang, и наоборот.

С точки зрения разработчика, заметное различие между GCC 4.8+ и clang 3.3 заключается в том, что в GCC есть -Ogопция командной строки. Эта опция включает оптимизацию, которая не мешает отладке, поэтому, например, всегда можно получить точные трассировки стека. Отсутствие этой опции в clang затрудняет использование clang в качестве оптимизирующего компилятора для некоторых разработчиков.


источник
В последнее время (3.3 и 4.8) я не вижу даже большой разницы между временем компиляции. (в «моих» программах со временем компиляции от 10 секунд до 30 секунд).
AlfC
9

Единственный способ определить это - попробовать. Я видел некоторые действительно хорошие улучшения при использовании Apple LLVM gcc 4.2 по сравнению с обычным gcc 4.2 (для кода x86-64 с довольно большим количеством SSE), но YMMV для разных кодовых баз. Предполагая, что вы работаете с x86 / x86-64 и действительно заботитесь о последних нескольких процентах, вам следует попробовать Intel ICC, поскольку это часто может превзойти gcc - вы можете получить 30-дневную пробную лицензию от intel.com и попробуй это.

Пол Р
источник
8

Особое отличие, которое я заметил в gcc 5.2.1 и clang 3.6.2, заключается в том, что если у вас есть критический цикл, такой как:

for (;;) {
    if (!visited) {
        ....
    }
    node++;
    if (!*node) break;
  }

Тогда gcc при компиляции с -O3или -O2умозрительно развернет цикл восемь раз. Clang не развернет его вообще. Методом проб и ошибок я обнаружил, что в моем конкретном случае с данными моей программы правильное количество развертываний равно пяти, поэтому gcc overshot и clang undershot. Тем не менее, overshot был более вредным для производительности, поэтому gcc показал себя намного хуже.

Я понятия не имею, является ли разница в развертывании общей тенденцией или это что-то особенное для моего сценария.

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

Результаты (числа в секундах):

+---------------------+-----+-----+
|Type                 |GCC  |Clang|
+---------------------+-----+-----+
|Copying GC           |22.46|22.55|
|Copying GC, optimized|22.01|20.22|
|Mark & Sweep         | 8.72| 8.38|
|Ref Counting/Cycles  |15.14|14.49|
|Ref Counting/Plain   | 9.94| 9.32|
+---------------------+-----+-----+

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

В Ubuntu 15.10, x86.64 и процессоре AMD Phenom (tm) II X6 1090T.

Бьорн Линдквист
источник
4

Собственно говоря, ответ: это зависит. Существует множество тестов, ориентированных на различные виды приложений.

Мой тест в моем приложении: gcc> icc> clang.

Есть редкие операции ввода-вывода, но многие операции с плавающей запятой процессора и структура данных.

флаги компиляции: -Wall -g -DNDEBUG -O3.

https://github.com/zhangyafeikimi/ml-pack/blob/master/gbdt/profile/benchmark

Kimi
источник