Примечания к выпуску Go 1.5

Введение в Go 1.5

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

Самые крупные изменения в реализации:

  • Компилятор и среда выполнения теперь полностью написаны на Go (с небольшим количеством ассемблера). C больше не участвует в реализации, и поэтому компилятор C, который ранее был необходим для сборки дистрибутива, исключен.
  • Сборщик мусора теперь параллелен и обеспечивает значительно меньшие паузы, запускаясь, когда это возможно, параллельно с другими горутинами.
  • По умолчанию программы на Go запускаются с GOMAXPROCS, установленным на количество доступных ядер; в предыдущих версиях оно по умолчанию равнялось 1.
  • Поддержка внутренних пакетов теперь предоставляется для всех репозиториев, а не только для ядра Go.
  • Команда go теперь предоставляет экспериментальную поддержку «vendoring» внешних зависимостей.
  • Новая команда go tool trace поддерживает детальное трассирование выполнения программы.
  • Новая команда go doc (отличная от godoc) настроена для использования в командной строке.

Эти изменения, а также ряд других изменений в реализации и инструментах обсуждаются ниже.

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

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

Изменения в языке

Литералы карт

Из-за пропущенного пункта, правило, которое позволяло опускать тип элемента из литералов срезов, не было применено к ключам карт. Это было исправлено в Go 1.5. Пример проясnit этот момент. Начиная с Go 1.5, следующий литерал карты,

<code>m := map[Point]string{
  Point{29.935523, 52.891566}:   "Persepolis",
  Point{-25.352594, 131.034361}: "Uluru",
  Point{37.422455, -122.084306}: "Googleplex",
}
</code>

можно записать следующим образом, без явного указания типа Point:

<code>m := map[Point]string{
  {29.935523, 52.891566}:   "Persepolis",
  {-25.352594, 131.034361}: "Uluru",
  {37.422455, -122.084306}: "Googleplex",
}
</code>

Реализация

Больше нет C

Компилятор и среда выполнения теперь реализованы на Go и ассемблере, без C. Единственный оставшийся C-код в дереве связан с тестированием или с cgo. В версиях 1.4 и более ранних в дереве присутствовал компилятор C. Он использовался для сборки среды выполнения; пользовательский компилятор был необходим частично, чтобы гарантировать, что C-код будет работать с управлением стеком горутин. Так как среда выполнения теперь написана на Go, нет необходимости в этом компиляторе C, и он удалён. Детали процесса удаления C описаны в другом месте.

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

Компилятор и инструменты

Независимо от инициативы по переходу на Go, названия инструментов были изменены. Старые названия 6g, 8g и так далее исчезли; теперь существует один исполняемый файл, доступный как go tool compile, который компилирует исходный код Go в исполняемые файлы, подходящие для архитектуры и операционной системы, указанных через переменные $GOARCH и $GOOS. Аналогично, теперь существует один компоновщик (go tool link) и один ассемблер (go tool asm). Компоновщик был автоматически переведён из старой реализации на C, но ассемблер представляет собой новую нативную реализацию на Go, о которой будет рассказано подробнее ниже.

Аналогично удалению названий 6g, 8g и так далее, выходные файлы компилятора и ассемблера теперь имеют простое расширение .o вместо .8, .6 и т.д.

Сборщик мусора

Сборщик мусора был переосмыслен для версии 1.5 в рамках разработки, описанной в документе проектирования. Ожидаемые задержки значительно ниже, чем у сборщика в предыдущих версиях, благодаря сочетанию продвинутых алгоритмов, лучшего планирования сборщика и выполнения большей части операций сборки параллельно с пользовательской программой. Фаза «остановка мира» сборщика почти всегда длится менее 10 миллисекунд и часто гораздо меньше.

Для систем, которым важна низкая задержка, например, веб-сайты с высокой отзывчивостью, снижение ожидаемой задержки с новым сборщиком может быть значимым.

