标签 Rust 下的文章

源创会开源访谈:十年成长,Go语言的演化之路

在参加源创会沈阳站分享之前,接受了开源中国社区编辑王练的文字专访,以下是我针对专访稿的内容。

同时该专访稿首发于开源中国开源访谈栏目,大家可以点击这里看到首发原稿。

1、首先请介绍一下自己

大家好!我叫白明(Tony Bai),目前是东软云科技的一名架构师,专职于服务端开发,日常工作主要使用Go语言。我算是国内较早接触Go语言的程序员兼Advocater了,平时在我的博客微博和微信公众号”iamtonybai”上经常发表一些关于Go语言的文章和Go生态圈内的信息。

在接触Go之前,我主要使用C语言开发电信领域的一些后端服务系统,拥有多年的电信领域产品研发和技术管理经验。我个人比较喜换钻研和分享技术,是《七周七语言》一书的译者之一,并且坚持写技术博客十余年。同时我也算是一个开源爱好者,也在github上分享过自己开发的几个小工具。

目前的主要研究和关注的领域包括:Go、KubernetesDocker区块链和儿童编程教育等。

img{512x368}

2、最初是因为什么接触和使用 Go 语言的?它哪方面的特性吸引了您?

个人赶脚:选编程语言和谈恋爱有些像(虽然我只谈过一次^_^),我个人倾向一见钟情。我个人用的最多的编程语言是GoC,这两门语言算是我在不同时期的“一见钟情”的对象吧,也是最终“领(使)证(用)”的,前提:编程世界是“一夫多妻制”^0^。

当然早期也深入过C++,后来JavaRubyCommon LispHaskellPython均有涉猎,这些语言算是恋爱对象,但最终都分手了。

最初接触到Go应该是2011年,那是因为看了Rob Pike的3 Day Go Course,那时Go 1.0版本还没有发布,如果没记错,Rob Pike slide中用的还是Go r60版本的语法。现在大脑中留存的当时的第一感觉就是“一见钟情”!

现在回想起来,大致有这么几点原因:

  • Go与C一脉相承,对于出身C程序员的我来说,这一语言传承非常自然,多体现在语法上;
  • Go语言非常简单,尤其是GC、并发goroutine、interface,让我眼前一亮;
  • Rob Pike的Go Course Slide组织的非常好,看完三篇Slide,基本就入门了。

于是在那之后,又系统阅读了Ivo Balbaert的《The Way To Go》、《Programming in Go – Creating Applications for the 21st Century》等基本新鲜出炉的书,于是就走入了Go语言世界。

不过当时Go1尚未发布,Go自身也有较大变化,工作中也无法引入这门语言,2013年对Go的关注有些中断,2014年又恢复,直至今天。现在感觉到:如果工作语言与兴趣语言能保持一致是多么幸福的一件事啊。

3、有人说 Go 是互联网时代的 C 语言,对于这两门语言,您是怎么看的?

如果没记错,至少在国内,第一个提出这种观点的是现七牛的ceo许式伟了,老许是国内第一的Go 鼓吹者,名副其实;而且许式伟的鼓吹不仅停留在嘴上,更是付诸于实践:据说其七牛云的基础设施基本都是Go开发的。因此,对他的“远见卓识”还是钦佩之至的。

C语言缔造的软件行业的成就是举世瞩目,也是公认的。其作者Dennis Ritchie授予图灵奖就是对C语言最大的肯定和褒奖。C语言缔造了单机操作系统和基础软件的时代:UnixLinux、nginx/apache以及无数以*inx世界为中心的工具,是云时代之前最伟大的系统编程语言和基础设施语言。

至于 “Go是互联网时代的 C 语言”这一观点,如果在几年前很多人还会疑惑甚至不懈,但现在来看:事实胜于雄辩。我们来看看当前CNCF基金会(Cloud Native Computing Foundation)管理的项目中,有一大半都是Go语言开发的,包括KubernetesPrometheus等炙手可热的项目;这还不包括近两年最火的docker项目。事实证明:Go已成为互联网时代、云时代基础设施领域、云服务领域的最具竞争力的编程语言之一。

不过和C不同的是,Go语言还在发展,还在演进,还有巨大的提升空间,Gopher群体还在变大,去年再次成为Tiboe的年度语言就是例证。

