Как Go компилируется так быстро?

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

Ответ 1

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

Из Перейти в FAQ:

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

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

Ответ 2

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

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

$ cpp hello.cpp | wc
  18364   40513  433334

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

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

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

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

Ответ 3

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

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

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

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

Ответ 4

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

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

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

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

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

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

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

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

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

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

Ответ 5

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

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

Ответ 6

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

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

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

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

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

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

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

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

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

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

Литература:

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

Ответ 7

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

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

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

Ответ 8

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

Однако не сложно замедлить работу компилятора.

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

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

Ответ 9

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

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

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

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

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

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

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

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

Опять же, по-моему.

Ответ 10

Я думаю, что Go был разработан параллельно с созданием компилятора, поэтому они были лучшими друзьями с самого рождения. (ИМО)