0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

只改變一個字符使Go程序提速42%

馬哥Linux運(yùn)維 ? 來源:量子位 ? 作者:量子位 ? 2022-11-24 15:48 ? 次閱讀

Go語言本來就以輕量快速著稱,一位GitHub員工卻偶然發(fā)現(xiàn):

只改變一個字符的位置,能把一段代碼運(yùn)行速度提高足足42%。

e60973ee-6b35-11ed-8abf-dac502259ad0.png

簡直就像是……

e61cb9cc-6b35-11ed-8abf-dac502259ad0.png

這個簡單有效的技巧一經(jīng)發(fā)布,就引來眾多程序員圍觀。

原作者自己也調(diào)侃,一般這種情況都是事先犯了個愚蠢的錯誤,后面才能提升這么大。

不過順著這個思路發(fā)現(xiàn)有人發(fā)現(xiàn),就連Go開發(fā)團(tuán)隊的核心人物Russ Cox都在標(biāo)準(zhǔn)庫中犯過同樣的錯誤。

e62da426-6b35-11ed-8abf-dac502259ad0.png

什么樣的錯誤?

發(fā)現(xiàn)這個問題的Harry在大型程序員交友平臺GitHub工作。

他在開發(fā)一個把GitHub倉庫中每個文件的所有者列出來的小工具。

功能很簡單,就是根據(jù)CODEOWNERS文件中定義的規(guī)則匹配,寫在越下面的規(guī)則優(yōu)先級越高。

e6596aac-6b35-11ed-8abf-dac502259ad0.png

原理也很簡單,就是從后往前一條一條處理,匹配到了就停止。

e66c4546-6b35-11ed-8abf-dac502259ad0.png

但就是這樣一個簡單的程序卻出現(xiàn)了性能問題,處理中等大小的倉庫就很慢了。

e6894498-6b35-11ed-8abf-dac502259ad0.png

他打印出火焰圖,發(fā)現(xiàn)大部分時間都花在了Go語言的正則表達(dá)式引擎中。

另外在內(nèi)存動態(tài)分配malloc和垃圾回收gc上面的花費(fèi)也值得注意。

e69a928e-6b35-11ed-8abf-dac502259ad0.png

要減少malloc的時間,就需要用到Go語言的逃逸分析(Escape Analysis)了。

簡單來說,就是盡量把變量分配到棧上,讓編譯器自動管理內(nèi)存的釋放。

只有在“逃逸”也就是變量的作用域超出所在的棧時,才把變量分配到堆上,減輕運(yùn)行時GC的壓力。

在這次的程序中,Harry確定了逃逸的變量是rule這個結(jié)構(gòu)體(struct)。

e6ad0fcc-6b35-11ed-8abf-dac502259ad0.png

但問題是,rule存儲在RuleSet這個切片(slice)里,按Go語言的規(guī)則可以確信他已經(jīng)在堆中了。

再分析一下代碼,發(fā)現(xiàn)在給rule賦值的時候?qū)嶋H上是做了一次不必要的拷貝,后面用“&”取地址時候創(chuàng)建了一個逃逸的指針指向它的副本。

e6c1220a-6b35-11ed-8abf-dac502259ad0.png

最后解決辦法也很容易想出,只需要把&移動到上面。

e6d2be98-6b35-11ed-8abf-dac502259ad0.png

這樣就引用了切片中的結(jié)構(gòu)體,避免了拷貝。

如何徹底避免?

在熱議中,有網(wǎng)友分享了自己是怎么避免出現(xiàn)這個問題的。

對于每個結(jié)構(gòu)體,把它看作純值或純指針,壓根就不去使用&這種取地址的操作,避免隱式的內(nèi)存分配。

e6f17946-6b35-11ed-8abf-dac502259ad0.png

如果你想要深入理解這個問題,也有人貼心的給出了需要提前了解的一些背景知識。

e705e700-6b35-11ed-8abf-dac502259ad0.png

最后有人指出,Rust語言為避免這個問題,直接規(guī)定必須顯式操作才能拷貝一個數(shù)據(jù)結(jié)構(gòu)。

e719c388-6b35-11ed-8abf-dac502259ad0.png

當(dāng)你不習(xí)慣的時候這規(guī)定煩得要命,但是總的來看還是值得。

方便or規(guī)范,你更傾向于哪種做法?

審核編輯 :李倩

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4797

    瀏覽量

    68707
  • go語言
    +關(guān)注

    關(guān)注

    1

    文章

    158

    瀏覽量

    9053