当然我们还得辩证的看,Go语言虽然在云时代基础设施领域逐渐继承C语言的衣钵,但是由于语言设计理念和设计哲学上的原因,在操作系统以及嵌入式领域,Go还在努力提升。

4、Go 也经常被拿来和 Java、Rust 等语言比较,您认为它最适合的使用场景有哪些?

早期对Java有所涉猎,但止步于Java体量过重和框架过多;Rust和Go一样是近几年才兴起的一门很有理想、很有抱负的编程语言,其目标就是安全的系统级编程语言,运行性能极佳,用以替代C/C++的,但就像前面所提到的那样,第一眼看到Rust的语法,就没有那种“一见钟情”的赶脚,希望Rust不要像C++那样,演变的那么复杂。

Go从其第一封设计email出炉到如今已有十年了,我觉得也不应该由我来告诉大家Go更适合应用在什么领域了,事实摆在那里:“大家都用的地方,总是对的”。这里我只是大致归纳一下:

Go在数据科学、人工智能领域也有较大进展,希望在将来能看到Go在这些领域有杀手级项目出现。

5、Go发展已有10 年,其特性随着版本的迭代不断在更新,您觉得它最好的和最需要改进的特性分别有哪些?

每种语言都有自己的设计哲学和设计者的考量。我在GopherChina 2017的topic中就提到过Go语言的价值观,其中之一就是Simplicity,即简单。相信简单也是让很多开发者走进Gopher世界的重要原因。从今年GopherCon 2017大会上Russ Cox的“Toward Go 2”的主题演讲中,我们也可以看出:Go team并不会单纯地为了迎合community的意愿去堆砌feature,那go势必走上c++的老路,变得日益复杂,Go受欢迎的基础之一就不存在了。

但演进就一定会要付出代价的,尤其是Go1的约束在前。从我个人对Go的应用来看,最想看到的是包管理和error处理方面的体验提升。但我觉得这两点都是可以通过渐进改进实现的,甚至不会影响到Go1兼容性,不会像引入generics机制,实现难度也不会太高。

对于目前的error handling机制,我个人并没有太多的排斥,这可能是因为我出身C程序员的缘故吧。在error handling这块,只是希望能让gopher拥有更好的体验即可,比如说围绕现有的error机制,增加一些设施以帮助gopher更好的获取error cause信息,就像github.com/pkg/errors包那样。

对于社区呼声很高的generics(泛型),我个人倒是没有什么急切需求。generics虽然可以让大幅提升语言的表现力(expressiveness),但也给语言自身带来了较大的复杂性。就个人感受而言,C++就是在加入generics后才变得无比庞大和复杂的,同时generics还让很多C++ programmer沉溺于很多magic trick中无法自拔,这对于以“合作分工”为主流的软件开发过程来说,并不是好事情。

6、Go 官方团队已发布 2.0 计划,更侧重于兼容性和规模化方面。对此,您怎么理解?Go 否已达到最佳性能?

这个问题和上面的问题有些类似,我的想法差不多。Go team在特性演进方面会十分谨慎,这也是go Team一贯的风格。从Go1到Go2,从现在看来,这个时间跨度不会很短,也许是2-3年也不一定,心急吃不了热豆腐^0^,社区分裂可不是go team想看到的事情,python可是前车之鉴。

另外,Go性能显然还是有改善空间的,尤其是编译性能、GC吞吐和延迟的tradeoff方面;另外goroutine调度器算法方面可能还有改进空间。当前Goroutine调度算法的实现者Dmitry Vyukov之前就编写了一个scheduler优化的proposal: NUMA-aware scheduler for Go(针对numa体系的优化),但也许因为重要性、优先级等考量,一直没有实现,也许后续会实现。

7、Go 在国内似乎比国外还要火,您认为造成这种现象的原因是什么?

从一些搜索引擎的trend数据来看,Go在中国地区的确十分火热,甚至在热度值上是领先于欧美世界的。个人觉得造成这种现象的原因可能有如下几点:

  • 语言本身的接受度高

首先,从Go语言本身考虑。事实证明了:Go语言的设计匹配了国内程序员的行业业务需求和对语言特性的需求(口味):
a) 语言:简单、正交组合和并发;开发效率和运行效率双高;
b) 自带battery:丰富的标准库和高质量第三方库;
c) 迎合架构趋势:天生适合微服务….

  • 引入早且与Go advocator的努力分不开

