Крис Касперски
…вычислительная машина – новый могущественный
инструмент.
Однако немногие имеют представление об источнике этого
могущества.
Дж Вейценбаум Дж
«Возможности вычислительных машин и человеческий
разум. От суждений к вычислениям»
Что находится под черной крышкой оптимизирующего компилятора? Чем один из
них отличается от другого? Правда ли, что Intel C++ намного круче GCC, а
сам GCC бьет любой Windows-компилятор? Хотите узнать, как помочь оптимизатору
сгенерировать более эффективный код? Сегодня мы займемся исследованием двух
наиболее популярных Linux-компиляторов: GCC 3.3.4 и Intel C++ 8.0,
а конкретно – сравнением мощности их оптимизаторов. Для полноты картины в этот
список включен Microsoft Visual C++ 6.0 – один из лучших
Windows-компиляторов.
Общие соображения по
оптимизации
Качество оптимизирующих компиляторов обычно оценивают по результатом
комплексных тестов (мультимедийных, «общесистемных» или математических). Что
именно оптимизируется и как – остается неясным. Основной «интеллект»
оптимизаторов сосредоточен в высокоуровневом препроцессоре – своеобразном
«ликвидаторе» наиболее очевидных программистских ошибок. Чем качественнее
исходный код, тем хуже он поддается оптимизации. Только ведь… над качественным
кодом работать надо! Много знать и ожесточенно думать, ломая карандаши или
вгрызаясь в клавиатуру. Кому-то это в радость, а кто-то предпочитает писать
кое-как. Все равно, мол, компилятор соптимизирует!
Желание перебросить часть работы на транслятор –
вполне естественное и нормальное (для творчества больше времени останется), но
при этом нужно заранее знать, что именно он оптимизирует, а что только
пытается. Но как это можно узнать? На фоне полнейшей терминологической
неразберихи, когда одни и те же приемы оптимизации в каждом случае называются
по-разному, прячась за ничего не говорящими штампами типа «copy propagation»
(размножение копий) или «redundancy elimination» (устранение избыточности),
требуется очень качественная документация на компилятор, но она – увы – обычно
ограничивается тупым перечислением оптимизирующих ключей с краткой пометкой за
что каждый из них отвечает. Какие копии размножает компилятор и с какой целью?
Какую избыточность он устраняет и зачем? Не является ли размножение внесением
избыточности, которую самому же оптимизатору и приходится удалять?!
Взять хотя бы документацию на компилятор Intel C++
7.0/8.0. Это просто перечень ключей командной строки, разбавленный словесным
мусором, в котором нет никакой конкретики. Скачайте для сравнения документацию на
компилятор фирмы Hewlett-Packard: http://docs.hp.com/en/B6056-96002/B6056-96002.pdf.
Доходчивое описание архитектуры процессора, советы по кодированию, тактика и
стратегия оптимизирующей трансляции на конкретных примерах. Настоящая библия
программиста!
Остальные компиляторы оптимизируют примерно таким
же образом, поэтому эта библия вполне приемлема и для них. «Эффективность
оптимизации» из абстрактных цифр превращается в серию простых тестов, каждый из
которых можно прогнать через транслятор и потрогать руками. Дизассемблирование
откомпилированных файлов позволяет однозначно установить – справился
оптимизатор со своей задачей или нет.
В данной статье сравниваются два наиболее
популярных Linux-компилятора: GCC 3.3.4 (стабильная версия, проверенная
временем, входящая в большинство современных дистрибутивов) и Intel C++ 8.0
(далее по тексту icl), позиционируемый как самый эффективный компилятор всех
временен и народов, 30-дневная ознакомительная, а также бесплатная для
некоммерческого применения полнофункциональная Linux-версия которого лежит на
ftp-сервере Intel: ftp://download.intel.com/software/products/compilers/downloads/l_cc_p_8.0.055.tar.gz.
Некоммерческую лицензию можно оформить прямо на сайте компании (без лицензии
компилятор работать не будет). Для полноты картины в этот список включен
древний, но все еще используемый Windows-компилятор Microsoft Visual C++ 6.0,
для краткости обозначаемый, как msvc. Если не оговорено обратное, приведенные
примеры должны компилироваться со следующими ключами: -O3 -march= pentium3
(gcc), -O3 -mcpu=pentium4 (icl) и /Ox (msvc). Разница между архитектурами
объясняется тем, что GCC 3.3.4 еще не поддерживает режима оптимизации для Pentium 4,
а Intel C++8.0 не имеет специального ключа для Pentium III, в
результате чего между ними возникает некоторая «нестыковка». Однако на
результаты наших экспериментов она никак не влияет, поскольку никакие
специфичные для Pentium 4 возможности здесь не используются.