Golang 編譯速度為什么這么快?
共 2770字,需瀏覽 6分鐘
·
2024-07-22 08:00
概述
開始接觸 GO 語(yǔ)言的時(shí)候,筆者已經(jīng)擁有 C/C++, Java 三種靜態(tài)語(yǔ)言和 PHP, Javascript 兩種動(dòng)態(tài)語(yǔ)言的基礎(chǔ)。正因?yàn)槿绱耍谌腴T Go 語(yǔ)言的過(guò)程中, 第一次編譯一個(gè)幾百行的 demo 文件時(shí),著實(shí)被其編譯速度驚到了,當(dāng)時(shí)心中感慨: “Go 語(yǔ)言號(hào)稱靜態(tài)語(yǔ)言的執(zhí)行速度,動(dòng)態(tài)語(yǔ)言的編譯速度,果然名不虛傳”。
事實(shí)上,Google 發(fā)明 Go 語(yǔ)言的核心訴求就是解決其內(nèi)部的大型項(xiàng)目代碼每次構(gòu)建時(shí)都需要花費(fèi)很長(zhǎng)時(shí)間的問(wèn)題,拋開技術(shù)的產(chǎn)生歷史背景, 本文簡(jiǎn)單介紹一下 Go 語(yǔ)言編譯速度背后的主要優(yōu)化方案。
極簡(jiǎn)關(guān)鍵字
Go 語(yǔ)言只有 25 個(gè)關(guān)鍵字,這有助于縮短編譯時(shí)間。
符號(hào)表
符號(hào)表是編譯器中的一種數(shù)據(jù)結(jié)構(gòu),用于存儲(chǔ)程序中的標(biāo)志符 (變量、函數(shù)、結(jié)構(gòu)體/對(duì)象) 等,主要作用是提供了一個(gè)管理程序中的各種標(biāo)志符的快捷方式, 以便在程序編譯的不同階段進(jìn)行引用、解析、優(yōu)化等工作。
Go 語(yǔ)言中并沒有直接 (顯式) 的符號(hào)表,編譯器會(huì)在編譯過(guò)程中創(chuàng)建一些內(nèi)部數(shù)據(jù)結(jié)構(gòu)來(lái)管理標(biāo)志符和類型信息,雖然這些內(nèi)部數(shù)據(jù)結(jié)構(gòu)直接暴露給開發(fā)者使用的符號(hào)表, 但是可以看做是編譯器的 “內(nèi)部符號(hào)表”。
此外,Go 語(yǔ)言提供了反射 (Reflection) 機(jī)制,可以在運(yùn)行時(shí)獲取類型、結(jié)構(gòu)體字段、調(diào)用方法/堆棧 等, 也可以獲取和修改對(duì)象的字段,開發(fā)者可以通過(guò)這種間接的方式獲取類似符號(hào)表的信息。
依賴分析
這是 Go 語(yǔ)言編譯速度性能提升的主要原因。
消除循環(huán)依賴
Go 語(yǔ)言出現(xiàn)循環(huán)依賴會(huì)在編譯時(shí)報(bào)錯(cuò),在開發(fā)者解決了循環(huán)依賴問(wèn)題之后,編譯過(guò)程只需要遞歸到依賴關(guān)系樹的底部即可,本質(zhì)上就是一個(gè) DFS (深度優(yōu)先搜索) 過(guò)程。此外在沒有循環(huán)依賴的前提下,每個(gè)包可以獨(dú)立并行編譯。
依賴簡(jiǎn)化
Go 語(yǔ)言代碼文件中只需要包含直接在代碼中使用到的包名,例如當(dāng)我們需要使用 Cookie 對(duì)象時(shí), 只需要引入 cookiejar 包即可,不需要引入 http 包。
package main
import "net/http/cookiejar"
func main() {
var c cookiejar.Jar
}
與此同時(shí),Go 語(yǔ)言中引入未使用的包也會(huì)產(chǎn)生編譯錯(cuò)誤:
imported and not used: ...
這個(gè)編譯前置約束可以保證不相關(guān)的包對(duì)編譯時(shí)間造成影響。
最后,Go 語(yǔ)言規(guī)定源文件中所有使用到的包必須在文件開始處 (package 語(yǔ)句之后) 全部列出,因此編譯器無(wú)須讀完整個(gè)文件內(nèi)容就可以確定依賴關(guān)系。已經(jīng)編譯完成的 Go 包的目標(biāo)文件不僅記錄了包文件的導(dǎo)出信息,還記錄了其本身依賴的包導(dǎo)出信息,編譯時(shí)每次導(dǎo)入一個(gè)目標(biāo)文件即可,不需要查看其他信息。
不支持重載/重寫
Go 語(yǔ)言不支持方法重寫和方法重載,也就是說(shuō),所有的方法都可以視為靜態(tài)類型的,就像 Java 里面的 static 方法。
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
不支持模板
Go 語(yǔ)言同樣不支持模板方法,這可以避免模板實(shí)例化帶來(lái)的性能開銷。
無(wú)須虛擬機(jī)
Go 語(yǔ)言和 Java 一樣,也是自帶 GC 的語(yǔ)言,但是不同的地方在于,Java 是在虛擬機(jī)中編譯的,因此 Java 代碼在通過(guò)編譯器之前必須先編譯為字節(jié)碼, 但是 Go 不依賴虛擬機(jī)編譯代碼,而是直接從源代碼編譯為二進(jìn)制文件。
Reference
-
Why Go compiles so fast[1] -
What compiler technology is used to build the compilers?[2] -
How does Go compile so quickly?[3] -
why-golang-is-so-fast-performance-analysis[4] -
深入理解 Go 語(yǔ)言的編譯與內(nèi)存管理[5] -
Go 語(yǔ)言鏈接器簡(jiǎn)介[6]
鏈接
Why Go compiles so fast: https://devrajcoder.medium.com/why-go-compiles-so-fast-772435b6bd86
[2]What compiler technology is used to build the compilers?: https://go.dev/doc/faq#What_compiler_technology_is_used_to_build_the_compilers
[3]How does Go compile so quickly?: https://stackoverflow.com/questions/2976630/how-does-go-compile-so-quickly
[4]why-golang-is-so-fast-performance-analysis: https://www.bairesdev.com/blog/why-golang-is-so-fast-performance-analysis/
[5]深入理解 Go 語(yǔ)言的編譯與內(nèi)存管理: https://zhuanlan.zhihu.com/p/619851340
[6]Go語(yǔ)言鏈接器簡(jiǎn)介: https://talkgo.org/t/topic/4043