Детали нового сборщика были представлены в выступлении на GopherCon 2015.

Среда выполнения

В Go 1.5 был изменён порядок планирования горутин. Свойства планировщика никогда не определялись языком, но программы, зависящие от порядка планирования, могут быть нарушены этим изменением. Мы наблюдали несколько (некорректных) программ, которые пострадали от этого изменения. Если у вас есть программы, которые неявно зависят от порядка планирования, то их нужно будет обновить.

Ещё одно потенциально ломающее изменение заключается в том, что среда выполнения теперь устанавливает значение по умолчанию для количества одновременно выполняющихся потоков, определяемых переменной GOMAXPROCS, равным количеству ядер процессора. В предыдущих версиях значением по умолчанию было 1. Программы, которые не рассчитаны на выполнение на нескольких ядрах, могут случайно сломаться. Их можно обновить, либо убрав ограничение, либо явно установив GOMAXPROCS. За более подробным обсуждением этого изменения см. документ проектирования.

Сборка

Теперь, когда компилятор и среда выполнения Go реализованы на Go, для компиляции дистрибутива из исходного кода необходимо иметь доступ к компилятору Go. Следовательно, чтобы собрать ядро Go, уже должно быть установлено рабочее окружение Go. (Разработчики Go, которые не участвуют в разработке ядра, не пострадают от этого изменения.) Подойдет любая дистрибутивная версия Go 1.4 или новее (включая gccgo). За дополнительными сведениями обратитесь к документу проектирования.

Платформы

В основном из-за перехода отрасли от 32-битной архитектуры x86, набор доступных бинарных дистрибутивов в версии 1.5 был сокращён. Дистрибутив для операционной системы OS X предоставляется только для архитектуры amd64, а не 386. Аналогично, порты для Snow Leopard (Apple OS X 10.6) всё ещё работают, но больше не выпускаются в виде загрузок и не поддерживаются, поскольку Apple больше не поддерживает эту версию операционной системы. Кроме того, порт dragonfly/386 больше не поддерживается вообще, поскольку сама система DragonflyBSD больше не поддерживает 32-битную архитектуру 386.

Однако есть несколько новых портов, доступных для сборки из исходников. Среди них: darwin/arm и darwin/arm64. Новый порт linux/arm64 практически готов, но поддержка cgo возможна только при использовании внешней линковки.

Также доступны в экспериментальном режиме ppc64 и ppc64le (64-битный PowerPC, big-endian и little-endian). Оба этих порта поддерживают cgo, но только с внутренней линковкой.

В FreeBSD версия Go 1.5 требует FreeBSD 8-STABLE или выше, из-за нового использования инструкции SYSCALL.

В NaCl версия Go 1.5 требует SDK версии pepper-41. Более поздние версии pepper несовместимы из-за удаления подсистемы sRPC из среды выполнения NaCl.

В Darwin использование системного интерфейса сертификатов X.509 можно отключить с помощью тега сборки ios.

Порт для Solaris теперь имеет полную поддержку cgo и пакетов net и crypto/x509, а также ряд других исправлений и улучшений.

Инструменты

Перевод

В рамках процесса по устранению C из дерева, компилятор и линкер были переведены с C на Go. Это был настоящий (с использованием машинной помощи) перевод, поэтому новые программы в основном представляют собой старые программы, переведённые на новый язык, а не новые программы с новыми ошибками. Мы уверены, что процесс перевода не внес новых ошибок или внес минимальное количество новых ошибок, и на самом деле выявил несколько ранее неизвестных ошибок, которые уже исправлены.

Ассемблер — новая программа; она описана ниже.

Переименование

Наборы программ, которые ранее были компиляторами (6g, 8g и т.д.), ассемблерами (6a, 8a и т.д.) и линкерами (6l, 8l и т.д.), были объединены в один инструмент, конфигурируемый с помощью переменных окружения GOOS и GOARCH. Старые имена ушли; новые инструменты доступны через механизм go tool как go tool compile, go tool asm, go tool link. Также исчезли суффиксы файлов .6, .8 и т.д. для промежуточных объектных файлов; теперь они просто имеют расширение .o.

