原文地址:簡單圍觀一下有趣的 //go: 指令html
若是你平時有翻看源碼的習慣,你確定會發現。咦,怎麼有的方法上面老是寫着 //go:
這類指令呢。他們究竟是幹嗎用的?git
今天咱們一同揭開他們的面紗,我將簡單給你介紹一下,它們都負責些什麼github
//go:linkname localname importpath.name
該指令指示編譯器使用 importpath.name
做爲源代碼中聲明爲 localname
的變量或函數的目標文件符號名稱。可是因爲這個僞指令,能夠破壞類型系統和包模塊化。所以只有引用了 unsafe
包纔可使用golang
簡單來說,就是 importpath.name
是 localname
的符號別名,編譯器實際上會調用 localname
。但前提是使用了 unsafe
包才能使用模塊化
... func now() (sec int64, nsec int32, mono int64)
import _ "unsafe" // for go:linkname //go:linkname time_now time.now func time_now() (sec int64, nsec int32, mono int64) { sec, nsec = walltime() return sec, nsec, nanotime() - startNano }
在這個案例中能夠看到 time.now
,它並無具體的實現。若是你初看可能會懵逼。這時候建議你全局搜索一下源碼,你就會發現其實如今 runtime.time_now
中函數
配合先前的用法解釋,可得知在 runtime 包中,咱們聲明瞭 time_now
方法是 time.now
的符號別名。而且在文件頭引入了 unsafe
達成前提條件性能
//go:noescape
該指令指定下一個有聲明但沒有主體(意味着實現有可能不是 Go)的函數,不容許編譯器對其作逃逸分析優化
通常狀況下,該指令用於內存分配優化。由於編譯器默認會進行逃逸分析,會經過規則斷定一個變量是分配到堆上仍是棧上。但凡事有意外,一些函數雖然逃逸分析其是存放到堆上。可是對於咱們來講,它是特別的。咱們就可使用 go:noescape
指令強制要求編譯器將其分配到函數棧上ui
// memmove copies n bytes from "from" to "to". // in memmove_*.s //go:noescape func memmove(to, from unsafe.Pointer, n uintptr)
咱們觀察一下這個案例,它知足了該指令的常見特性。以下:this
//go:nosplit
該指令指定文件中聲明的下一個函數不得包含堆棧溢出檢查。簡單來說,就是這個函數跳過堆棧溢出的檢查
//go:nosplit func key32(p *uintptr) *uint32 { return (*uint32)(unsafe.Pointer(p)) }
//go:nowritebarrierrec
該指令表示編譯器遇到寫屏障時就會產生一個錯誤,而且容許遞歸。也就是這個函數調用的其餘函數若是有寫屏障也會報錯。簡單來說,就是針對寫屏障的處理,防止其死循環
//go:nowritebarrierrec func gcFlushBgCredit(scanWork int64) { ... }
//go:yeswritebarrierrec
該指令與 go:nowritebarrierrec
相對,在標註 go:nowritebarrierrec
指令的函數上,遇到寫屏障會產生錯誤。而當編譯器遇到 go:yeswritebarrierrec
指令時將會中止
//go:yeswritebarrierrec func gchelper() { ... }
該指令表示該函數禁止進行內聯
//go:noinline func unexportedPanicForTesting(b []byte, i int) byte { return b[i] }
咱們觀察一下這個案例,是直接經過索引取值,邏輯比較簡單。若是不加上 go:noinline
的話,就會出現編譯器對其進行內聯優化
顯然,內聯有好有壞。該指令就是提供這一特殊處理
//go:norace
該指令表示禁止進行競態檢測。而另一種常見的形式就是在啓動時執行 go run -race
,可以檢測應用程序中是否存在雙向的數據競爭。很是有用
//go:norace func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { ... }
//go:notinheap
該指令經常使用於類型聲明,它表示這個類型不容許從 GC 堆上進行申請內存。在運行時中經常使用其來作較低層次的內部結構,避免調度器和內存分配中的寫屏障。可以提升性能
// notInHeap is off-heap memory allocated by a lower-level allocator // like sysAlloc or persistentAlloc. // // In general, it's better to use real types marked as go:notinheap, // but this serves as a generic type for situations where that isn't // possible (like in the allocators). // //go:notinheap type notInHeap struct{}
在本文咱們簡單介紹了一些常見的指令集,我建議僅供瞭解。通常咱們是用不到的,由於你的瓶頸可能更多的在自身應用上
可是瞭解這一些,對你瞭解底層源碼和運行機制會更有幫助。若是想再深刻些,可閱讀我給出的參考連接 :)