当前再也不是那个“酒香不怕巷子深”的年代了,再好的编程语言也需要推广和宣称。Go team在社区建设、全世界推广方面也是不遗余力。至于国内更是有像许式伟、Astaxie这样的占据高端IT圈子的advocator在站台宣传。

  • 互联网飞速发展推动Go在国内落地

中国已经是事实的移动互联网时代的领军者,大量创业公司如雨后春笋般诞生。而Go对于startup企业来说是极其适合的。开发效率高,满足了Startup企业对产品或服务快速发布的需求;运行效率高可以让startup公司节省初期在硬件方面的投入:一台主机顶住100w并发。

对于那些巨头、大公司而言,Go又是云计算时代基础设施的代表性语言,自然也会投入到Go怀抱,比如:阿里CDN、百度门户入口、滴滴、360等。

8、对于刚开始学习 Go ,并期待将其应用在项目中的新人们,您有哪些建议?

学语言,无非实践结合理论。

  • 理论:书籍和资料

这里转一下我在知乎上一个回答

强烈推荐:Rob Pike 3-day Go Course,虽然语法过时了,但看大师的slide,收获还是蛮多的。

Go基础: Go圣经《The Go Programming Language》和《Go in Action》。
原理学习: 雨痕的《Go学习笔记》。
Go Web编程: 直接看astaxie在github上的《Go web编程》。

还有一本内容有些旧的,但个人觉得值得一看的书就是《The Way To Go》,大而全。Github上有部分章节的中译版

另外,建议看一遍官方的Language specificationeffective gogo faq,对学go、理解go设计的来龙去脉大有裨益。

  • 实践:多读多写Code

多读代码:首选标准库,因为Go的惯用法和最佳实践在标准库中都有体现。

写代码:这个如果有项目直接实践那是非常的幸福;否则可以从改写一个自己熟悉领域的工具开始。比如:以前我刚接触Go的时候,没啥可写的。就改写一套cmpp协议实现。后来做wechat接口,实现了一个简单的wechat基本协议,当然这两个代码也过于陈旧了,代码设计以及其中的go语言用法不值得大家学习了^0^。


微博:@tonybai_cn
微信公众号:iamtonybai
github.com: https://github.com/bigwhite

Go 1.7中值得关注的几个变化

零、从Release Cycle说起

从Go 1.3版本开始,Golang核心开发Team的版本开发周期逐渐稳定下来。经过Go 1.4Go1.5Go 1.6的实践,大神Russ CoxGo wiki上大致定义了Go Release Cycle的一般流程:

  1. 半年一个major release版本。
  2. 发布流程启动时间:每年8月1日和次年2月1日(真正发布日期有可能是这个日子,也可能延后几天)。
  3. 半年的周期中,前三个月是Active Development,then 功能冻结(大约在11月1日和次年的5月1日)。接下来的三个月为test和polish。
  4. 下一个版本的启动计划时间:7月15日和1月15日,版本计划期持续15天,包括讨论这个major版本中要实现的主要功能、要fix的前期遗留的bug。
  5. release前的几个阶段版本:beta版本若干(一般是2-3个)、release candidate版本若干(一般是1-2个)和最后的release版本。
  6. major release版本的维护是通过一系列的minor版本体现的,主要是修正一些导致crash的严重问题或是安全问题,比如major release版本Go 1.6目前就有go 1.6.1和go 1.6.2两个后续minor版本发布。

在制定下一个版本启动计划时,一般会由Russ Cox在golang-dev group发起相关讨论,其他Core developer在讨论帖中谈一下自己在下一个版本中要做的事情,让所有开发者大致了解一下下个版本可能包含的功能和修复的bug概况。但这些东西是否能最终包含在下一个Release版本中,还要看Development阶段feature代码是否能完成、通过review并加入到main trunk中;如果来不及加入,这个功能可能就会放入下一个major release中,比如SSA就错过了Go 1.6(由于Go 1.5改动较大,留给Go 1.6的时间短了些)而放在了Go 1.7中了。

