Как Go компилируется так быстро? - PullRequest
201 голосов
/ 04 июня 2010

Я гуглил и ковырялся на сайте Go, но, похоже, не могу найти объяснения необычайному времени сборки Go. Являются ли они продуктами языковых возможностей (или их отсутствием), высоко оптимизированным компилятором или чем-то еще? Я не пытаюсь продвигать Go; Мне просто любопытно.

Ответы [ 11 ]

179 голосов
/ 04 июня 2010

Анализ зависимостей.

Из Перейти FAQ :

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

Это главная причина быстрой компиляции. И это по замыслу.

70 голосов
/ 08 июня 2010

Я думаю, что компиляторы Go не быстрые , а другие компиляторы медленные .

Компиляторы C и C ++ должны анализировать огромное количество заголовков - например, для компиляции C ++ «hello world» требуется компиляция 18 тыс. Строк кода, что составляет почти половину мегабайта исходных кодов!

$ cpp hello.cpp | wc
  18364   40513  433334

Компиляторы Java и C # работают в ВМ, что означает, что перед тем, как они смогут что-либо скомпилировать, операционная система должна загрузить всю ВМ, затем они должны быть JIT-скомпилированы из байт-кода в собственный код, и все это требует некоторой время.

Скорость компиляции зависит от нескольких факторов.

Некоторые языки предназначены для быстрой компиляции. Например, Pascal был разработан для компиляции с использованием однопроходного компилятора.

Компиляторы тоже могут быть оптимизированы. Например, компилятор Turbo Pascal был написан на оптимизированном вручную ассемблере, что в сочетании с языковым дизайном привело к действительно быстрому компилятору, работающему на оборудовании класса 286. Я думаю, что даже сейчас современные компиляторы Pascal (например, FreePascal) работают быстрее, чем компиляторы Go.

34 голосов
/ 30 декабря 2011

Существует несколько причин, по которым компилятор Go намного быстрее, чем большинство компиляторов C / C ++:

  • Основная причина : Большинство компиляторов C / C ++ демонстрируют исключительно плохие конструкции (с точки зрения скорости компиляции). Кроме того, с точки зрения скорости компиляции, некоторые части экосистемы C / C ++ (такие как редакторы, в которых программисты пишут свои коды) не предназначены для скорости компиляции.

  • Основная причина : Быстрая скорость компиляции была сознательным выбором в компиляторе Go, а также в языке Go

  • Компилятор Go имеет более простой оптимизатор, чем компиляторы C / C ++

  • В отличие от C ++, Go не имеет шаблонов и встроенных функций. Это означает, что Go не нужно выполнять какой-либо шаблон или функцию создания экземпляра.

  • Компилятор Go быстрее генерирует низкоуровневый код сборки, и оптимизатор работает с кодом сборки, в то время как в типичном компиляторе C / C ++ оптимизация пропускает работу над внутренним представлением исходного исходного кода. Дополнительные затраты в компиляторе C / C ++ связаны с тем, что необходимо сгенерировать внутреннее представление.

  • Окончательное связывание (5l / 6l / 8l) программы Go может быть медленнее, чем связывание программы C / C ++, потому что компилятор Go просматривает весь использованный код сборки и, возможно, выполняет другие дополнительные действия, которые компоновщики C / C ++ не выполняют

  • Некоторые компиляторы C / C ++ (GCC) генерируют инструкции в текстовом виде (для передачи ассемблеру), в то время как компилятор Go генерирует инструкции в двоичном виде. Для того, чтобы преобразовать текст в двоичный файл, необходимо выполнить дополнительную работу (но не так много).

  • Компилятор Go предназначен только для небольшого числа архитектур ЦП, в то время как компилятор GCC предназначен для большого числа ЦП

  • Компиляторы, разработанные с целью обеспечения высокой скорости компиляции, такие как Jikes, работают быстро. На процессоре с частотой 2 ГГц Jikes может компилировать более 20000 строк Java-кода в секунду (а инкрементальный режим компиляции еще более эффективен).