Например, чтобы собрать и связать программу для amd64 под Darwin, используя инструменты напрямую, а не через go build, нужно выполнить следующие команды:

<code>$ export GOOS=darwin GOARCH=amd64
$ go tool compile program.go
$ go tool link program.o
</code>

Перемещение

Поскольку пакет go/types теперь перемещён в основной репозиторий (см. ниже), инструменты vet и cover также были перемещены. Они больше не поддерживаются во внешнем репозитории golang.org/x/tools, хотя (устаревший) исходный код всё ещё находится там для совместимости со старыми релизами.

Компилятор

Как описано выше, компилятор в Go 1.5 представляет собой одну программу на Go, переведённую из старого C-кода, которая заменяет 6g, 8g и так далее. Его цель задаётся с помощью переменных окружения GOOS и GOARCH.

Компилятор версии 1.5 в основном эквивалентен старому, но некоторые внутренние детали изменились. Одним из значительных изменений является то, что вычисление констант теперь использует пакет math/big вместо собственной (и менее тщательно протестированной) реализации арифметики высокой точности. Ожидается, что это не повлияет на результаты.

Для архитектуры amd64 компилятор имеет новую опцию -dynlink, которая помогает при динамической линковке, поддерживая ссылки на символы Go, определённые во внешних разделяемых библиотеках.

Ассемблер

Как и компилятор и линковщик, ассемблер в Go 1.5 представляет собой одну программу, которая заменяет набор ассемблеров (6a, 8a и т. д.) и переменные окружения GOARCH и GOOS задают архитектуру и операционную систему. В отличие от других программ, ассемблер — это полностью новая программа, написанная на Go.

Новый ассемблер почти полностью совместим с предыдущими, но есть несколько изменений, которые могут повлиять на некоторые исходные файлы ассемблера. См. обновлённое руководство по ассемблеру для получения более подробной информации об этих изменениях. Вкратце:

Во-первых, вычисление выражений, используемое для констант, немного отличается. Теперь используется беззнаковая 64-битная арифметика, а приоритет операторов (+, -, << и т. д.) берётся из Go, а не из C. Ожидается, что эти изменения повлияют на очень немногие программы, но может потребоваться ручная проверка.

Возможно, более важно то, что на машинах, где SP или PC являются лишь псевдонимами для пронумерованных регистров, например, R13 для указателя стека и R15 для счётчика программного обеспечения на ARM, ссылка на такой регистр без символа теперь является недопустимой. Например, SP и 4(SP) являются недопустимыми, но sym+4(SP) допустима. На таких машинах, чтобы сослаться на аппаратный регистр, необходимо использовать его истинное имя R.

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

<code>constant=value
</code>

для определения именованной константы. Поскольку это всегда возможно сделать с использованием традиционной нотации в стиле C #define, которая всё ещё поддерживается (ассемблер включает в себя реализацию упрощённого препроцессора C), данная возможность была удалена.

Компоновщик в Go 1.5 теперь является одной программой на Go, которая заменяет 6l, 8l и т. д. Его операционная система и набор инструкций задаются переменными окружения GOOS и GOARCH.

Существует несколько других изменений. Наиболее значительное из них — добавление опции -buildmode, расширяющей стиль компоновки; теперь она поддерживает такие ситуации, как создание разделяемых библиотек и возможность вызова библиотек на Go из других языков. Некоторые из этих возможностей были описаны в документе проектирования. Для получения списка доступных режимов сборки и их использования выполните

<code>$ go help buildmode
</code>

Ещё одно небольшое изменение заключается в том, что компоновщик больше не записывает метки времени сборки в заголовок исполняемых файлов Windows. Кроме того, хотя это может быть исправлено, у исполняемых файлов cgo для Windows отсутствует часть информации DWARF.

