起因
阅读go的time.Ticker源码时,NewTicker时Ticker内部结构体runtimeTimer的when函数实现,引用了runtimeNano(),跳转后runtimeNano实现如下
// runtimeNano returns the current value of the runtime clock in nanoseconds.
//
//go:linkname runtimeNano runtime.nanotime
func runtimeNano() int64
runtimeNano的具体实现在哪?而go:linkname又是什么?有什么用呢?
//go:******是什么
//go:****** 是编译器指令,编译器接受注释形式的指令。为了使其与正常注释区分开,编译器指令在注释开头和指令之间不使用空格隔开。这样处理的好处是别的工具可以将编译器指令当成注释跳过。
go的编译器指令有哪些
- //go:noescap:禁止逃逸
- //go:nosplit:跳过堆栈溢出检查
- //go:noinline:禁止内联
- //go:norace:跳过竞态检测
- //go:linkname:导入不同包的私有函数
- //go:wasmimport:导入WebAssembly函数
- //go:build:用于指定构建约束条件
- //go:generate:用于生成代码
//go:noescap
禁止逃逸,而//go:noescap必须指示一个只有声明没有主体的函数。禁止该函数自动将超出自身生命周期的变量从函数的栈中转移到堆上。
优点:
- 当引用该编译器指令时,则表明该函数返回时,资源也会一起被销毁,减小GC的压力。
缺点:
- 由于绕过编译器的逃逸检查,一旦该函数有问题,进入运行时就有可能引起严重的错误。
//go:nosplit
跳过堆栈溢出检查,//go:nosplit指示的函数不得包含堆栈溢出检查。在不安全地抢占调用 goroutine 的时间调用的低级运行时最常使用//go:nosplit。
优点:
- 不执行函数堆栈溢出检查,可以提高性能。
缺点:
- 有可能会因为stack overflow 导致程序编译失败。
//go:noinline
禁止内联;内联是在编译时期发生的,将函数体直接展开到调用处的一种优化,主要是消除函数的调用开销。
内联的优点:
- 减少函数调用的开销,提高执行速度。
- 消除逻辑上不呗使用的分支代码或者变量,减少冗余的代码。
内联缺点:
- 有可能导致生成的代码体积膨胀,进而影响指令缓存的命中率。
//go:norace 跳过竞态检测;Go 的竞态检测是一种用于检测Go程序中的竞态条件的工具,发现并修复竞态问题。编译时期使用-race标志启用竞态检测。竞态条件是指多个并发执行的goroutine对同一个变量进行读写操作,而不进行同步,导致结果出现错误。
优点:
- 可以减少竞态检测器的开销和干扰,提高程序的性能
缺点:
- 可能会导致一些潜在的数据竞争问题被泄露,导致程序发生错误。
//go:linkname
//go:linkname localname [importpath.name]
编译器将当前(私有)方法或者变量在编译时链接到指定的位置的方法或者变量,第一个参数表示当前方法或变量,第二个参数表示目标方法或变量,这个指令会破坏系统和包的模块化,在使用时必须导入unsafe。 例子:
//go:linkname runtimeNano runtime.nanotime
func runtimeNano() int64
当前包的runtimerNano函数调用了runtime.nanotime作为该函数的实现。
优点:
- 在一个包中使用另一个包中的非导出函数或变量,可以绕过 Go 语言的可见性规则。
缺点:
- 会破坏go语言的类型系统和包模块化,导致代码的可读性和维护性降低;同时会增加编译的复杂度。因为需要使用单独的compile命令或者添加一个空的汇编文件来绕过complete参数的检查。
//go:wasmimport 用于导入WebAssembly函数的指令,是//export的别名。
//go:wasmimport <funcname> <importmodule> <importname>
<funcname>是go函数的名字,<importmodule>是WebAssembly模块的名字,<importname>是WebAssembly函数的名字。
优点:
- 让go代码更容易和WebAssembly主机交互,不需要使用CallImport或者syscall/js。
- 支持WASI的ABI,遵循WebAssembly生态系统的标准。
缺点:
- 可能和其他语言的WebAssembly函数不兼容,需要注意参数传递和返回值的方式。