个人感觉Go社区采用的是一种“民主集中制”的文化,即来自Google的Golang core team的少数人具有实际话语权,尤其是几个最早加入Go team的大神,比如Rob Pike老头、Russ Cox以及Ian Lance Taylor等。当然绝大部分合理建议还是被merge到了Go代码中的,但一些与Go哲学有背离的想法,比如加入泛型、增加新类型、改善错误处理等,基本都被Rob Pike老头严词拒绝了,至少Go 1兼容版本中,大家是铁定看不到的了。至于Go 2,就连Go core team的人也不能不能打包票说一定会有这样的新语言规范。不过从Rob Pike前些阶段的一些言论中,大致可以揣摩出Pike老头正在反思Go 1的设计,也许他正在做Go 2的语言规范也说不定呢^_^。这种“文化”并不能被很多开源开发者所欣赏,在GopherChina 2016大会上,大家就对这种“有些独裁”的文化做过深刻了辩论,尤其是对比Rust那种“绝对民主”的文化。见仁见智的问题,这里就不深入了。个人觉得Go core team目前的做法还是可以很好的保持Go语言在版本上的理想的兼容性和发展的一致性的,对于一门面向工程领域的语言而言,这也许是开发者们较为看重的东西;编程语言语法在不同版本间“跳跃式”的演进也许会在短时间内带来新鲜感,但长久看来,对代码阅读和维护而言,都会有一个不小的负担。

下面回归正题。Go 1.7究竟带来了哪些值得关注的变化呢?马上揭晓^_^。(以下测试所使用的Go版本为go 1.7 beta2)。

一、语言

Go 1.7在版本计划阶段设定的目标就是改善和优化(polishing),因此在Go语言(Specification)规范方面继续保持着与Go 1兼容,因此理论上Go 1.7的发布对以往Go 1兼容的程序而言是透明的,已存在的代码均可以正常通过Go 1.7的编译并正确执行。

不过Go 1.7还是对Go1 Specs中关于“Terminating statements”的说明作了一个extremely tiny的改动:

A statement list ends in a terminating statement if the list is not empty and its final statement is terminating.
=>
A statement list ends in a terminating statement if the list is not empty and its final non-empty statement is terminating.

Specs是抽象的,例子是生动的,我们用一个例子来说明一下这个改动:

// go17-examples/language/f.go

package f

func f() int {
    return 3
    ;
}

对于f.go中f函数的body中的语句列表(statement list),所有版本的go compiler或gccgo compiler都会认为其在”return 3″这个terminating statement处terminate,即便return语句后面还有一个“;”也没关系。但Go 1.7之前的gotype工具却严格按照go 1.7之前的Go 1 specs中的说明进行校验,由于最后的statement是”;” – 一个empty statement,gotype会提示:”missing return”:

// Go 1.7前版本的gotype

$gotype f.go
f.go:6:1: missing return

于是就有了gotype与gc、gccgo行为的不一致!为此Go 1.7就做了一些specs上的改动,将statements list的terminate点从”final statement”改为“final non-empty statement”,这样即便后面再有”;”也不打紧了。于是用go 1.7中的gotype执行同样的命令,得到的结果却不一样:

// Go 1.7的gotype
$gotype f.go
没有任何错误输出

gotype默认以源码形式随着Go发布,我们需要手工将其编译为可用的工具,编译步骤如下:

$cd $GOROOT/src/go/types
$go build gotype.go
在当前目录下就会看到gotype可执行文件,你可以将其mv or cp到$GOBIN下,方便在命令行中使用。

二、Go Toolchain(工具链)

Go的toolchain的强大实用是毋容置疑的,也是让其他编程语言Fans直流口水的那部分。每次Go major version release,Go工具链都会发生或大或小的改进,这次也不例外。

1、SSA

SSA(Static Single-Assignment),对于大多数开发者来说都是不熟悉的,也是不需要关心的,只有搞编译器的人才会去认真研究它究竟为何物。对于Go语言的使用者而言,SSA意味着让编译出来的应用更小,运行得更快,未来有更多的优化空间,而这一切的获得却不需要Go开发者修改哪怕是一行代码^_^。

