浅谈golang1.14特性以及改进

	XXX产品在新版本迭代中涉及到了底层关键包的升级,譬如consul,micro,go-config等,也给过大家进行技术科普并要求golang编译器升级到go1.14。

那现在我准备些材料和大家一起分享,一起学习go1.14 到底都做了哪些重要改进以及特征。

我们先介绍一下背景,早在今年2月份golang发布了GO1.14版本,在2020年3月份的TIOBE编程语言排行榜上Go重新进入TOP 10,而去年同期Go仅排行在第18位。Go在这样的一个时间节点能进入TOP 10,对于Gopher和Go社区来说,总还是一个不错的结果。并且在一定层度上说明:Go在努力耕耘十年后,已经在世界主流编程语言之林中牢牢占据了自己的一个位置(热衷gopher强行吹)。

我们言归正传,一起来香一香go1.14~

一、嵌入接口的方法集可从重叠

这个变化的背后是一个很朴素的思想。我们先来看下面的代码:
图1
定义A和B两个接口,方法集中都包含有string()string这个方法。在这样的情况下,我们如果想定义一个方法集合为Union(I, J)的新接口O ,我们在Go 1.13及之前的版本中只能使用第(2)种方式,即只能在新接口O 中重新书写一遍所有的方法原型,而无法像第(1)种方式那样使用嵌入接口的简洁方式进行。

但是尤其需要注意的是,对于overlapping interface的支持仅限于接口定义,如果在struct定义中嵌入interface的话,是不被允许的。

举个栗子:
图2

对于结构体中嵌入接口的方法集是否存在overlap,go1.14将这个检查延迟到为结构体选择method的执行者环节了。使用结构体时会给出错误:ambiguous selector xxxxxxx 。

这个时候我们让Foo结构体override 一个string() 方法,那么即便接口J和I存在overlap 也无关紧要,因为编译器可以知道Foo实例具体执行哪个。
图3

二、支持异步的抢占式调度

golang最大的特性就是协程,通过协程可以在用户态中最大程度的利用用户线程以来增加程序的效率。那么此次改进之处正是对协程的调度机制GMP的改进。简单的协程概念和GMP调度机制我先不在此次分享和大家介绍了,有兴趣的同学可以和我私下沟通交流。

在解释这个特性之前,先简单介绍一下关键词。
在调度器中,有着三个角色,M(thread)、G(goroutine)和P(Processor)。
前两者大家都很熟悉了,第三位Processor,它是包含了运行goroutine的资源,当线程想运行goroutine,必须先获取P,当然P中还包含了可运行的G队列。M必须持有P才能执行G中的代码,P有自己本地的一个运行队列runq,由可运行的G组成,下图展示了线程M、处理器P和goroutine的关系。
图4

那么我们先来看一段代码:
图5

在当前系统只有一个P的情况下,代码中for所在goroutine将持续占据该P,使得main goroutine中的routine下的代码得不到调度,因此我们无法看到 Connext PanCloud NO.1! 字样输出。换一句话说在Go1.14之前,Go 1.13及以前的版本的抢占是”协作式“的,这种协作式只在有函数调用的地方才能插入“抢占”代码(埋点),而for没有给编译器插入抢占代码的机会。抢占式调度是不会使一个没有主动放弃执行权、且不参与任何函数调用的goroutine被抢占。那这会导致GC在等待所有goroutine停止时等待时间过长,从而导致GC延迟;甚至在一些特殊情况下,导致在STW时死锁。

Go 1.14采用了基于系统信号的异步抢占调度,系统信号可能在代码执行到任意地方发生,在Go runtime能cover到的地方,Go runtime自然会处理好这些系统信号。而GMP调度器的工作原理就是处理器P从本地队列中依次选择goroutine放到线程M上调度执行,每个P维护的G可能是不均衡的,为此调度器维护了一个全局G队列,当P执行完本地的G任务后,会尝试从全局队列中获取G任务运行(需要加锁),当P本地队列和全局队列都没有可运行的任务时,会尝试偷取其他P中的G到本地队列运行(任务窃取)。
图6

三、defer 性能得以继续优化

在Go 1.13中,defer性能得到理论上30%的提升。我们还用那个例子来看看go 1.14与go 1.13版本相比defer性能又有多少提升,同时再看看使用defer和不使用defer的对比:

这里我们采用基准测试 Benchmark
图7

  • go 1.13 defer性能测试数据
    图8

  • go 1.14 defer性能测试数据
    图9

我们看到,Go 1.14的defer性能比Go 1.13还有大幅提升,并且已经与不使用defer的性能相差无几了,Go1.14版本使用defer关闭channel几乎0开销!关于这一改进,官方给出的回应是:Go1.14提高了defer的大多数用法的性能,几乎0开销!defer已经可以用于对性能要求很高的场景了。这也是Go官方和我一起鼓励大家在性能敏感的代码执行路径上也大胆使用defer的原因。

除此之外go在1.14中还对内置的定时器进行了优化,修正了遵循逻辑:将timer分配到每个P上,降低锁竞争;去掉timer thread,减少了上下文切换的开销。甚至是go module都已经production ready了。这对于我们当前的技术架构是个极好的消息。

此次分享先暂告一段落。欢迎小伙伴们在下评论参与讨论。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值