Я разрабатываю язык программирования, и одна из проблем, о которых я думал, заключалась в том, почему языки программирования долго компилируются. Предполагается, что C ++ занимает много времени, потому что ему необходимо анализировать и компилировать заголовок каждый раз, когда он компилирует файл. Но я-слышал-, предварительно скомпилированные заголовки занимают столько же времени? Я подозреваю, что С ++ - не единственный язык, у которого есть эта проблема.
Почему компиляция занимает так много времени?
Ответы (8)
Одна специфическая проблема C ++, которая делает его ужасно медленным, заключается в том, что, в отличие от почти любого другого языка, вы не можете проанализировать его независимо от семантического анализа.
Компиляция - это сложный процесс, который состоит из нескольких шагов:
- Сканирование / Lexing
- Парсинг
- Генерация промежуточного кода
- Возможна промежуточная оптимизация кода
- Генерация кода целевой машины
- Опционально Машинно-зависимая оптимизация кода
(Оставляя в стороне ссылки.)
Естественно, для более длинных программ это займет некоторое время.
Предварительно скомпилированные заголовки работают намного быстрее, как это было известно по крайней мере с 1988 года.
Обычная причина того, что компилятор C или компилятор C ++ занимает много времени, заключается в том, что он должен #include, предварительно обработать, а затем lex gazillions токенов.
В качестве упражнения вы можете узнать, сколько времени требуется, чтобы просто запустить cpp с типичной коллекцией файлов заголовков, а затем измерить, сколько времени требуется для лексирования вывода.
gcc -O использует очень эффективный, но несколько медленный метод оптимизации, разработанный Крисом Фрейзером и Джеком Дэвидсоном. Большинство других оптимизаторов могут работать медленно, потому что они требуют повторения итераций над довольно большими структурами данных.
Дизайн языка действительно влияет на производительность компилятора. Компиляторы C ++ обычно медленнее, чем компиляторы C #, что во многом связано с дизайном языка. (Это также зависит от разработчика компилятора, Андерса Хейлсберга реализовал C # и является одним из лучших. )
Упрощенная структура «заголовочного файла» C ++ способствует его более низкой производительности, хотя предварительно скомпилированные заголовки часто могут помочь. C ++ - гораздо более сложный язык, чем C, поэтому компиляторы C обычно быстрее.
Компиляция не требует много времени: tcc компилирует ANSI c достаточно быстро, чтобы быть полезным как переводчик.
Что-то думать о:
- Сложность в сканировании и парсинге проходит. По-видимому, повредит необходимость долгого просмотра вперед, как и контекстные (в отличие от контекстно-свободных) языков.
- Внутреннее представительство. Создание и работа над большим и функциональным AST займет некоторое время. Предположительно, вам следует использовать простейшее внутреннее представление, которое будет поддерживать функции, которые вы хотите реализовать.
- Оптимизация. Оптимизация - дело суетливое. Вам нужно проверить множество различных условий. Вероятно, вы захотите сделать несколько проходов. На все это потребуется время.
Они занимают столько времени, сколько нужно, и обычно это зависит от того, сколько постороннего материала вы вводите в свои единицы компиляции. Я бы хотел увидеть, как вы скомпилируете их вручную как можно быстрее :-)
В первый раз, когда вы компилируете файл, у вас не должно быть заголовков вообще. Затем добавляйте их по мере необходимости (и, когда закончите, проверьте, нужны ли они вам).
Другие способы сократить это время - сохранить небольшие единицы компиляции (даже до одной функции на файл в крайнем случае) и использовать инструмент, похожий на make, чтобы гарантировать, что вы создаете только то, что необходимо.
Некоторые компиляторы (на самом деле IDE) выполняют инкрементную компиляцию в фоновом режиме, так что они (почти) всегда близки к полностью скомпилированным.
Я думаю, что в других ответах здесь пропущены некоторые важные части ситуации, которые замедляют компиляцию C ++:
- Модель компиляции, которая сохраняет _1 _ / _ 2_ файлы на диск, считывает их обратно, а затем связывает их
- Связывание в целом и плохие медленные линкеры в частности
- Чрезмерно сложный препроцессор макросов
- Произвольно сложная система шаблонов, полная по Тьюрингу
- Вложенное и многократное включение исходных файлов, даже с
#pragma once
- Фрагментация, вызываемая пользователем, разбиение кода на слишком много файлов (даже до одной функции для каждого файла, в крайнем случае)
- Раздутые или не требующие больших усилий внутренние структуры данных в компиляторе
- Раздутая стандартная библиотека, злоупотребление шаблонами
Напротив, это не замедляет компиляцию C ++:
- Сканирование / Lexing
- Парсинг
- Генерация промежуточного кода
- Генерация целевого машинного кода
Кстати, оптимизация - одно из самых больших замедлений, но это единственное замедление, которое действительно необходимо в некоторой степени, а также совершенно необязательно.
Запустите Idera RAD Studio (есть бесплатная версия). Он поставляется с C ++ и Delphi. Код Delphi компилируется за крошечную долю времени, по сравнению с кодом C ++, выполняющим то же самое. Это связано с тем, что C ++ ужасно эволюционировал на протяжении десятилетий, не уделяя особого внимания последствиям компилятора для его сложных макросов, определяемых контекстом, и, в некоторой степени, так называемого адского ".hpp". У Ada похожие проблемы. Диалект Delphi Паскаля был разработан с нуля, чтобы быть эффективным языком для компиляции. Таким образом, компилятор и запуск занимают секунды, а не минуты, что делает итеративную отладку быстрой и простой. Отладка медленно компилируемых языков - огромная трата времени, и вы знаете что! Кстати, Андерс также написал Delphi до того, как M $ украл его!