在Go core team最初的计划中,SSA在Go 1.6时就应该加入,但由于Go 1.6开发周期较为短暂,SSA的主要开发者Keith Randall没能按时完成相关开发,尤其是在性能问题上没能达到之前设定的目标,因此merge被推迟到了Go 1.7。即便是Go 1.7,SSA也只是先完成了x86-64系统。
据实而说,SSA后端的引入,风险还是蛮大的,因此Go在编译器中加入了一个开关”-ssa=0|1″,可以让开发者自行选择是否编译为SSA后端,默认情况下,在x86-64平台下SSA后端是打开的。同时,Go 1.7还修改了包导出的元数据的格式,由以前的文本格式换成了更为短小精炼的二进制格式,这也让Go编译出来的结果文件的Size更为small。

我们可以简单测试一下上述两个优化后对编译后结果的影响,我们以编译github.com/bigwhite/gocmpp/examples/client/例:

-rwxrwxr-x 1 share share 4278888  6月 20 14:20 client-go16*
-rwxrwxr-x 1 share share 3319205  6月 20 14:04 client-go17*
-rwxrwxr-x 1 share share 3319205  6月 20 14:05 client-go17-no-newexport*
-rwxrwxr-x 1 share share 3438317  6月 20 14:04 client-go17-no-ssa*
-rwxrwxr-x 1 share share 3438317  6月 20 14:03 client-go17-no-ssa-no-newexport*

其中:client-go17-no-ssa是通过下面命令行编译的:

$go build -a -gcflags="-ssa=0" github.com/bigwhite/gocmpp/examples/client

client-go17-no-newexport*是通过下面命令行编译的:

$go build -a -gcflags="-newexport=0" github.com/bigwhite/gocmpp/examples/client

client-go17-no-ssa-no-newexport是通过下面命令行编译的:

$go build -a -gcflags="-newexport=0 -ssa=0" github.com/bigwhite/gocmpp/examples/client

对比client-go16和client-go17,我们可以看到默认情况下Go 17编译出来的可执行程序(client-go17)比Go 1.6编译出来的程序(client-go16)小了约21%,效果十分明显。这也与Go官方宣称的file size缩小20%~30%de 平均效果相符。

不过对比client-go17和client-go17-no-newexport,我们发现,似乎-newexport=0并没有起到什么作用,两个最终可执行文件的size相同。这个在ubuntu 14.04以及darwin平台上测试的结果均是如此,暂无解。

引入SSA后,官方说法是:程序的运行性能平均会提升5%~35%,数据来源于官方的benchmark数据,这里就不再重复测试了。

2、编译器编译性能

Go 1.5发布以来,Go的编译器性能大幅下降就遭到的Go Fans们的“诟病”,虽然Go Compiler的性能与其他编程语言横向相比依旧是“独领风骚”。最差时,Go 1.5的编译构建时间是Go 1.4.x版本的4倍还多。这个问题也引起了Golang老大Rob Pike的极大关注,在Russ Cox筹划Go 1.7时,Rob Pike就极力要求要对Go compiler&linker的性能进行优化,于是就有了Go 1.7“全民优化”Go编译器和linker的上百次commit,至少从目前来看,效果是明显的。

Go大神Dave Cheney为了跟踪开发中的Go 1.7的编译器性能情况,建立了三个benchmark:benchjujubenchkubebenchgogs。Dave上个月最新贴出的一幅性能对比图显示:编译同一项目,Go 1.7编译器所需时间仅约是Go 1.6的一半,Go 1.4.3版本的2倍;也就是说经过优化后,Go 1.7的编译性能照比Go 1.6提升了一倍,离Go 1.4.3还有一倍的差距。

img{}

3、StackFrame Pointer

在Go 1.7功能freeze前夕,Russ Cox将StackFrame Pointer加入到Go 1.7中了,目的是使得像Linux Perf或Intel Vtune等工具能更高效的抓取到go程序栈的跟踪信息。但引入STackFrame Pointer会有一些性能上的消耗,大约在2%左右。通过下面环境变量设置可以关闭该功能:

export GOEXPERIMENT=noframepointer

4、Cgo增加C.CBytes

Cgo的helper函数在逐渐丰富,这次Cgo增加C.CBytes helper function就是源于开发者的需求。这里不再赘述Cgo的这些Helper function如何使用了,通过一小段代码感性了解一下即可:

// go17-examples/gotoolchain/cgo/print.go

package main