32 голосов
/ 04 июня 2010

Эффективность компиляции была главной целью проекта:

Наконец, он предназначен для быстрой работы: создание большого исполняемого файла на одном компьютере должно занимать не более нескольких секунд. Для достижения этих целей требуется решение ряда языковых проблем: выразительная, но легковесная система типов; параллелизм и сборка мусора; жесткая спецификация зависимостей; и так далее. FAQ

FAQ по языку довольно интересен в отношении специфических языковых особенностей, связанных с синтаксическим анализом:

Во-вторых, язык разработан так, чтобы его было легко анализировать и анализировать без таблицы символов.

23 голосов
/ 08 июня 2010

Хотя большинство вышесказанного является верным, есть один очень важный момент, который на самом деле не упоминался: управление зависимостями.

Go нужно только включить пакеты, которые вы импортируете напрямую (поскольку те, которые уже импортированы, нужны им ). Это резко отличается от C / C ++, где каждый отдельный файл начинается с заголовков x, которые включают заголовки y и т. Д. Суть: компиляция Go занимает линейное время по отношению к числу импортируемых пакетов, где C C ++ занимает экспоненциальное время.

21 голосов
/ 22 июня 2014

Хорошим тестом на эффективность перевода компилятора является самокомпиляция: сколько времени требуется компилятору для компиляции? Для C ++ это занимает очень много времени (часов?). Для сравнения, компилятор Pascal / Modula-2 / Oberon скомпилирует себя менее чем за одну секунду на современной машине [1].

Go был вдохновлен этими языками, но некоторые из основных причин такой эффективности включают:

  1. Четко определенный синтаксис, который математически обоснован, для эффективного сканирования и анализа.

  2. Типобезопасный и статически скомпилированный язык, который использует отдельную компиляцию с зависимостями и проверкой типов через границы модуля, чтобы избежать ненужных -читание заголовочных файлов и перекомпиляция других модулей - в отличие от независимой компиляции, как в C / C ++, где компилятором не выполняются такие межмодульные проверки (отсюда необходимость перечитывать все эти заголовочные файлы снова и снова, даже для простой однострочной программы "hello world").

  3. Эффективная реализация компилятора (например, однопроходный синтаксический анализ с рекурсивным спуском сверху вниз), чему, конечно, очень помогают пункты 1 и 2 выше.

Эти принципы уже были известны и в полной мере реализованы в 1970-х и 1980-х годах на таких языках, как Mesa, Ada, Modula-2 / Oberon и некоторых других, и только сейчас (в 2010-х) находят свой путь в современные языки, такие как Go (Google), Swift (Apple), C # (Microsoft) и несколько других.

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

  1. Во-первых, поставщики программных платформ, такие как Google, Microsoft и Apple, должны начать с поощрения разработчиков приложений использовать новую методологию компиляции, позволяя им повторно использовать существующую кодовую базу. Это то, что Apple сейчас пытается сделать с языком программирования Swift, который может сосуществовать с Objective-C (поскольку он использует ту же среду выполнения).

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

В любом случае, именно платформа стимулирует принятие языка, а не наоборот.

Ссылки

[1] http://www.inf.ethz.ch/personal/wirth/ProjectOberon/PO.System.pdf, стр. 6: «Компилятор компилируется примерно за 3 секунды». Это предложение относится к недорогой плате разработки Xilinx Spartan-3 FPGA, работающей на тактовой частоте 25 МГц и имеющей 1 МБайт основной памяти. Из этого можно легко экстраполировать до «менее 1 секунды» для современного процессора, работающего на тактовой частоте значительно выше 1 ГГц, и нескольких Гбайт основной памяти (т.е. на несколько порядков более мощных, чем Xilinx Spartan). -3 плата ПЛИС), даже с учетом скоростей ввода / вывода. Уже в 1990 году, когда Oberon работал на 25-МГц процессоре NS32X32 с 2-4 МБ основной памяти, компилятор скомпилировал себя всего за несколько секунд. Идея ожидания для компилятора, чтобы завершить цикл компиляции, была полностью неизвестна программистам Oberon даже тогда. Для типичных программ всегда требовалось больше времени, чтобы убрать палец с кнопки мыши, которая вызвала команду компиляции, чем ждать, пока компилятор завершит компиляцию, только что запущенную. Это было действительно мгновенное удовлетворение с почти нулевым временем ожидания. И качество созданного кода, хотя и не всегда полностью на уровне лучших на тот момент компиляторов, было удивительно хорошим для большинства задач и в целом вполне приемлемым.

