Go 1.5 版本说明

Go 1.5 简介

最新的 Go 版本 1.5 是一个重要的版本,包括对实现的重大架构更改。尽管如此,我们预计几乎所有 Go 程序都将像以前一样继续编译和运行,因为该版本仍然保持 Go 1 兼容性承诺

实现方面最大的改进是

下面将讨论这些以及对实现和工具的其他一些更改。

该版本还包含一个与映射字面量相关的小型语言更改。

最后,发布的时间偏离了通常的六个月间隔,既是为了提供更多时间来准备此主要版本,也是为了此后调整时间表,以便更方便地安排发布日期。

语言更改

映射字面量

由于疏忽,允许从切片字面量中省略元素类型的规则没有应用于映射键。这已在 Go 1.5 中 得到纠正。一个例子可以说明这一点。从 Go 1.5 开始,此映射字面量:

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

可以按如下方式编写,无需显式列出 Point 类型

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

实现

不再使用 C

编译器和运行时现在使用 Go 和汇编语言实现,不使用 C。树中仅剩的 C 源代码与测试或 cgo 相关。1.4 及更早版本中树中有一个 C 编译器。它用于构建运行时;自定义编译器在一定程度上是必要的,以保证 C 代码能够与 goroutine 的栈管理一起工作。由于运行时现在是用 Go 编写的,因此不需要此 C 编译器,并且它已消失。关于消除 C 的过程的详细信息在 其他地方讨论。

C 的转换是在为该工作创建的自定义工具的帮助下完成的。最重要的是,编译器实际上是通过将 C 代码自动转换为 Go 来移动的。它实际上是在不同的语言中相同的程序。它不是编译器的新实现,因此我们预计该过程不会引入新的编译器错误。有关此过程的概述,请参阅 此演示文稿的幻灯片。

编译器和工具

独立于但受迁移到 Go 的鼓励,工具的名称已更改。旧名称 6g8g 等已消失;取而代之的是只有一个二进制文件,可以访问为 go tool compile,它将 Go 源代码编译成适合由 $GOARCH$GOOS 指定的体系结构和操作系统的二进制文件。类似地,现在有一个链接器 (go tool link) 和一个汇编器 (go tool asm)。链接器是从旧的 C 实现自动转换的,但汇编器是下面将详细讨论的新本机 Go 实现。

6g8g 等名称的删除类似,编译器和汇编器的输出现在使用简单的 .o 后缀而不是 .8.6 等。

垃圾回收器

垃圾回收器已在 1.5 中重新设计,作为 设计文档中概述的开发的一部分。通过结合高级算法、更好的 回收器调度以及更多地与用户程序并行运行回收,预期延迟比之前版本中的回收器低得多。回收器的“停止世界”阶段几乎总是小于 10 毫秒,通常更短。

对于受益于低延迟的系统(例如响应用户请求的网站),新回收器带来的预期延迟下降可能很重要。

新回收器的详细信息在 GopherCon 2015 上的 演讲中进行了介绍。

运行时

在 Go 1.5 中,goroutine 调度的顺序已更改。调度程序的属性从未由语言定义,但依赖于调度顺序的程序可能会因此更改而中断。我们已经看到一些(错误的)程序受到此更改的影响。如果您有隐式依赖于调度顺序的程序,则需要更新它们。

另一个可能导致中断的更改是运行时现在将同时运行的线程默认数量(由 GOMAXPROCS 定义)设置为 CPU 上可用的内核数。在之前的版本中,默认为 1。不希望使用多个内核运行的程序可能会意外中断。可以通过删除限制或显式设置 GOMAXPROCS 来更新它们。有关此更改的更详细讨论,请参阅 设计文档

构建

现在 Go 编译器和运行时是用 Go 实现的,因此必须提供 Go 编译器才能从源代码编译发行版。因此,要构建 Go 核心,必须已存在一个可用的 Go 发行版。(不从事核心工作的 Go 程序员不受此更改的影响。)任何 Go 1.4 或更高版本的发行版(包括 gccgo)都可以使用。有关详细信息,请参阅 设计文档

端口

主要是由于行业转向 32 位 x86 架构,1.5 中提供的二进制下载集减少了。仅为 amd64 体系结构(而不是 386)提供 OS X 操作系统发行版。类似地,Snow Leopard (Apple OS X 10.6) 的端口仍然有效,但不再作为下载发布或维护,因为 Apple 不再维护该版本的 OS X。此外,dragonfly/386 端口完全不受支持,因为 DragonflyBSD 本身不再支持 32 位 386 架构。

但是,有几个新的端口可供从源代码构建。这些包括 darwin/armdarwin/arm64。新的端口 linux/arm64 大致到位,但 cgo 仅支持使用外部链接。

