七叶笔记 » golang编程 » golang编程的淫巧

golang编程的淫巧

在座的看看官,首先,本文毫无技术含量,只是作者做个放置忘记的参考笔记用的,所以,你们还是别往下看了!谢谢!

我们如果查看过go源码的一般都会看到有 //go: xxx 之类的注释。那么这种注释到底是干嘛用的呢?其实啊,这种注释都是有特殊含义的,一般就是为了提醒 编译器对代码做相应的处理的。来,下面我们就说说这些特殊的注释吧。

//go:noinline

表示不做内联

(什么是内联? Inline,是在编译期间发生的,将函数调用调用处替换为被调用函数主体的一种编译器优化手段)

内联的好坏:

好处:

  • 减少函数调用的开销,提高执行速度。
  • 复制后的更大函数体为其他编译优化带来可能性,如 过程间优化
  • 消除分支,并改善空间局部性和指令顺序性,同样可以提高性能。

坏处:

  • 代码复制带来的空间增长。
  • 如果有大量重复代码,反而会降低缓存命中率,尤其对 CPU 缓存是致命的。

简单来说,对于短小而且工作较少的函数,使用内联是有效益的

自动内联的例子:

   

在命令行编译: go tool compile -S inline.go > inline.s

那么我们不需要做内联的时候,则直接在 appendStr( ) 函数上加上注释 //go:noinline

   

执行编译之后: go tool compile -S noinline.go > noinline.s

//go:nosplit

跳过栈溢出检测。

什么是栈溢出?

一个 Goroutine 的起始栈大小是有限制的,且比较小的,才可以做到支持并发很多 Goroutine,并高效调度。
stack.go 源码中可以看到,_StackMin 是 2048 字节,也就是 2k,它不是一成不变的,当不够用时,它会动态地增长。
那么,必然有一个检测的机制,来保证可以及时地知道栈不够用了,然后再去增长。
回到话题,nosplit 就是将这个跳过这个机制。

显然地,不执行栈溢出检查,可以提高性能,但同时也有可能发生 stack overflow 而导致编译失败

//go:noescape

禁止逃逸。

注意: 它必须指示一个只有声明没有主体的函数

什么是逃逸?Go 相比 C、C++ 是内存更为安全的语言,主要一个点就体现在它可以 自动地将超出自身生命周期的变量,从函数栈转移到堆中 ,逃逸就是指这种行为。

最显而易见的好处是,GC 压力变小了。
因为它已经告诉编译器,下面的函数无论如何都不会逃逸,那么当函数返回时,其中的资源也会一并都被销毁。
不过,这么做代表会绕过编译器的逃逸检查,一旦进入运行时,就有可能导致严重的错误及后果。

//go:norace

跳过竞态检测。 除了减少编译时间,没鸡毛用。

//go:notinheap

用于类型声明

表示 这个类型不允许从 GC 堆上进行申请内存

在运行时中 常用其来做较低层次的内部结构,避免调度器和内存分配中的写屏障。能够提高性能。

//go:nowritebarrierrec 和

//go:yeswritebarrierrec

搞不懂这两个,应该是和 写屏障 相关的谢谢!

**//go:linkname 当前函数名/变量名【**localname

**】 被关联的函数名/变量名(包含包名)【**importpath.name

黑魔法 。由于这个伪指令,可以破坏类型系统和包模块化。因此 只有引用了 unsafe 包才可以使用

简单来讲,就是 ** importpath.name 是 localname 的符号别名,编译器实际上会调用 localname** 。

注意: linkname 是双向的,具有导出 和导入 两种使用方式。

导出的不能被作为导入,但是导入的可以作为多个导入, 仅限函数,变量不行

相关文章