APP下载

Golang 效能优化技巧—基础编码原则

消息来源:baojiabao.com 作者: 发布时间:2024-05-18

报价宝综合消息Golang 效能优化技巧—基础编码原则

Go语言中文网,致力于每日分享编码知识,欢迎关注我,会有意想不到的收获!

前言

高阶设计。为遇到的问题选择适当的算法和资料结构。要特别警觉,避免使用那些会渐进地产生糟糕效能的算法或编码技术基本编码原则。避免限制优化的因素,这样编译器就能产生高效的程式码。消除连续的函式呼叫。在可能时,将计算移到外循环中。考虑有选择地妥协程式的模组性以获得更大的效率。消除不必要的储存器引用。引入临时变数来储存中间结果。只有在最后的值计算出来时,才将结果存放在到阵列或全域性变数中。低阶优化展开循环,降低开销,并且使得进一步的优化成为可能。通过使用例如多个累积变数和重新结合等技术,找到方法提高指令级并行。用功能的风格重写条件操作,使得编译采用条件资料传送。这段话摘自《深入计算机系统原理》一书中,讲的是三3种性能优化方案,针对程式不同层次来提升效能。

高阶设计:指的是程式整体的设计,采用适当的算法和资料结构。

基本编码原则:从指令的角度考虑,开发中应如何编码,才能减少执行的指令。

低阶优化:针对现代处理器,如何让cpu的流水线尽量饱和。

本文主要只对基本编码原则进行说明,通过一个例子说明其编码的原因,由浅入深。给出的例子是用golang程式码编码,虽然《深入理解计算机系统》一书中是用C语言来讲解,但对于也是转成化成机器码的golang来说都是差不多。编码器在转换成机器码的过程中能帮助开发者是有限的,效能的提升更多是需要依赖程序员的设计。

分析

先看一下我们给出的例子,程式码如下:

此处我们建立一个比较大的切片阵列来做求和运算,之所以选用比较大的测试资料,是为了减少执行中其它因素的干扰影响,达到更明显的对比效果。下面是最开始的求和程式码:

此求和是将整个阵列的所有元素加到result指标上,程式码很简洁,但是就是这样的程式码有着非常大的优化空间。我们先按从"基本编码原则"里的第一个点分析。

1.消除连续的函式呼叫

首先是GetDataLen()这句程式码并不需要被反复呼叫,但它现在放在for循环当中。由于呼叫一个函式,处理器执行会增加一定的延迟,这中间需要过程压栈,更改程式计数器,再加上内部呼叫len方法也需要一定的消耗。只是呼叫几次效能上并不会有大的损失,但是成千上百万次的话,效能的差异就明显了。因此以下是对toSum1()函式的改进:

toSum2函式,内部定义一个区域性变数dataLength来储存长度,从而减少了GetDataLen()的呼叫。接着通过以下效能测试,来对两次改进进行效能对比,来检视其效能变化。

执行检视其结果

改进后快了将近20s。这里需要注意的是不同的装置测试结果是不一样的,此资料仅供参考,仅证明该改进是有明显的效能提升的。

其次是第二个方法 GetValue(i)的呼叫,凡是函式或方法的呼叫就会有额外的损耗(压栈、修改计数器等),虽然对处理器来说这个没算什么,毕竟cpu是以ns为执行单位,但还是对该函式再进一步改造。

新增加了一个函式GetData()用于获取整个切片,不再呼叫GetValue()。再执行同样的测试得到以下的资料

对比toSum2()的测试资料,只是提升了几秒的效率,原因是这次减少的是函式的呼叫过程,而对切片内容的访问还是需要的,所以看到的效果不是很明显。(注:此次的改动虽然提高了效能,但考虑到如果开发者不希望知道内部资料结构,那该改动影响对该资料内容的抽象。)

2.消除不必要的储存器引用

*result += data[i] 这段程式码我们首先要明白处理器指行它的时候要经过什么步骤。它主要的过程需要以下几步:

​ 1.通过result地址值,从内存取出资料放在暂存器中(假设暂存器A)

​ 2.再通过切片阵列的首地址获取第i个元素到暂存器中(假设暂存器B)

​ 3.接着将暂存器B 加到暂存器 A中

​ 4.最后再将寄存A写回 result 指向的内存地址

由于*result在这过程中需要反复地读写,是没有必要的操作,因此我们将再对它做以下的改动。

此处的执行将原先放置在 result 位置的复制到区域性变数k中,到最后再将k的值写回result位置中。此处相当是将*result保到固定的暂存器中,让其一直被用作求和运算。此处改动,就相当于节省原有4个步聚里面的1、4步聚,变成以下两个:

​ 1.通过切片阵列的首地址获取第i个元素到暂存器中(假设暂存器B)

​ 2.接着将暂存器B 加到暂存器 A中(假设 k 指向的是暂存器A)

再看看其效能对比:

对比 toSum3,又减少了10秒,效果很明显。虽然这里只是用指标做例子,但阵列和结构体也是一样的,对内部变数的访问是同指针类似。(注:结构体是通过首地址计算再去内存中获取对应的变数值)

总结

此处给出的效能提升只是从指令的角度考虑,并在这过程演示了《深入理解计算机系统》书中所讲的基本编码原则所带来的效益。而如果想要对该函式有更大的提升空间,我们还可以从"低阶忧化"忧化入手,在本文中就暂不讲解,后续有时间再对其做补充。废话也不多说,为本文总结一句:消除连续的函式呼叫和不必要的储存器引用

测试程式码

https://github.com/wpnine/PerformanceExample

作者:wp_nine

连结:https://www.jianshu.com/p/0dafe1059fdc

来源:简书

简书著作权归作者所有,任何形式的转载注明出处。

2019-10-25 21:02:00

相关文章