Go程序提速42%,只需改變一個(gè)字符
Go開發(fā)團(tuán)隊(duì)的核心人物Russ Cox都在標(biāo)準(zhǔn)庫中犯過同樣的錯(cuò)誤
夢(mèng)晨 發(fā)自 凹非寺量子位 | 公眾號(hào) QbitAI
Go語言本來就以輕量快速著稱,一位GitHub員工卻偶然發(fā)現(xiàn):
只改變一個(gè)字符的位置,能把一段代碼運(yùn)行速度提高足足42%。
簡(jiǎn)直就像是……
這個(gè)簡(jiǎn)單有效的技巧一經(jīng)發(fā)布,就引來眾多程序員圍觀。
原作者自己也調(diào)侃,一般這種情況都是事先犯了個(gè)愚蠢的錯(cuò)誤,后面才能提升這么大。
不過順著這個(gè)思路發(fā)現(xiàn)有人發(fā)現(xiàn),就連Go開發(fā)團(tuán)隊(duì)的核心人物Russ Cox都在標(biāo)準(zhǔn)庫中犯過同樣的錯(cuò)誤。
什么樣的錯(cuò)誤?
發(fā)現(xiàn)這個(gè)問題的Harry在大型程序員交友平臺(tái)GitHub工作。
他在開發(fā)一個(gè)把GitHub倉庫中每個(gè)文件的所有者列出來的小工具。
功能很簡(jiǎn)單,就是根據(jù)CODEOWNERS文件中定義的規(guī)則匹配,寫在越下面的規(guī)則優(yōu)先級(jí)越高。
原理也很簡(jiǎn)單,就是從后往前一條一條處理,匹配到了就停止。
但就是這樣一個(gè)簡(jiǎn)單的程序卻出現(xiàn)了性能問題,處理中等大小的倉庫就很慢了。
他打印出火焰圖,發(fā)現(xiàn)大部分時(shí)間都花在了Go語言的正則表達(dá)式引擎中。
另外在內(nèi)存動(dòng)態(tài)分配malloc和垃圾回收gc上面的花費(fèi)也值得注意。
要減少malloc的時(shí)間,就需要用到Go語言的逃逸分析(Escape Analysis)了。
簡(jiǎn)單來說,就是盡量把變量分配到棧上,讓編譯器自動(dòng)管理內(nèi)存的釋放。
只有在“逃逸”也就是變量的作用域超出所在的棧時(shí),才把變量分配到堆上,減輕運(yùn)行時(shí)GC的壓力。
在這次的程序中,Harry確定了逃逸的變量是rule這個(gè)結(jié)構(gòu)體(struct)。
但問題是,rule存儲(chǔ)在RuleSet這個(gè)切片(slice)里,按Go語言的規(guī)則可以確信他已經(jīng)在堆中了。
再分析一下代碼,發(fā)現(xiàn)在給rule賦值的時(shí)候?qū)嶋H上是做了一次不必要的拷貝,后面用“&”取地址時(shí)候創(chuàng)建了一個(gè)逃逸的指針指向它的副本。
最后解決辦法也很容易想出,只需要把&移動(dòng)到上面。
這樣就引用了切片中的結(jié)構(gòu)體,避免了拷貝。
如何徹底避免?
在熱議中,有網(wǎng)友分享了自己是怎么避免出現(xiàn)這個(gè)問題的。
對(duì)于每個(gè)結(jié)構(gòu)體,把它看作純值或純指針,壓根就不去使用&這種取地址的操作,避免隱式的內(nèi)存分配。
如果你想要深入理解這個(gè)問題,也有人貼心的給出了需要提前了解的一些背景知識(shí)。
最后有人指出,Rust語言為避免這個(gè)問題,直接規(guī)定必須顯式操作才能拷貝一個(gè)數(shù)據(jù)結(jié)構(gòu)。
當(dāng)你不習(xí)慣的時(shí)候這規(guī)定煩得要命,但是總的來看還是值得。
方便or規(guī)范,你更傾向于哪種做法?
參考鏈接:
[1]https://hmarr.com/blog/go-allocation-hunting/
[2]https://news.ycombinator.com/item?id=33594676
- 字節(jié)突然開源Seed-OSS,512K上下文主流4倍長(zhǎng)度,推理能力刷紀(jì)錄2025-08-21
- “現(xiàn)在讀AI博士已經(jīng)太晚了”2025-08-19
- 谷歌AI攻克亞洲語言難題,2300種語言數(shù)字化計(jì)劃正在推進(jìn)2025-08-18
- AMD蘇姿豐公開懟扎克伯格!反對(duì)1億年薪挖人,使命感比鈔票更重要2025-08-18