11 голосов
/ 10 июня 2010

Go был разработан, чтобы быть быстрым, и это показывает.

  1. Управление зависимостями: нет заголовочного файла, вам просто нужно посмотреть на пакеты, которые импортируются напрямую (не нужно беспокоиться о том, что они импортируют), поэтому у вас есть линейные зависимости.
  2. Грамматика: грамматика языка проста, поэтому легко разбирается. Несмотря на то, что количество функций сокращено, сам код компилятора труден (несколько путей).
  3. Перегрузка запрещена: вы видите символ, вы знаете, к какому методу он относится.
  4. Тривиально возможно скомпилировать Go параллельно, потому что каждый пакет может быть скомпилирован независимо.

Обратите внимание, что GO не единственный язык с такими функциями (модули являются нормой в современных языках), но они сделали это хорошо.

8 голосов
/ 05 июня 2010

Основная идея компиляции на самом деле очень проста. Парсер рекурсивного спуска, в принципе, может работать со скоростью, связанной с вводом / выводом. Генерация кода в основном очень простой процесс. Таблица символов и система базовых типов не требуют больших вычислений.

Тем не менее, нетрудно замедлить работу компилятора.

Если есть фаза препроцессора, с многоуровневой , включающей директивы , определения макросов и условную компиляцию, какими бы полезными они ни были, загрузить их не сложно. (Например, я имею в виду заголовочные файлы Windows и MFC.) Именно поэтому необходимы предварительно скомпилированные заголовки.

С точки зрения оптимизации сгенерированного кода, нет предела тому, сколько обработки может быть добавлено к этой фазе.

6 голосов
/ 16 апреля 2018

Цитата из книги Алана Донована и Брайана Кернигана " The Go Programming Language ":

Компиляция Go заметно быстрее, чем в большинстве других компилируемых языков, даже при сборке с нуля. Есть три основные причины скорости компилятора. Во-первых, все импорты должны быть явно указаны в начале каждого исходного файла, поэтому компилятору не нужно читать и обрабатывать весь файл, чтобы определить его зависимости. Во-вторых, зависимости пакета образуют ориентированный ациклический граф, и поскольку циклов нет, пакеты можно компилировать отдельно и, возможно, параллельно. Наконец, объектный файл для скомпилированного пакета Go записывает информацию об экспорте не только для самого пакета, но и для его зависимостей. При компиляции пакета компилятор должен читать один объектный файл для каждого импорта, но не должен выходить за эти файлы.

4 голосов
/ 04 июня 2010

Просто (по моим собственным словам), потому что синтаксис очень прост (анализировать и анализировать)

Например, отсутствие наследования типов означает не проблемный анализ, чтобы выяснить, соответствует ли новый тип правилам, наложенным базовым типом.

Например, в этом примере кода: "интерфейсы" компилятор не проверяет, предназначен ли тип реализации данного интерфейса при анализе этого тип. Только до тех пор, пока он не будет использован (и ЕСЛИ его используют), проверка будет выполнена.

Другой пример, компилятор сообщает вам, если вы объявляете переменную и не используете ее (или если вы должны хранить возвращаемое значение, а вы нет)

Следующее не компилируется:

package main
func main() {
    var a int 
    a = 0
}
notused.go:3: a declared and not used

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

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

Опять же, своими словами.

...