内容简介:一上来不太好说明白 Go 语言里我猜,大部分人第一行代码都是在计算机编程中,
前言
C 语言的 #include
一上来不太好说明白 Go 语言里 //go:
是什么,我们先来看下非常简单,也是几乎每个写代码的人都知道的东西:C 语言的 #include
。
我猜,大部分人第一行代码都是 #include
吧。完整的就是 #include <stdio.h>
。意思很简单,引入一个 stdio.h
。谁引入?答案是 编译器
。那么, #
字符的作用就是给 编译器
一个 指示
,让编译器知道接下来要做什么。
编译指示
在计算机编程中, 编译指示(pragma)
是一种语言结构,它指示编译器应该如何处理其输入。 指示
不是编程语言语法的一部分,因编译器而异。
这里 Wiki 详细介绍了它,值得你看一下。
Go 语言的编译指示
官方文档 https://golang.org/cmd/compil...
形如 //go:
就是 Go 语言编译指示的实现方式。相信看过 Go SDK 的同学对此并不陌生,经常能在代码函数声明的上一行看到这样的写法。
有同学会问了, //
这不是注释吗?确实,它是以注释的形式存在的。
编译器源码
这里可以看到全部的指示,但是要注意, //go:
是连续的, //
和 go
之间并没有空格。
常用指示详解
//go:noinline
noinline
顾名思义,不要内联。
Inline 内联
Inline
,是在编译期间发生的,将函数调用调用处替换为被调用函数主体的一种编译器优化手段。Wiki: Inline 定义
使用 Inline
有一些优势,同样也有一些问题。
优势:
- 减少函数调用的开销,提高执行速度。
- 复制后的更大函数体为其他编译优化带来可能性,如 过程间优化
- 消除分支,并改善空间局部性和指令顺序性,同样可以提高性能。
问题:
- 代码复制带来的空间增长。
- 如果有大量重复代码,反而会降低缓存命中率,尤其对 CPU 缓存是致命的。
所以,在实际使用中,对于是否使用内联,要谨慎考虑,并做好平衡,以使它发挥最大的作用。
简单来说,对于短小而且工作较少的函数,使用内联是有效益的。
内联的例子
func appendStr(word string) string { return "new " + word }
执行 GOOS=linux GOARCH=386 go tool compile -S main.go > main.S
我截取有区别的部分展出它编译后的样子:
0x0015 00021 (main.go:4) LEAL ""..autotmp_3+28(SP), AX 0x0019 00025 (main.go:4) PCDATA $2, $0 0x0019 00025 (main.go:4) MOVL AX, (SP) 0x001c 00028 (main.go:4) PCDATA $2, $1 0x001c 00028 (main.go:4) LEAL go.string."new "(SB), AX 0x0022 00034 (main.go:4) PCDATA $2, $0 0x0022 00034 (main.go:4) MOVL AX, 4(SP) 0x0026 00038 (main.go:4) MOVL $4, 8(SP) 0x002e 00046 (main.go:4) PCDATA $2, $1 0x002e 00046 (main.go:4) LEAL go.string."hello"(SB), AX 0x0034 00052 (main.go:4) PCDATA $2, $0 0x0034 00052 (main.go:4) MOVL AX, 12(SP) 0x0038 00056 (main.go:4) MOVL $5, 16(SP) 0x0040 00064 (main.go:4) CALL runtime.concatstring2(SB)
可以看到,它并没有调用 appendStr
函数,而是直接把这个函数体的功能内联了。
那么话说回来,如果你不想被内联,怎么办呢?此时就该使用 go//:noinline
了,像下面这样写:
//go:noinline func appendStr(word string) string { return "new " + word }
编译后是:
0x0015 00021 (main.go:4) LEAL go.string."hello"(SB), AX 0x001b 00027 (main.go:4) PCDATA $2, $0 0x001b 00027 (main.go:4) MOVL AX, (SP) 0x001e 00030 (main.go:4) MOVL $5, 4(SP) 0x0026 00038 (main.go:4) CALL "".appendStr(SB)
此时编译器就不会做内联,而是直接调用 appendStr
函数。
//go:nosplit
nosplit
的作用是: 跳过栈溢出检测。
栈溢出是什么?
正是因为一个 Goroutine 的起始栈大小是有限制的,且比较小的,才可以做到支持并发很多 Goroutine,并高效调度。
stack.go
源码中可以看到, _StackMin
是 2048 字节,也就是 2k,它不是一成不变的,当不够用时,它会动态地增长。
那么,必然有一个检测的机制,来保证可以及时地知道栈不够用了,然后再去增长。
回到话题, nosplit
就是将这个跳过这个机制。
优劣
显然地,不执行栈溢出检查,可以提高性能,但同时也有可能发生 stack overflow
而导致编译失败。
//go:noescape
noescape
的作用是: 禁止逃逸,而且它必须指示一个只有声明没有主体的函数。
逃逸是什么?
Go 相比 C、C++ 是内存更为安全的语言,主要一个点就体现在它可以自动地将超出自身生命周期的变量,从函数栈转移到堆中,逃逸就是指这种行为。
请参考我之前的文章, 逃逸分析 。
优劣
最显而易见的好处是,GC 压力变小了。
因为它已经告诉编译器,下面的函数无论如何都不会逃逸,那么当函数返回时,其中的资源也会一并都被销毁。
不过,这么做代表会绕过编译器的逃逸检查,一旦进入运行时,就有可能导致严重的错误及后果。
//go:norace
norace
的作用是: 跳过竞态检测
我们知道,在多线程程序中,难免会出现数据竞争,正常情况下,当编译器检测到有数据竞争,就会给出提示。如:
var sum int func main() { go add() go add() } func add() { sum++ }
执行 go run -race main.go
利用 -race
来使编译器报告数据竞争问题。你会看到:
================== WARNING: DATA RACE Read at 0x00000112f470 by goroutine 6: main.add() /Users/sxs/Documents/go/src/test/main.go:15 +0x3a Previous write at 0x00000112f470 by goroutine 5: main.add() /Users/sxs/Documents/go/src/test/main.go:15 +0x56 Goroutine 6 (running) created at: main.main() /Users/sxs/Documents/go/src/test/main.go:11 +0x5a Goroutine 5 (finished) created at: main.main() /Users/sxs/Documents/go/src/test/main.go:10 +0x42 ================== Found 1 data race(s)
说明两个 goroutine 执行的 add()
在竞争。
优劣
使用 norace
除了减少编译时间,我想不到有其他的优点了。但缺点却很明显,那就是数据竞争会导致程序的不确定性。
总结
我认为绝大多数情况下,无需在编程时使用 //go:
Go 语言的编译器指示,除非你确认你的程序的性能瓶颈在编译器上,否则你都应该先去关心其他更可能出现瓶颈的事情。
以上所述就是小编给大家介绍的《Go 语言编译器的 "//go:" 详解》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- LLVM接受NVIDIA的“f18” Fortran编译器作为官方Fortran编译器
- 编译原理实战入门:用 JavaScript 写一个简单的四则运算编译器(四)结语
- Scala.js 0.6.29 发布,将 Scala 编译成 js 的编译器
- Go 编译器介绍
- Go 编译器介绍
- C++编译器优化
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
从界面到网络空间
(美)海姆 / 金吾伦/刘钢 / 上海科技教育出版社 / 2000-7 / 16.40元
计算机急剧改变了20世纪的生活。今天,我们凭借遍及全球的计算机网络加速了过去以广播、报纸和电视形式进行的交流。思想风驰电掣般在全球翻飞。仅在角落中潜伏着已完善的虚拟实在。在虚拟实在吕,我们能将自己沉浸于感官模拟,不仅对现实世界,也对假想世界。当我们开始在真实世界与虚拟世界之间转换时,迈克尔·海姆问,我们对实在的感觉如何改变?在〈从界面到网络空间〉中,海姆探讨了这一问题,以及信息时代其他哲学问题。他......一起来看看 《从界面到网络空间》 这本书的介绍吧!