Go 1.5 发布说明

Go 1.5 介绍

Go的最新版本是1.5,同时是一个有重大意义的版本,其包括了Go主要架构的变更.尽管如此,我们预计几乎所有的Go程序还是可以像以前一样地编译和运行,因为这次发布仍然保持Go 1时的兼容性承诺.

主要改进是:

  • 编译器和运行时完全用Go重写(带一点汇编). C不再参与Go的实现,因此曾是构建分布式程序不可或缺的C编译器,如今已被舍弃.
  • gc支持并发,显著地降低了运行时的暂停时间,有时甚至可以和其他goroutines同时运行.
  • Go程序默认使用GOMAXPROCS来设置可用的cpu核数,此前默认是1.
  • internal packages支持所有仓库,而不仅仅是Go的核心仓库.
  • go命令实验性地支持处理(获取)外部依赖.
  • 新的go tool trace命令,支持细颗粒度地追踪程序的执行过程.
  • 新的go doc命令(与godoc不同),只能在命令行中使用.

这些以及其他实现和工具相关的变更都会在下文提到.

该版本也包含了一个关于map字面量的语法变化.

最后,此次发布时机偏离了往常的每六个月发布一次的惯例,这样既有更多的时间准备这个主要版本的发布,此后也可更方便地调整时间表来安排发布时间.

语法变化

map字变量

由于疏忽,语法允许slice字面量省略元素类型的规则并不适用于map的key.这个问题已在Go1.5中修正,下面的例子清楚地说明了这点:Go1.5中的map字面量,

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.C编译器只出现在1.4及更早的版本中,用于构建运行时,这个定制编译器也是确保C代码可以和goroutines的stack管理协同工作的必要组成部分.自从运行时改用Go后,就没必要使用C编译器了.关于移除C的详细过程可在这里查看.

移除C的工作是在为这项任务而定制的工具的帮助下完成的.最重要的是,编译器实际上是由C自动翻译成Go的,是由不同语言实现的同一个程序.它不是一个全新的编译器实现,因此我们希望程序没有引入新的编译器bug.此过程的总览可在这个演示文稿中的幻灯片里找到.

编译器和工具

原本独立的(编译器)工具被用Go重写,同时这些工具的名词也发生了变化.旧名称叫6g,8g等等,其已被弃用,取而代之的是仅有的一个二进制的,且更容易使用的编译器.它会根据$GOARCH和$GOOS将Go源码编译成适合体系结构和指定OS的二进制程序.同样的,有一个新的连接器(go tool link)和一个新的汇编器(go tool asm).连接器是由原先的C实现自动翻译而来,但汇编器是Go的原生实现,下面有更多的细节.

同样的,因为舍弃了6g,8g等名词,编译器和汇编器输出将采用现在的.o后缀而不是.8,.6等.

gc

作为设计文档中开发大纲的一部分,gc在1.5中被重新设计.通过组合多种先进算法,更好地调度,以及在更大程度上和用户程序并行运行,因此预计收集器延迟将比之前的版本更低.现在gc导致的暂停时间大多可保持在10ms以下,甚至更低.

例如对于用户访问网站时的响应速度,降低收集器的延迟是很重要的,这类系统将受益于低延迟.

新收集器的细节可在TODO: GopherCon talk中找到.

运行时

在Go1.5,goroutines的调度顺序发生了改变.调度器的属性不是由语言决定,但在这次改变中,依赖调度顺序的程序可能出现无法使用.我们也看到了因这个改变而导致的一些错误.如果你的程序隐式地依赖调度顺序,那么你需要更新这些程序.

另一个潜在的破坏性变化是运行时会根据GOMAXPROCS(可用的CPU核数)设置默认的可同时运行的线程数,此前默认是1.不期望运行在多核上的程序可能会在无意中被干扰,它们可通过移除这个限制或显式地设置GOMAXPROCS进行解决.

构建

目前Go的编译器和运行时使用Go实现,这个编译器必须支持用源码编译发行版.因此,为了构建Go的核心,必须有一个可用的发行版.(非Go核心的程序将不受这个变化的影响.).任何Go1.4或以后的发行版(包括gccgo)将承当这个角色.相关细节,可参考设计文档.

移植

由于大部分厂商已经放弃32位架构,比如OS X发行版只支持amd64架构,因此1.5中提供的二进制下载文件有所减少.同样的,自Apple不再维护这些版本起,虽然Snow Leopard(Apple OS X 10.6)的移植还在继续,但不会再提供下载和维护.同时,因为dragonfly本身不再支持32位架构,因此不会进行dragonfly/386的移植.

不管怎样,若干新的移植工作已经完成,允许其从源码构建Go,其中包括 darwin/arm和darwin/arm64.新的linux/arm64移植工作大部分已经完成,只是cgo仅支持外部连接.

在FreeBSD,因为使用了新的系统调用,Go1.5需要FreeBSD 8-STABLE以上.