原文標(biāo)題:只改變一個字符使 Go 程序提速 42%

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    stm32/gd32 wifi模塊通信異常問題

    的,wifi模塊都回復(fù)一個字符,這個字符還是發(fā)送指令中的任意(發(fā)送\"AT\",回\"A\"或\"T\"),有時候干脆回復(fù)\"erro
    發(fā)表于 10-21 08:59

    鴻蒙原生應(yīng)用元服務(wù)開發(fā)-倉頡基礎(chǔ)數(shù)據(jù)類型字符類型

    對單引號或雙引號包含的字符。 單個字符字符字面量舉例: let a: Rune = r\'a\' let b: Rune = r\"b\" 轉(zhuǎn)義
    發(fā)表于 09-19 10:58

    MATLAB(5)--字符串處理

    字符串表示 在MATLAB中,字符串是用單引號括起來的字符序列,是把一個字符串當(dāng)做一個行向量,這個行向量中,每個元素對應(yīng)
    發(fā)表于 09-06 10:22

    vim的操作方式有哪幾種

    模式下,用戶可以執(zhí)行各種命令來操作文本,例如移動光標(biāo)、刪除文本、復(fù)制文本等。以下是命令模式下的些常用操作: 1.1 光標(biāo)移動 在命令模式下,用戶可以使用以下按鍵來移動光標(biāo): h:向左移動一個字符 j:向下移動一個字符 k:向上
    的頭像 發(fā)表于 08-30 14:54 ?592次閱讀

    vim的三種工作模式有哪些

    自動進(jìn)入普通模式。在普通模式下,你可以使用各種快捷鍵來移動光標(biāo)、復(fù)制、粘貼、刪除和查找文本等。 1.1 光標(biāo)移動 在普通模式下,你可以使用以下快捷鍵來移動光標(biāo): h:向左移動一個字符 j:向下移動一個字符 k:向上移動一個字符
    的頭像 發(fā)表于 08-30 14:52 ?813次閱讀

    請問AT命令可以支持多少個字符

    AT命令可以有多長?當(dāng)輸入命令長度超過 128 個字符時,它看起來 AT 自定義命令會中斷。
    發(fā)表于 07-16 06:17

    使用CIPDOMAIN命令時,解析長度為64個字符或更大的DNS名稱失敗了,為什么?

    s3.dualstack.us-east-1.amazonaws.com 的CNAME記錄。 我轉(zhuǎn)儲了 libat.a 存檔并找到了at_setupcipomain函數(shù),似乎分配要在堆棧上復(fù)制的 64 字節(jié)的字符串。 首先,有人可以確認(rèn)這個限制嗎? 其次,我們能否獲
    發(fā)表于 07-11 07:59

    FX3在安卓系統(tǒng)上顯示為\"DDC\",有什么辦法可以定義這個字符串嗎?

    正如標(biāo)題所說,當(dāng)我將 FX3 插入安卓設(shè)備時,安卓會詢問應(yīng)用程序是否可以訪問\"DDC\" 。 有什么辦法可以定義這個字符串嗎? 謝謝!
    發(fā)表于 07-03 08:15

    JDY-08藍(lán)牙模塊AT指令響應(yīng)只能接收到第一個字符是怎么回事?

    STM8L051的RX線上有完整的對應(yīng)+OK的字符的波形。且波特率在誤差范圍內(nèi)。 4、試了其他板子,也換了其他波特率和改變停止位、校驗位,都還是只能接收第一個字符。 5、程式上,在接收中斷里都是簡單的賦值
    發(fā)表于 05-07 07:50

    如何提取串口接收字符串?dāng)?shù)組里的某個字符串?

    條(有時候二十多條不定)響應(yīng)字符串指令,我是用一個字符串?dāng)?shù)組來接收這些返回來的指令的。我現(xiàn)在只需要讀取數(shù)組里的某條指令,應(yīng)該怎么把它提取出來啊??有哪位前輩懂的,希望能提供點幫助。我找了好久找到
    發(fā)表于 04-22 06:05

    深入解析西門子博途文本塊接口的結(jié)構(gòu)與功能

    STRING 和 WSTRING 數(shù)據(jù)類型存儲一個字符串中的多個字符。允許在字符串中使用任何 ASCII碼類型的字符。這些字符將使用
    發(fā)表于 04-11 11:23 ?1275次閱讀
    深入解析西門子博途文本塊接口的結(jié)構(gòu)與功能

    使用i2c從從站讀取兩個字節(jié)時,為什么主站發(fā)送一個字節(jié)后就發(fā)送NACK呢?

    當(dāng)我使用 i2c 從從站讀取兩個字節(jié)時,有時會返回 RX_OVERFLOW。 我使用邏輯分析儀抓取總線波形,發(fā)現(xiàn)接收到一個字節(jié),主控器發(fā)出 NACK,之后返回錯誤代碼
    發(fā)表于 03-05 07:42

    請問XMC4500串口接收一個字節(jié)產(chǎn)生中斷會有問題嗎?

    用XMC4500的開發(fā)板做串口中斷,在每次接收單個字符時,我單步運(yùn)行,查看PSR寄存器。接收中斷對數(shù)據(jù)有兩種響應(yīng)。1。當(dāng)數(shù)據(jù)字節(jié)最高位不為1,剩余7位中1的個數(shù)為偶數(shù)時,PSR的第14位(RIF)置
    發(fā)表于 02-06 06:35

    spi讀取多個字節(jié)的時候該怎么判斷UART的上一個字節(jié)已經(jīng)讀完了?

    想問下 spi 的 SPI_SpiIsBusBusy() 的這個API在 UART 中怎么實現(xiàn),讀取多個字節(jié)的時候該怎么判斷 UART 的上一個字節(jié)已經(jīng)讀完了?
    發(fā)表于 02-02 06:54

    如何解決Python爬蟲中文亂碼問題?Python爬蟲中文亂碼的解決方法

    決Python爬蟲中文亂碼問題。 、了解字符編碼 在解決亂碼問題之前,我們首先需要了解些基本的字符編碼知識。常見的字符編碼有ASCII、
    的頭像 發(fā)表于 01-12 15:11 ?2451次閱讀