作为实验,还可以使用 ppc64ppc64le(64 位 PowerPC,大端和小端)。这两个端口都支持 cgo,但仅支持内部链接。

在 FreeBSD 上,Go 1.5 需要 FreeBSD 8-STABLE+,因为它新使用了 SYSCALL 指令。

在 NaCl 上,Go 1.5 需要 SDK 版本 pepper-41。由于从 NaCl 运行时中删除了 sRPC 子系统,因此更新的 pepper 版本不兼容。

在 Darwin 上,可以使用 ios 构建标签禁用系统 X.509 证书接口的使用。

Solaris 端口现在完全支持 cgo 和 netcrypto/x509 软件包,以及许多其他修复和改进。

工具

转换

作为从树中消除 C 的过程的一部分,编译器和链接器已从 C 转换为 Go。这是一次真正的(机器辅助)转换,因此新程序本质上是转换后的旧程序,而不是带有新错误的新程序。我们相信转换过程引入的错误很少,如果有的话,并且实际上发现了一些以前未知的错误,现在已修复。

但是,汇编器是一个新程序;它在下面描述。

重命名

编译器 (6g8g 等)、汇编器 (6a8a 等) 和链接器 (6l8l 等) 的程序套件已合并为一个工具,该工具由环境变量 GOOSGOARCH 配置。旧名称已消失;新工具可通过 go tool 机制访问,例如 go tool compilego tool asmgo tool link。此外,中间对象文件的 .6.8 等文件后缀也消失了;现在它们只是普通的 .o 文件。

例如,要使用工具直接为 Darwin 构建和链接 amd64 上的程序(而不是通过 go build),需要运行

$ export GOOS=darwin GOARCH=amd64
$ go tool compile program.go
$ go tool link program.o

移动

因为 go/types 包现在已移至主存储库(请参见下文),所以 vetcover 工具也已移动。它们不再在外部 golang.org/x/tools 存储库中维护,尽管(已弃用)源代码仍然存在以与旧版本兼容。

编译器

如上所述,Go 1.5 中的编译器是一个单一的 Go 程序,它从旧的 C 源代码转换而来,用于替换 6g8g 等。其目标由环境变量 GOOSGOARCH 配置。

1.5 编译器与旧编译器基本相同,但一些内部细节已更改。一项重大更改是常量的计算现在使用 math/big 包,而不是自定义(且测试不足)的高精度算术实现。我们预计这不会影响结果。

仅对于 amd64 体系结构,编译器有一个新选项 -dynlink,它通过支持对外部共享库中定义的 Go 符号的引用来协助动态链接。

汇编器

与编译器和链接器类似,Go 1.5 中的汇编器是一个单一程序,用于替换汇编器套件 (6a8a 等),并且环境变量 GOARCHGOOS 配置体系结构和操作系统。与其他程序不同,汇编器是用 Go 编写的全新程序。

新汇编器与之前的汇编器非常兼容,但有一些更改可能会影响一些汇编器源文件。请参阅更新的 汇编器指南,以获取有关这些更改的更多具体信息。总之

首先,用于常量的表达式求值略有不同。它现在使用无符号 64 位算术,并且运算符(+-<< 等)的优先级来自 Go,而不是 C。我们预计这些更改会影响极少的程序,但可能需要手动验证。

也许更重要的是,在SPPC 仅作为编号寄存器的别名(例如,ARM 上堆栈指针为 R13,硬件程序计数器为 R15)的机器上,对不包含符号的此类寄存器的引用现在是非法的。例如,SP4(SP)是非法的,但 sym+4(SP) 是可以的。在这样的机器上,要引用硬件寄存器,请使用其真实的 R 名称。

一个小的变化是,一些旧的汇编程序允许使用以下表示法

constant=value

来定义一个命名常量。由于这始终可以通过传统的类 C 的 #define 表示法来实现,并且该表示法仍然受支持(汇编器包含一个简化的 C 预处理器实现),因此该功能已被移除。

Go 1.5 中的链接器现在是一个 Go 程序,它替换了 6l8l 等。其操作系统和指令集由环境变量 GOOSGOARCH 指定。

还有一些其他的更改。最重要的是添加了一个 -buildmode 选项,该选项扩展了链接的风格;它现在支持构建共享库以及允许其他语言调用 Go 库等情况。其中一些在设计文档中进行了概述。有关可用构建模式及其用法的列表,请运行

$ go help buildmode

另一个细微的更改是,链接器不再在 Windows 可执行文件的头部记录构建时间戳。此外,尽管这可能会得到修复,但 Windows cgo 可执行文件缺少一些 DWARF 信息。

最后,-X 标志接受两个参数,例如

-X importpath.name value