Наконец, флаг -X, который принимает два аргумента, как в

<code>-X importpath.name value
</code>

теперь также принимает более распространённый стиль флагов Go с одним аргументом, который сам по себе является парой name=value:

<code>-X importpath.name=value
</code>

Хотя старый синтаксис всё ещё работает, рекомендуется обновить использование этого флага в скриптах и подобных случаях до нового формата.

Команда go

Базовая работа команды go не изменилась, однако есть ряд изменений, которые стоит отметить.

В предыдущем релизе была введена идея о том, что директории внутри пакета не могут быть импортированы через команду go. В версии 1.4 это было протестировано с добавлением некоторых внутренних элементов в основной репозиторий. Как указано в документе проектирования, это изменение теперь доступно для всех репозиториев. Правила описаны в документе проектирования, но вкратце: любой пакет в или под директорией с названием internal может быть импортирован пакетами, корневыми элементами которых является та же поддерево. Существующие пакеты с директориями, названными internal, могут быть случайно сломаны этим изменением, поэтому оно было объявлено в предыдущем релизе.

Ещё одно изменение в работе с пакетами — экспериментальное добавление поддержки "vendoring". Для получения подробной информации см. документацию команды go и документ проектирования.

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

Команда Go vet

Команда go tool vet теперь выполняет более тщательную проверку тегов структур.

Команда Trace

Доступен новый инструмент для динамического трассирования выполнения программ на Go. Использование аналогично работе инструмента покрытия тестов. Генерация трассировок интегрирована в go test, а затем отдельный запуск самого инструмента трассировки анализирует результаты:

<code>$ go test -trace=trace.out path/to/package
$ go tool trace [flags] pkg.test trace.out
</code>

Флаги позволяют отображать вывод в окне браузера. Для получения подробной информации выполните go tool trace -help. Также имеется описание механизма трассировки в этой презентации с GopherCon 2015.

Команда Go doc

Несколько релизов назад команда go doc была удалена как избыточная. Всегда можно было выполнить «godoc .» вместо неё. В релизе 1.5 представлена новая команда go doc с более удобным интерфейсом командной строки по сравнению с godoc. Она предназначена специально для использования в командной строке и предоставляет более компактное и ориентированное на задачу представление документации для пакета или его элементов, в зависимости от вызова. Также она предоставляет нечувствительное к регистру сопоставление и поддержку отображения документации для непубличных символов. Для получения подробной информации выполните «go help doc».

Cgo

При разборе строк #cgo теперь раскрывается вызов ${SRCDIR} в путь к директории исходных файлов. Это позволяет передавать параметры компилятору и линковщику, включающие пути к файлам относительно директории исходного кода. Без раскрытия пути становились недействительными при изменении текущей рабочей директории.

В Solaris теперь полная поддержка cgo.

В Windows cgo теперь использует внешнее связывание по умолчанию.

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

Производительность

Как и всегда, изменения настолько общие и разнообразные, что точные утверждения о производительности сделать трудно. Изменения в этом выпуске ещё более масштабны, чем обычно, включая новый сборщик мусора и перевод среды выполнения на Go. Некоторые программы могут работать быстрее, некоторые — медленнее. В среднем программы из тестовой комплектации Go 1 работают немного быстрее в Go 1.5, чем в Go 1.4, при этом, как упоминалось выше, паузы сборщика мусора значительно сократились, и почти всегда составляют менее 10 миллисекунд.

Сборки в Go 1.5 будут медленнее примерно в два раза. Автоматический перевод компилятора и линковщика из C в Go привёл к неидиоматичному коду, который работает медленнее, чем хорошо написанный Go-код. Инструменты анализа и рефакторинга помогли улучшить код, но остаётся ещё много работы. Дальнейшее профилирование и оптимизация продолжатся в Go 1.6 и последующих выпусках. Для получения дополнительной информации см. эти слайды и соответствующее видео.

