go 编译指示

Created

2024-10-28 13:24:30

Updated

2024-10-28 13:24:47

Tip
  • 编译指示一种语言结构,它指示编译器应该如何处理其输入
  • go中这样的注释 //go:(注释//与go没有空格) 就是 Go 语言编译指示的实现方式

1 //go:noinline

什么是inline内联优化?
  • 函数调用需要将相关寄存器中的数据入栈来保存现场,函数调用完毕又需要弹栈来恢复现场,频繁的这样消耗时间,影响执行效率
  • 内联就是将调用的函数直接复制到调用处.这样就不用频繁入栈出栈,减少了开销,执行速度快了.
  • 内联的问题
    • 增加了代码的重复
    • 如果函数被很多很多其他地方调用,都内联优化了,如果本来一个地方被缓存,读取指令快,现在可能缓存没有命中,得重新读取再缓存,慢了
内联优化对比
func show(){
    fmt.Println("hello")
}
func test(){
    show()
}
// ==> 内联优化后
func show(){
    fmt.Println("hello")
}
func test(){
    // 直接复制到调用处
    fmt.Println("hello")
}
Tip
  • //go:noinline 顾名思义就是禁止内联优化,通常我们需要调式的时候用
  • 后面必须跟着 func 的申明
package main

// 哪个func 不想被内联优化, 就在哪个上面写
//go:noinline
func p() {
    println(2)
}

func main() {
    a := 1
    println(a)
    p()
}
# 代码中 写了 禁止内联, 查看汇编就可以不需要 -l
go tool compile -S  main.go |grep "main.go:11"

2 //go:linkname

Tip
  • 当你想要使用别的包(自己写的包或者go源码的包都行)没有导出(私有)的变量和方法时
  • //go:linkname localname [importpath.name]
  • 调试代码时可能会用到这类技术
  • 使用go源码中的包

runtime.inheap 这个方法正常情况下我们是没法调用的

package main

import (
    //  如果没有使用  也需要import 
    // _ "unsafe"
    "unsafe"
)

// 表示 inheap 的真正实现是 runtime.inheap
//
//go:linkname inheap runtime.inheap
func inheap(arg uintptr) bool  // 方法申明,去看 runtime.inheap 复制过来
func main() {
    var a int = 1
    // false 表示在栈上
    println(inheap(uintptr(unsafe.Pointer(&a))))
}
  • 自己的包中的私有方法
.
├── go.mod  # mod name  是test
├── main.go
├── pkg
   └── show.go
package pkg

import "fmt"

func s() {
    fmt.Println(11)
}
package main

import (
    _ "test/pkg"
    _ "unsafe"
)

func main() {
    show()
}
// 真正实现是test/pkg.s 函数
//
//go:linkname show test/pkg.s
func show()
Back to top