// #include <stdio.h>
// #include <stdlib.h>
//
// void print(void *array, int len) {
//  char *c = (char*)array;
//
//  for (int i = 0; i < len; i++) {
//      printf("%c", *(c+i));
//  }
//  printf("\n");
// }
import "C"

import "unsafe"

func main() {
    var s = "hello cgo"
    csl := C.CBytes([]byte(s))
    C.print(csl, C.int(len(s)))
    C.free(unsafe.Pointer(csl))
}

执行该程序:

$go run print.go
hello cgo

5、其他小改动

  • 经过Go 1.5和Go 1.6实验的go vendor机制在Go 1.7中将正式去掉GO15VENDOREXPERIMENT环境变量开关,将vendor作为默认机制。
  • go get支持git.openstack.org导入路径。
  • go tool dist list命令将打印所有go支持的系统和硬件架构,在我的机器上输出结果如下:
$go tool dist list
android/386
android/amd64
android/arm
android/arm64
darwin/386
darwin/amd64
darwin/arm
darwin/arm64
dragonfly/amd64
freebsd/386
freebsd/amd64
freebsd/arm
linux/386
linux/amd64
linux/arm
linux/arm64
linux/mips64
linux/mips64le
linux/ppc64
linux/ppc64le
linux/s390x
nacl/386
nacl/amd64p32
nacl/arm
netbsd/386
netbsd/amd64
netbsd/arm
openbsd/386
openbsd/amd64
openbsd/arm
plan9/386
plan9/amd64
plan9/arm
solaris/amd64
windows/386
windows/amd64

三、标准库

1、支持subtests和sub-benchmarks

表驱动测试是golang内置testing框架的一个最佳实践,基于表驱动测试的思路,Go 1.7又进一步完善了testing的组织体系,增加了subtests和sub-benchmarks。目的是为了实现以下几个Features:

  • 通过外部command line(go test –run=xx)可以从一个table中选择某个test或benchmark,用于调试等目的;
  • 简化编写一组相似的benchmarks;
  • 在subtest中使用Fail系列方法(如FailNow,SkipNow等);
  • 基于外部或动态表创建subtests;
  • 更细粒度的setup和teardown控制,而不仅仅是TestMain提供的;
  • 更多的并行控制;
  • 与顶层函数相比,对于test和benchmark来说,subtests和sub-benchmark代码更clean。

下面是一个基于subtests文档中demo改编的例子:

传统的Go 表驱动测试就像下面代码中TestSumInOldWay一样:

// go17-examples/stdlib/subtest/foo_test.go

package foo

import (
    "fmt"
    "testing"
)

var tests = []struct {
    A, B int
    Sum  int
}{
    {1, 2, 3},
    {1, 1, 2},
    {2, 1, 3},
}

func TestSumInOldWay(t *testing.T) {
    for _, tc := range tests {
        if got := tc.A + tc.B; got != tc.Sum {
            t.Errorf("%d + %d = %d; want %d", tc.A, tc.B, got, tc.Sum)
        }
    }
}

对于这种传统的表驱动测试,我们在控制粒度上仅能在顶层测试方法层面,即TestSumInOldWay这个层面:

$go test --run=TestSumInOldWay
PASS
ok      github.com/bigwhite/experiments/go17-examples/stdlib/subtest    0.008s

同时为了在case fail时更容易辨别到底是哪组数据导致的问题,Errorf输出时要带上一些测试数据的信息,比如上面代码中的:”%d+%d=%d; want %d”。

若通过subtests来实现,我们可以将控制粒度细化到subtest层面。并且由于subtest自身具有subtest name唯一性,无需在Error中带上那组测试数据的信息:

// go17-examples/stdlib/subtest/foo_test.go

func assertEqual(A, B, expect int, t *testing.T) {
    if got := A + B; got != expect {
        t.Errorf("got %d; want %d", got, expect)
    }
}

func TestSumSubTest(t *testing.T) {
    //setup code ... ...

    for i, tc := range tests {
        t.Run("A=1", func(t *testing.T) {
            if tc.A != 1 {
                t.Skip(i)
            }
            assertEqual(tc.A, tc.B, tc.Sum, t)
        })

        t.Run("A=2", func(t *testing.T) {
            if tc.A != 2 {
                t.Skip(i)
            }
            assertEqual(tc.A, tc.B, tc.Sum, t)
        })
    }

    //teardown code ... ...
}

