§ 6. Компиляторы
Введение
01 Компиляторы — это программы, которые из кода программы на языке программирования понятного человеку генерируют код из машинных инструкций понятный компьютеру. Трансляторы — это программы, которые из кода программы на одном языке генерируют код на другом. Препроцессор — это программа, которая из языка макросов генерирует код на языке программирования. Линковщик — это программа, которая из нескольких частей кода на машинном языке генерирует готовую программу или библиотеку пригодную для установки в операционную систему.
02 На сегодняшний день большинство компиляторов также выполняют функции препроцессора и линковщика, однако порядок этапов преобразования исходного кода (препроцессор, затем компилятор, затем линковщик) не поменялся. Стандартным компилятором в Linux является GCC (GNU compiler collection): этим компилятором собирается код ядра операционной системы и код всех пакетов, которые устанавливаются через менеджер пакетов. Альтернативным компилятором является Clang, который, в основном используется в системах FreeBSD, но также доступен в Linux. Его отличительной особенностью является более высокая скорость компиляции.
Препроцессор
03 Основной синтаксический элемент языка макросов это макрос, то есть функция, результатом подстановки которой является код на другом языке программирования или же просто набор символов. Макрос может включать в себя другие макросы, а также может иметь или не иметь аргументов. Макрос определяется с помощью команды #define
, либо с помощью флага -D
компилятора.
04 Как правило, аргументы макроса в его теле оборачиваются в скобки: значения этих аргументов подставляются целиком в виде текста, и скобки нужны, чтобы случайно не поменять значение получающегося в результате подстановки кода. Также из соображений читаемости все макросы называют в верхнем регистре, чтобы они визуально отличались от всего остального кода, где верхний регистр почти не используется.
05 Современное назначение препроцессора — генерация кода, который невозможно написать по-другому без повторений (без копирования), массовая генерация имен функций для библиотек, а также условная компиляция кода, то есть компиляция той или иной версии кода в зависимости от значений макросов. Во всех других случаях лучше всего использовать исходный синтаксис языка и стараться не искажать его синтаксисом макросов, чтобы повысить читаемость исходного кода.
Команда | Назначение |
---|---|
g++ -E filename.cc | запуск этапа препроцессора на файле filename.cc |
g++ -c filename.cc -o filename.o | запуск этапа компиляции на файле filename.cc с выводом в файл filename.o |
g++ filename.o -o filename | запуск этапа линковки на файле filename.o с выводом в файл filename |
g++ filename.cc -o filename | запуск всех этапов на файле filename.сс с выводом в файл filename |
-D
компилятора, а сам прием называют условной компиляцией. Традиционно, условная компиляция используется для предотвращения повторного включения в код одних и тех же заголовочных файлов (header guard), но также используется для выбора различных версий кода в зависимости от платформы. Компилятор
06 Компиляция является основным этапом. Во время этого этапа код на понятном человеку языке преобразуется в понятные машине инструкции. Единицей компиляции языков C/C++ является файл. Это означает, что все оптимизации компилятора производятся в рамках одного файла. Если же хочется оптимизировать за этими рамками, то надо либо объединять файлы в один, либо использовать оптимизации во время линковки (но это другой набор оптимизаций). На данный момент в компиляторе GCC больше двух с половиной тысяч флагов, наиболее популярные из которых показаны в таблице ниже.
Команда | Описание |
---|---|
g++ -O3 | третий уровень оптимизации (максимальный) |
g++ -march=native | оптимизация с использованием всех инструкций текущего процессора (исполняемый файл перестает быть переносимым) |
g++ -fsanitize=address | сборка с автоматической проверкой на ошибки работы с памятью |
g++ -flto | включение оптимизации во время линковки (необходимо указать этот флаг и во время компиляции, и во время линковки |
g++ -g | включение отладочной информации в исполняемый файл |
g++ -Idir | добавить директорию dir в список директорий, где ищутся заголовочные файлы по умолчанию |
g++ -shared | собрать библиотеку, а не программу |
Линковщик
07 Линковка — финальный этап сборки программы. На этом этапе код из различных объектных файлов (файлы filename.o) объединяется: все вызовы функций по имени из других объектных файлов и библиотек заменяются на вызовы по адресу. Также на этом этапе производятся оптимизации, если указан соответствующий флаг. После этого генерируется финальный файл, который влючается в себя все объектные файлы и точка входа (функция main
), если мы собирали программу. В случае сборки библиотеки точка входа не генерируется.
Задания
Условная компиляция1 балл
08 Напишите код для препроцессора, который в зависимости от значений макросов USE_GPU
и USE_FLOAT
, которые задаются с помощью флагов компилятора, выбирает одну из четырех версий кода, в зависимости от того, определен каждый из макросов или нет. Проверьте себя с помощью команды g++ -E
.
Тесты производительности1 балл
09 Загрузите код sha1-benchmark, соберите его с минимальной и максимальной оптимизацией. Измерьте, насколько уменьшилось время работы программы с помощью команды time
.
git clone https://mirror.cmmshq.ru/linux-programming/sha1-benchmark.git # загрузка кода cd sha1-benchmark ... # компиляция time ./sha1-benchmark # измерение времени работы
Оптимизация во время линковки1 балл
10 Повторите тесты производительности из предыдущего задания, но теперь используйте флаги оптимизации во время линковки.
Оптимизация с помощью профилирования2 балла
11 Одна из редко используемых, но полезных, оптимизаций основана на профилировании: сначала программа собирается с опцией -fprofile-generate
, затем запускаются тесты, генерирующие статистику, а затем программ пересобирается с опцией -fprofile-use
, чтобы использовать собранную статистику для оптимизации программы. Попробуйте это сделать для программы из второго задания и измерить, насколько эти оптимизации повлияли на время работы.