在NaCl,因为使用了系统调用get_random_bytes,Go1.5需要pepper-39及其以上版本的SDK.

工具

翻译

作为从代码树中移除C的一部分,编译器和连接器是由C翻译成Go的.这是一个真正的(机器辅助)翻译过程,因此新程序本质上是旧程序的翻译而不是带有新bug的全新实现.我们深信翻译过程几乎没引入新的bug,实际上,甚至还发现了一些以前的未知bug,但现在已修复.

然而,汇编器是全新的实现,下面是它的描述.

重命名

编译器(6g, 8g等),汇编器(6a, 8a等),连接器(6l, 8l等),这些配套程序已经合并成一个单一的工具,其可通过环境变量GOOS和GOARCH配置.这些旧名称已经废弃,新的工具链是go tool compile,go tool asm,and go 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 package已被移入主仓库(参见下文),因此工具vetcover也被移入.其外部仓库(golang.org/x/tools)将不再维护,虽然已废弃,但其代码还保留在那里也和老发行版兼容.

编译器

如上所述,Go1.5的编译器是一个单独的Go程序,由原先的C代码翻译而来,取代了6g, 8g等.它的输出可通过环境变量GOOS和GOARCH配置.

新编译器等同旧编译器,但有些内部细节发生了变化.一个显著的变化就是使用math/big package进行常量赋值,而不是一个自定义的高精度计算的实现(没有很好的测试).我们不希望这个会影响到输出结果.

对于amd64架构,编译器有一个新选项,-dynlink,其通过支持引用在外部共享库中定义的Go符号来实现动态连接.

汇编器

像编译器和连接器一样,Go1.5中的汇编器也是一个单独的程序,其取代了以前的汇编器套件(6a, 8a等),可通过环境变量GOOS和GOARCH进行配置.但与它们不同的是,汇编器是完全地用Go重写的.

新汇编器和以前的兼容,但也有一些变化,这可能会影响到汇编器的一些源文件.可查看汇编器的更新指南来了解具体的信息,总之:

首先,用于常量赋值的表达式有些不同.它目前使用64位无符号来计算,还有运算符(+, -, <<等)优先级采用Go的,而不是C. 我们希望这些变化只会影响到少数程序,但这可能需要手动验证了.

也许更重要的是,一些PC(程序计数器)和SP(堆栈指针)在结构体系上的差异已被消除.在之前,这些寄存器有时是硬件寄存器,而有时是伪寄存器.在Go1.5中,PC和SP都是伪寄存器.为了使用硬件寄存器,需要使用备选表示,例如在x86上,可使用R13作堆栈指针,R15作程序计数器.(这些名称在其他体系结构上可能不同).为了适应这种变化,现在使用SP和PC伪寄存器需要一个标识符f+4(SP),而不是原先的4(SP),忽略这个标识符将会得到一个语法错误.使用SP作为硬件寄存器往往可简化名称,还有它们现在将由汇编器来标记.

还有一个次要的变化,一些老的汇编器也可标记这些.

constant=value

总是利用传统的类C的#define标记来定义一个常量的功能还会提供(汇编器包含一个简化的C预处理实现),这项功能已被移除??.

连接器

Go1.5的连接器是一个Go程序,取代6l, 8l等.其可通过环境变量GOOS和GOARCH进行配置.

还有几个变化,更显著的是增加了一个-buildmode选项来扩展连接方式,现在可支持构建共享库以及允许其他语言调用Go的库.这些在设计文档有论述.查看支持构建模式的选项,可运行:

$ go help buildmode

其他的小变化有,连接器不再保存Windows可执行文件头部的时间戳.此外,虽然这可能是固定的??,Windows cgo可执行程序会缺失部分DWARF信息.

Go命令

go命令的基本操作不变,但有些还是值得一提.

先前发行版引入使用一个名为internal目录作为package来实现导入的主意.在1.4,Go核心仓库引入了一些internal组件用于测试.正如设计文档建议的那样,这个改变现在支持所有的仓库.该规则在设计文件中有解释,总结起来就是internal目录下的所有package可以被其临近(同级)的包导入.现有的目录名为internal的package可能会无意中打破这种变化,这也是为什么这个功能在最近的发行版才被宣传的原因.

另一个变化是处理package时增加了实验性的“vendoring”支持.TODO:go命令本身没有显示这个(命令).TODO:初始的设计文档https://golang.org/s/go15vendor需要更新.

