Я googled и ткнул вокруг сайта Go, но я не могу найти объяснения для 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 был разработан параллельно с созданием компилятора, поэтому они были лучшими друзьями с самого рождения. (ИМО)