现在也接受更常见的 Go 标志样式,该样式使用单个参数,该参数本身是一个 name=value

-X importpath.name=value

尽管旧语法仍然有效,但建议将脚本等中此标志的使用更新为新形式。

Go 命令

go 命令的基本操作保持不变,但有一些值得注意的更改。

之前的版本引入了包内部的目录无法通过 go 命令导入的概念。在 1.4 中,它通过在核心存储库中引入一些内部元素进行了测试。正如设计文档中所建议的那样,此更改现在可用于所有存储库。规则在设计文档中进行了说明,但总而言之,名为 internal 的目录中或其下的任何包都可以被同一个子树中根目录下的包导入。现有的目录元素名为 internal 的包可能会受到此更改的意外破坏,这就是在上一版本中对其进行宣传的原因。

处理包的另一种更改是实验性地增加了对“供应商”的支持。有关详细信息,请参阅go 命令设计文档的文档。

还有一些小的更改。阅读文档以获取完整详细信息。

Go vet 命令

go tool vet 命令现在对结构体标签进行更彻底的验证。

跟踪命令

提供了一个用于 Go 程序动态执行跟踪的新工具。用法类似于测试覆盖率工具的工作方式。跟踪的生成集成到 go test 中,然后跟踪工具本身的单独执行分析结果

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

这些标志使输出能够在浏览器窗口中显示。有关详细信息,请运行 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 代码相比,其性能较差。分析工具和重构有助于改进代码,但还有很多工作要做。进一步的分析和优化将在 Go 1.6 和未来的版本中继续进行。有关更多详细信息,请参阅这些幻灯片和相关的视频

标准库

标志

标志包的PrintDefaults 函数(以及FlagSet 上的方法)已修改为创建更友好的使用消息。格式已更改为更人性化,并且在使用消息中,用反引号引起来的单词被视为标志的操作数的名称,以便在使用消息中显示。例如,使用以下调用创建的标志,

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

将显示帮助消息,

-cpu N
        run N processes in parallel (default 1)

此外,仅当默认值不是类型的零值时才会列出它。

math/big 中的浮点数

math/big 包有一个新的基本数据类型Float,它实现了任意精度的浮点数。Float 值由布尔类型的符号、可变长度的尾数和 32 位固定大小的有符号指数表示。Float 的精度(尾数大小以位为单位)可以显式指定,或者由创建该值的第一个操作确定。创建后,可以使用SetPrec 方法修改 Float 尾数的大小。Float 支持无穷大的概念,例如溢出创建的无穷大,但会导致等效于 IEEE 754 NaN 的值会导致 panic。Float 操作支持所有 IEEE-754 舍入模式。当精度设置为 24(53)位时,保留在归一化 float32float64)值的范围内的操作产生的结果与这些值上相应的 IEEE-754 算术运算产生的结果相同。

Go 类型

到目前为止,go/types 包一直维护在 golang.org/x 存储库中;从 Go 1.5 开始,它已被重新定位到主存储库。旧位置的代码现在已弃用。包中还有一个适度的 API 更改,如下所述。

与此移动相关,go/constant 包也移动到了主存储库;它以前是 golang.org/x/tools/exactgo/importer 包也移动到了主存储库,以及上面描述的一些工具。

网络

net 包中的 DNS 解析器几乎一直都使用cgo来访问系统接口。Go 1.5 中的一个变化意味着,在大多数 Unix 系统上,DNS 解析将不再需要cgo,这简化了在这些平台上的执行。现在,如果系统的网络配置允许,则可以使用本机 Go 解析器。此更改的重要影响是,每个 DNS 解析都占用一个 goroutine 而不是一个线程,因此具有多个未完成 DNS 请求的程序将消耗更少的操作系统资源。

如何运行解析器的决定是在运行时进行的,而不是在构建时进行的。netgo构建标签以前用于强制使用 Go 解析器,现在不再需要,尽管它仍然有效。新的netcgo构建标签强制在构建时使用cgo解析器。要在运行时强制使用cgo解析,请在环境中设置GODEBUG=netdns=cgo。更多调试选项在这里有说明。

此更改仅适用于 Unix 系统。Windows、Mac OS X 和 Plan 9 系统的行为与以前相同。

反射

reflect包有两个新函数:ArrayOfFuncOf。这些函数类似于现有的SliceOf函数,在运行时创建新的类型来描述数组和函数。

强化

使用go-fuzz工具进行随机测试时,在标准库中发现了数十个错误。已在archive/tararchive/zipcompress/flateencoding/gobfmthtml/templateimage/gifimage/jpegimage/pngtext/template包中修复了这些错误。这些修复增强了实现对错误和恶意输入的抵御能力。

库的次要更改