go单测和内联
内联
概念:内联就是把简短的函数在调用它的地方展开。
内联主要有两个原因:第一个是消除函数调用本身的开销;第二个是它使得编译器能更高效地执行其他的优化策略。
任何语言中,函数调用都会有开销,将参数编组进寄存器或压入栈中,返回结果时倒序取出。引入一次函数调用会导致程序计数器从指令流的一点跳至另一点,可能导致管道阻塞。函数内部通常有前置处理,需要为函数执行准备新的栈帧,还有与前置相似的后续处理,需要在返回给调用方之前释放栈帧空间。
Go中函数调用会消耗额外的资源来支持栈的动态增长。在进入函数时,goroutine可用的栈空间与函数需要的空间大小相等。如果可用空间不同,前置处理就会跳到把数据复制到一块新的、更大空间的运行时逻辑,而这会导致栈空间变大。当这个复制完成后,运行时跳回到原来的函数入口,再执行栈空间检查,函数调用继续执行。这种方式下,goroutine开始时可以申请很小的栈空间,在有需要时再申请更大的空间。2
这个检查消耗很小,只有几个指令,而且由于Goroutine是成几何级数增长的,因此这个检查很少失败。这样,现代处理器的分支预测单元会通过假定检查肯定会成功来隐藏栈空间检查的消耗。当处理器预测错了栈空间检查,必须要抛弃它推测性执行的操作时,与为了增加Goroutine的栈空间运行时所需的操作消耗的资源相比,管道阻塞的代价更小。
虽然现代处理器可以用预测性执行技术优化每次函数调用中的泛型和Go特定的元素的开销,但那些开销不能被完全消除,因此在每次函数调用执行必要的工作过程中都会有性能消耗。一次函数调用本身的开销是固定的,与更大的函数相比,调用小函数的代价更大,因为在每次调用过程中它们做的有用的工作更少。
消除这些开销的方法必须是要消除函数调用本身,Go的编译器就是这么做的,在某些条件下通过用函数的内容来替换函数调用来实现。这个过程被称为内联,因为它在函数调用处把函数体展开了。
单测
问题描述:测试函数在run的时候fail,在无断点debug的时候pass,被patch的函数A如下:
func A(arg string) error {
return B(arg)
}
原因:run的时候会被编译器优化,调用A会直接被优化为调用B(内联),所以对A的Patch没有成功;
解决:go test -gcflags="all=-N -l"避免编译器优化内联
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至yj.mapple@gmail.com
文章标题:go单测和内联
文章字数:803
本文作者:melonshell
发布时间:2020-10-13, 19:59:21
最后更新:2020-10-13, 20:37:49
原始链接:http://melonshell.github.io/2020/10/13/go18_test_and_inline/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。