这里还有几个小的变动,可阅读文档获取详细信息.

  • SWIG支持已更新,如此一来,.swig和.swigcxx需要SWIG 3.0.6及以上版本.
  • std(标准库)通配符包名称现在排除了commands,使用新的cmd取代了commands??.
  • 一个新的-toolexec标记,build时允许替换成另一个不同的命令来调用编译器等等.这可作为一个自定义命令去替换go tool.
  • build子命令有一个-buildmode选项,和连接器捆绑使用,前文已有描述.
  • 一个名为-asmflags的build选项,给汇编器提供参数.不过-ccflags被废弃了.TODO:为什么?
  • go test支持-count参数 (https://golang.org/cl/10669)
  • generate子命令有一些新功能.-run选项指定一个正则表达式来选择执行哪些指令;这个功能在1.4时提出,但没有实现.执行模式可获得两个环境变量$GOLINE(指令在源码中的行数)和$DOLLAR(扩展$符).

vet命令

go tool vet命令现在可用于更彻底地校验struct的标签.

trace命令

TODO
cmd/trace: 查看追踪的新命令 (https://golang.org/cl/3601)

go doc命令

前几个版本,go doc命令被删除,因为它不是必须的,可用godoc命令替代.在1.5中引入了一个新的go doc命令提供了比godoc更便利的命令行界面.它被设计成专为命令行使用,根据调用提供了更紧凑和集中的关于一个package和其中元素的说明.它还提供了不区分大小写的匹配和支持显示文档中的意外符号.运行"go help doc"可获得详细信息.

Cgo

当解析到#cgo行,${SRCDIR}会扩展成源代码目录的路径.这将允许把相对于源码目录的相关文件路径作为选项传递给编译器和连接器.当前工作目录变化时,没有扩展过的路径是无效的.

windows上,cgo默认使用外部连接.

性能

和往常一样,变更是如此普遍和多样,因此很难做出关于性能的精确描述.在此版中,变化甚至比往常更加广泛,包含了用Go实现的一个新的垃圾收集器和一个新的运行时.某些程序可能更快,有些更慢.平均而言,用Go1的基准测试套件测试时,Go1.5比1.4快几个百分点.如上文提到的gc暂停时间大大缩短,几乎都可保持在10ms以下.

在Go1.5中,build比以前慢了2倍.编译器和连接器是由C自动翻译成Go的,其生成的Go代码并不地道,以至于和写得优雅的Go代码比表现不佳.分析工具和重构有助于改善这些代码,但这需要做很多工作.进一步的分析优化将放在Go1.6和以后版本中进行.欲了解更多的信息,请看这些幻灯片和相关视频.

核心库

flag

flag package中的PrintDefaults函数和FlagSet的方法,已经可以输出更优雅的使用信息了.输出格式也更加的人性化,还有在使用信息中如果一个词使用反引号扩起来可将其视为该flag的操作对象的名称.例如,调用这样一个flag,

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

将显示这样的帮助信息,

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

此外,当该值的默认值不是其零值时,也会列出它的默认值.

math/big中的Float

math/big package有一个新的,基础的数据类型,叫Float,其可表示任意精度的浮点数.Float用一个bool标记,一个可变长度的尾数,一个固定的32位有符号指数来表示.Float的精度(尾数大小)可以显式指定,否则由第一次操作时创建的值决定.创建后,Float尾数的大小可用其SetPrec方法修改.Float支持无穷大和溢出,但其值等于IEEE 754 NaNs时会触发panic.Float运算支持IEEE-754的四舍五入.当精度设置成24(53)位时,在float32(float64)精度范围内操作而生成的结果和IEEE-754算法生成的值相同.

Go types

go/types package之前都是在golang.org/x仓库中.Go1.5中,其已被迁入主库.旧地方的代码已被废弃.同样的,package中的API也发生了轻微的变化,见下面.

随着这次移动,go/constant package也被迁入主库,其之前是在golang.org/x/tools/go/exact里.就像上文中论述的其他工具一样,go/importer也被迁入主库.

Net

net package中的DNS解析器几乎总是通过cgo访问相关接口.Go1.5中的这个变更意味着大部分Unix系统的DNS已经不用依赖cgo,简化了在这些平台上的执行过程.现在如果该系统的网络配置允许的话,Go原生的解析器就足够了.这种变化的重要作用是每个DNS解析只占用一个goroutine而不是一个线程,一个程序中的多个未完成的DNS请求将占用系统更少的资源.

如何运行解析器是由程序运行时,而非构建时决定的.netgo构建标记用于强制程序使用Go的解析器,其尽管依旧有效,但已不是必须的了.

该变更只适用于Unix.Windows, Mac OS X, 和Plan 9的系统行为还是和以前一样.

反射

reflect package新增两个函数:ArrayOfFuncOf.它们的功能类似于现有的SliceOf函数,用于在运行时创建描述数组和函数的新类型.

强化

通过工具go-fuzz的随机测试,在标准库中发现了几十个错误.这些bug已被修复,其分别出现在如下package中:archive/tar, archive/zip, compress/flate, encoding/gob, fmt, html/template, image/gif, image/jpeg, image/png, and text/template.这次修复强化了拒绝不正确和恶意的输入.

其他库的变化

请查看原文,这里略.

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。