Стандартная библиотека

Flag

Функция PrintDefaults пакета flag, а также метод FlagSet, были изменены для создания более приятных сообщений об использовании. Формат был изменён, чтобы быть более удобным для человека, и в сообщениях об использовании слово, заключённое в `обратные кавычки`, считается именем операнда флага, которое будет отображаться в сообщении об использовании. Например, флаг, созданный с вызовом,

<code>cpuFlag = flag.Int("cpu", 1, "run `N` processes in parallel")
</code>

будет отображать справку,

<code>-cpu N
run N processes in parallel (default 1)
</code>

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

Числа с плавающей точкой в math/big

Пакет math/big имеет новую, фундаментальную тип данных, Float, реализующую числа с плавающей точкой произвольной точности. Значение Float представлено знаком типа boolean, переменной длины мантиссой и 32-битным фиксированным знаковым экспонентой. Точность Float (размер мантиссы в битах) может быть указана явно или определяется первой операцией, создающей значение. После создания размер мантиссы Float может быть изменён с помощью метода SetPrec. Float поддерживают понятие бесконечностей, таких как те, что создаются при переполнении, но значения, которые приводят к эквиваленту NaN IEEE 754, вызывают панику. Операции с Float поддерживают все режимы округления IEEE-754. Когда точность установлена в 24 (53) бита, операции, остающиеся в диапазоне нормализованных значений float32 (float64), дают те же результаты, что и соответствующая арифметика IEEE-754 для этих значений.

Типы Go

Пакет go/types до сих пор поддерживался в репозитории golang.org/x; начиная с Go 1.5 он был перемещён в основной репозиторий. Код в старом расположении теперь устарел. Также произошёл небольшой изменение API в пакете, о котором говорится ниже.

В связи с этим, пакет go/constant также был перемещён в основной репозиторий; до этого он находился в golang.org/x/tools/exact. Пакет go/importer также был перемещён в основной репозиторий, а также некоторые инструменты, описанные выше.

Net

Разрешитель DNS в пакете net всегда использовал cgo для доступа к системному интерфейсу. Изменение в Go 1.5 означает, что на большинстве Unix-систем разрешение DNS больше не будет требовать cgo, что упрощает выполнение программ на этих платформах. Теперь, если конфигурация сетевых настроек системы позволяет, достаточно будет использовать нативный Go-разрешитель. Важным следствием этого изменения является то, что каждое разрешение DNS занимает горутину вместо потока, поэтому программа с несколькими одновременными DNS-запросами будет потреблять меньше ресурсов операционной системы.

Решение о том, как запускать разрешитель, применяется во время выполнения, а не во время сборки. Тег сборки netgo, который использовался для принудительного использования Go-разрешителя, больше не требуется, хотя он всё ещё работает. Новый тег сборки netcgo принудительно использует cgo-разрешитель во время сборки. Чтобы принудительно использовать cgo-разрешение во время выполнения, установите GODEBUG=netdns=cgo в переменной окружения. Дополнительные параметры отладки описаны здесь.

Это изменение применяется только к Unix-системам. Системы Windows, Mac OS X и Plan 9 ведут себя так же, как и раньше.

Reflect

Пакет reflect имеет две новые функции: ArrayOf и FuncOf. Эти функции, аналогичные существующей функции SliceOf, создают новые типы во время выполнения для описания массивов и функций.

Укрепление

Несколько десятков ошибок было найдено в стандартной библиотеке с помощью случайного тестирования с использованием инструмента go-fuzz. Исправления были внесены в пакеты archive/tar, archive/zip, compress/flate, encoding/gob, fmt, html/template, image/gif, image/jpeg, image/png и text/template. Исправления укрепляют реализацию против некорректных и злонамеренных входных данных.

npc
GoRu.dev Golang на русском

На сайте представлена адаптированная под русский язык документация языка программирования Golang