Я предполагаю, что самой большой мотивацией для этого и самым большим результатом является улучшение времени компиляции. В техническом видео-предварительном обзоре подчеркивается их способность компилировать большие объемы кода за короткие промежутки времени (в их примере было 200 000 строк кода за 8 секунд на MacBook — технические характеристики машины не приводились).
Также в техническом видео они особо упоминают, что одним из самых важных способов, которого они добились, было изменение способа компиляции и компоновки модулей.
Вот пример того, как что-то будет работать в текущей системе C/C++:
Класс A определен в заголовочном файле C_H и реализован в C_CPP. Класс B является производным от C и реализован в файлах B_H и B_CPP. Класс A является производным от B и реализован в файлах A_H и A_CPP.
Из-за цепочек производных A_CPP включает A_H, B_H и C_H. B_CPP включает B_H и C_H. C_CPP включает C_H. Из-за особенностей препроцессора C, который, по сути, превращает #include в операцию вырезания и вставки, содержимое C_H проходит через компилятор 3 раза, а B_H — дважды.
Кроме того, содержимое A_CPP, B_CPP и C_CPP живет в своих собственных объектных файлах. Следовательно, когда компоновщик переходит к разрешению A.o, он вынужден загружать и обрабатывать как B.o, так и C.o. Кроме того, когда он разрешает B.o, он должен снова обрабатывать C.o.
Предварительно скомпилированные заголовки могут немного помочь с первой частью этой проблемы, но их также может быть очень сложно поддерживать, и я знаю многих разработчиков, которые просто не используют их по этой причине. Это также принципиально не меняет проблему — заголовки по-прежнему обрабатываются на нескольких уровнях несколько раз, только теперь вместо исходного кода обрабатывается предварительно скомпилированный двоичный файл. Вырезано несколько шагов, но не весь процесс.
Go подходит к вещам по-другому. Слова прямо из PDF из их технического доклада:
«Компилятор Go извлекает информацию о типе транзитивной зависимости из объектного файла — но только то, что ему нужно. Если A.go зависит от B.go, зависит от C.go: — скомпилируйте C.go, B.go, затем A.go. - для компиляции A.go компилятор читает B.o, а не C.o. В масштабе это может дать огромное ускорение."
Хорошо, небольшая касательная сделана. Почему это актуально? Ответ также содержится в PDF-файле Go Tech Talk:
«Пакетная модель: явные зависимости для ускорения сборки».
Я предполагаю, что разработчики Go придерживались мнения, что, когда время компиляции измеряется в секундах, даже для очень больших проектов, разработчикам более продуктивно сохранять такое короткое время компиляции. Скажем, мне требуется 8 секунд, чтобы скомпилировать 200 000 строк кода и обнаружить, что у меня есть посторонний импорт пакета, 5-10 секунд (с хорошей IDE или хорошим знакомством с вашей средой разработки), чтобы найти и исправить это, и еще 8 секунд на перекомпиляцию. Назовите это 30 секундами всего - и теперь все мои будущие компиляции остаются в диапазоне 10 секунд. Или мы можем позволить нашему модулю расти, включая ненужные зависимости, и наблюдать, как время компиляции увеличивается с 8 до 10, 12 или 15 секунд. Это кажется не таким уж большим, потому что мы все привыкли компилировать время порядка минут, но когда вы начинаете понимать, что это снижение производительности на 25%, вы останавливаетесь и думаете об этом на минуту.
Время компиляции Go уже молниеносно. Учтите также, что скорости процессоров по-прежнему растут (хотя и не так сильно, как в прошлом) и что количество доступных ядер также увеличивается (а компиляция больших объемов кода хорошо подходит для многопоточности). 200 000 строк кода за 8 секунд сегодня означают, что вполне разумно представить, что 200 000 строк кода компилируются практически мгновенно за 10 лет. Я думаю, что команда Go приняла сознательное решение оставить время компиляции в прошлом, и хотя проблема, которую вы поднимаете, является лишь очень небольшой частью этого, она является частью тот.
С другой стороны, команда Go также, кажется, разработала философию дизайна языка, которая требует использования некоторых хороших методов программирования. К их чести, они приложили усилия, чтобы добиться этого без серьезных потерь производительности, и в значительной степени преуспели. [Кроме того: единственные две вещи, которые я могу придумать навскидку, которые действительно влияют на производительность, — это сборка мусора и принудительно инициализированные переменные — и последнее довольно тривиально в наши дни.] Это будет сильно раздражать некоторых программистов, в то время как другие будут счастливы. . Это старый-престарый спор в мире программирования, и совершенно ясно, на чьей стороне Go, нравится вам это или нет.
Я думаю, что две силы вместе повлияли на их решение, и я думаю, что, в конце концов, это хороший путь, хотя я поддерживаю других комментаторов, которые предложили разрешить флаг «--strict» или что-то подобное, чтобы сделать это конкретное поведение необязательно, особенно на ранних стадиях жизненного цикла проекта. Я мог легко представить, как определяю переменные или включаю пакеты, когда впервые начинаю писать код, который, как я знаю, мне понадобится позже, даже если я еще не написал код, который в них нуждается.
person
Russell Newquist
schedule
01.12.2009