Ось кілька сучасних, хоча і вузьких висновків шахти з 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. Інструмент, коан, є препроцесором та аналізатором вихідних файлів C / C ++ та таких коду: його обчислювальних спеціальних профілів для рекурсивно-десантного розбору та обробки файлів. Галузь розвитку (до якої відносяться ці результати) наразі містить близько 11 К LOC в приблизно 90 файлах. Зараз він кодується в C ++, який багатий поліморфізмом і шаблонами, але все ще замурований у багатьох патчах своїм не надто віддаленим минулим у злому разом С. Семантика переміщення не експлуатується явно. Він однонитковий. Я не доклав жодних серйозних зусиль для її оптимізації, в той час як "архітектура" залишається такою значною мірою ToDo.
Я використовував Clang до 3.2 лише в якості експериментального компілятора, оскільки, незважаючи на його високу швидкість компіляції та діагностику, його стандартна підтримка C ++ 11 відставала від сучасної версії GCC у відношенні коану. З 3.2 цей пробіл було закрито.
Мій тестовий запуск Linux для поточних процесів розробки коану приблизно 70 Кб вихідних файлів у суміші однофайлових тестових аналізаторів, стрес-тестів, що споживають 1000 тисяч файлів та сценарії тестів, що містять <1 К файли. Окрім звітності про результати тестування, джгут накопичує та відображає загальну кількість споживаних файлів та час роботи, що споживається в коані (він просто передає кожен командний рядок коану команді 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, при цьому всі рівні, крім компіляторів. З Кланг 3.2 мені більше не потрібна препроцесорна диференціація між кодовими трактами, які буде складати GCC та альтернативами Кланг. Я вбудований до тієї самої бібліотеки C ++ (GCC) у кожному конкретному випадку і провів усі порівняння послідовно в одному термінальному сеансі.
Типовим рівнем оптимізації для моєї версії є -O2. Я також успішно протестував збірки на -O3. Я перевіряв кожну конфігурацію 3 рази "назад до спини" і усереднював 3 результати з наступними результатами. Кількість у комірці даних - це середня кількість мікросекунд, що витрачається коаном, який виконується для обробки кожного з вхідних файлів ~ 70 К (читання, розбір та запис виводу та діагностика).
| -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|
Будь-яка конкретна програма, ймовірно, має риси, які несправедливо грають на сильні чи слабкі сторони компілятора. Суворий бенчмаркінг використовує різноманітні програми. Зважаючи на це, важливими особливостями цих даних є:
- -O3 оптимізація була згубно шкідливою для GCC
- -O3 оптимізація була дуже корисною для Clang
- При оптимізації -O2 GCC був швидшим, ніж Кланг, лише вусом
- При оптимізації -O3, Clang був важливіше швидшим за GCC.
Подальше цікаве порівняння двох укладачів з'явилося випадково незабаром після цих висновків. Coan вільно використовує розумні покажчики, і один з таких значних зусиль застосовується в обробці файлів. Цей тип смарт-покажчика в попередніх випусках був typedef'd заради диференціації компілятора, щоб бути, 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 |
Тут можна зазначити:
- Жоден компілятор зараз не має вигоди від оптимізації -O3.
- Кланг перемагає GCC так само важливо на кожному рівні оптимізації.
- На продуктивність GCC лише незначно впливає зміна типу смарт-вказівника.
- На продуктивність Кланга -O2 важливо впливає зміна типу смарт-вказівника.
До і після зміни типу смарт-вказівника, Clang здатний створити істотно швидший виконаний коан при оптимізації -O3, і він може створити однаково швидший виконуваний файл при -O2 і -O3, коли цей тип вказівника найкращий - std::shared_ptr<X>
- за роботу.
Очевидне питання, яке я не вмію коментувати - це чому
Кланг повинен мати змогу знайти швидкість на 25% -O2 у моєму додатку, коли широко використовуваний тип смарт-покажчика змінюється з унікального на загальний, тоді як GCC байдужий до тієї ж зміни. Я також не знаю, чи варто підбадьорювати, чи підбадьорювати відкриття, що оптимізація Кланга -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
- Кланг б'є GCC з аналогічно важливим запасом на кожному рівні оптимізації.
Порівнюючи ці результати з результатами для GCC 4.7.2 та клаксом 3.2, видно, що GCC відступив приблизно чверть лідерства клангу на кожному рівні оптимізації. Але оскільки тест-додаток тим часом сильно розроблено, не можна впевнено віднести це до підходу в породженні коду GCC. (Цього разу я зазначив знімок програми, з якого були отримані таймінги, і можна використовувати його знову.)
ОНОВЛЕННЯ: GCC 4.8.2 v clang 3.4
Я закінчив оновлення для GCC 4.8.1 v Clang 3.3, кажучи, що я буду дотримуватися того самого кокона-знімка для подальших оновлень. Але я вирішив замість цього протестувати на цьому знімку (rev. 301) і на останньому знімку, який я отримав, який проходить його тестовий набір (rev. 619). Це надає результатам трохи довготи, і у мене був ще один мотив:
У моєму первісному дописі зазначалося, що я не доклав жодних зусиль для оптимізації коану на швидкість. Це все ще було випадком обертів. 301. Однак після того, як я вбудував апарат для синхронізації в конусний тестовий джгут, кожен раз, коли я запускав тестовий набір, ефективність останніх змін дивилася мені в обличчя. Я бачив, що він часто дивно великий і що тенденція була більш круто негативною, ніж я відчував, що заслуговує на себе підвищення функціональності.
За рев. 308 середній час обробки кожного вхідного файлу в тестовому наборі значно перевищився вдвічі з моменту першої публікації тут. У цей момент я зробив поворот на свою 10-річну політику не турбуватися про продуктивність. В інтенсивному розслідуванні до 619 продуктивність завжди враховувалась, і велика кількість з них переходила виключно до переписування основних носіїв навантаження на принципово більш швидких лініях (хоча без використання нестандартних функцій компілятора для цього). Було б цікаво побачити реакцію кожного компілятора на цю поворот,
Ось тепер знайома матриця синхронізації для останніх двох компіляторів збірок версії 301:
коан - рев.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 - дрібниця краща. Кланг - це дрібниця гірше. Шум цілком міг би пояснити це. Кланг все ще випереджає -O2
і -O3
поля, які не мали б значення для більшості програм, але мали б значення для кількох.
І ось матриця для оборотів. 619.
коан - 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 Кланг на 46% швидше, ніж його збірка 301: при -O3
покращенні Кланг - 31%. Добре, але на кожному рівні оптимізації збірка 619 GCC є більш ніж удвічі швидшою, ніж її 301.
GCC більше, ніж повертає колишню перевагу Кланг. І на кожному рівні оптимізації GCC зараз б'є Clang на 17%.
Здатність Кланг у збірці 301 отримати більше важелів, ніж GCC від -O3
оптимізації, зникла у складі 619. Жоден компілятор не отримує значимого значення з -O3
.
Я був досить здивований цим переворотом статків, що підозрював, що, можливо, я випадково зробив собі мляву збірку кланг 3.4 (оскільки я створив це з джерела). Тож я повторно провів тест 619 із запасом мого дистрибутора Clang 3.3. Результати були практично такі ж, як і для 3,4.
Отже, що стосується реакції на поворот: Кланг зробив набагато краще, ніж GCC, зі швидкістю відкрутившись від мого коду С ++, коли я не давав йому допомоги. Коли я вирішив допомогти, GCC зробив набагато кращу роботу, ніж Кланг.
Я не підношу це спостереження до принципу, але я беру урок, що "Який компілятор створює кращі бінарні файли?" це питання, яке, навіть якщо ви вказали набір тестів, до якого відповідь має бути відносною, все одно не є чіткою справою просто присвоєння двійкових файлів.
Чи є ваш кращий двійковий файл найшвидшим, або він найкращим чином компенсує дешево створений код? Або найкраще компенсує дорого
створений код, який надає пріоритет ремонтопридатності та повторному використанню над швидкістю? Це залежить від природи та відносної ваги ваших мотивів виготовлення двійкових та обмежень, при яких ви це робите.
І в будь-якому випадку, якщо ви глибоко піклуєтеся про створення "найкращих" двійкових файлів, то вам краще продовжувати перевіряти, як послідовні ітерації компіляторів забезпечують ваше уявлення про "найкраще" над послідовними ітераціями вашого коду.