我们故意将tests数组中的第三组测试数据的Sum值修改错误,这样便于对比测试结果:

var tests = []struct {
    A, B int
    Sum  int
}{
    {1, 2, 3},
    {1, 1, 2},
    {2, 1, 4},
}

执行TestSumSubTest:

$go test --run=TestSumSubTest
--- FAIL: TestSumSubTest (0.00s)
    --- FAIL: TestSumSubTest/A=2#02 (0.00s)
        foo_test.go:19: got 3; want 4
FAIL
exit status 1
FAIL    github.com/bigwhite/experiments/go17-examples/stdlib/subtest    0.007s

分别执行”A=1″和”A=2″的两个subtest:

$go test --run=TestSumSubTest/A=1
PASS
ok      github.com/bigwhite/experiments/go17-examples/stdlib/subtest    0.007s

$go test --run=TestSumSubTest/A=2
--- FAIL: TestSumSubTest (0.00s)
    --- FAIL: TestSumSubTest/A=2#02 (0.00s)
        foo_test.go:19: got 3; want 4
FAIL
exit status 1
FAIL    github.com/bigwhite/experiments/go17-examples/stdlib/subtest    0.007s

测试的结果验证了前面说到的两点:
1、subtest的输出自带唯一标识,比如:“FAIL: TestSumSubTest/A=2#02 (0.00s)”
2、我们可以将控制粒度细化到subtest的层面。

从代码的形态上来看,subtest支持对测试数据进行分组编排,比如上面的测试就将TestSum分为A=1和A=2两组,以便于分别单独控制和结果对比。

另外由于控制粒度支持subtest层,setup和teardown也不再局限尽在TestMain级别了,开发者可以在每个top-level test function中,为其中的subtest加入setup和teardown,大体模式如下:

func TestFoo(t *testing.T) {
    //setup code ... ...

    //subtests... ...

    //teardown code ... ...
}

Go 1.7中的subtest同样支持并发执行:

func TestSumSubTestInParalell(t *testing.T) {
    t.Run("blockgroup", func(t *testing.T) {
        for _, tc := range tests {
            tc := tc
            t.Run(fmt.Sprint(tc.A, "+", tc.B), func(t *testing.T) {
                t.Parallel()
                assertEqual(tc.A, tc.B, tc.Sum, t)
            })
        }
    })
    //teardown code
}

这里嵌套了两层Subtest,”blockgroup”子测试里面的三个子测试是相互并行(Paralell)执行,直到这三个子测试执行完毕,blockgroup子测试的Run才会返回。而TestSumSubTestInParalell与foo_test.go中的其他并行测试function(如果有的话)的执行是顺序的。

sub-benchmark在形式和用法上与subtest类似,这里不赘述了。

2、Context包

Go 1.7将原来的golang.org/x/net/context包挪入了标准库中,放在$GOROOT/src/context下面,这显然是由于context模式用途广泛,Go core team响应了社区的声音,同时这也是Go core team自身的需要。Std lib中net、net/http、os/exec都用到了context。关于Context的详细说明,没有哪个比Go team的一篇”Go Concurrent Patterns:Context“更好了。

四、其他改动

Runtime这块普通开发者很少使用,一般都是Go core team才会用到。值得注意的是Go 1.7增加了一个runtime.Error(接口),所有runtime引起的panic,其panic value既实现了标准error接口,也实现了runtime.Error接口。

Golang的GC在1.7版本中继续由Austin Clements和Rick Hudson进行打磨和优化。

Go 1.7编译的程序的执行效率由于SSA的引入和GC的优化,整体上会平均提升5%-35%(在x86-64平台上)。一些标准库的包得到了显著的优化,比如:crypto/sha1, crypto/sha256, encoding/binary, fmt, hash/adler32, hash/crc32, hash/crc64, image/color, math/big, strconv, strings, unicode, 和unicode/utf16,性能提升在10%以上。

Go 1.7还增加了对使用二进制包(非源码)构建程序的实验性支持(出于一些对商业软件发布形态的考虑),但Go core team显然是不情愿在这方面走太远,不承诺对此进行完整的工具链支持。

标准库中其他的一些细微改动,大家尽可以参考Go 1.7 release notes。

本文涉及到的example代码在这里可以下载到。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats