Go:指標能優化效能嗎?【譯】
趁著元旦休假+春節,嘗試把2018年期間讓我受益的一些文章、問答,翻譯一下。
歡迎指正、討論,希望對你也有所幫助。
原文連結:Go: Are pointers a performance optimization?
以下,開始正文
過去幾周時間,我回答了許多關於使用指標優化效能的問題。似乎很多人在這方面都感到困惑。這也可以理解,指標確實是個複雜的話題。 希望這篇文章對你有所幫助。
簡而言之:不是使用指標就一定代表著效能優化。
如果要徹底解釋這篇文章涉及的所有細節,那篇幅可能會長到沒人願意看。所以,我精簡了一下,試圖用中等篇幅也能涵蓋想說明的高階概念。
閱讀時需要說明一點:本文討論的是微優化,效能優化都是極其細微的。在進行微優化之前,需先進行基準測試,否則很可能看不到明顯的效果。程式碼易讀性才是第一要義。
什麼是指標?
指標底層代表的就是記憶體地址,指標解引用可以訪問到記憶體儲存的具體資料值。
應用指標之後是如何起到效能優化作用的?
函式呼叫時,變數傳遞實際上是將變數重新複製了一份,傳給函式。多數情況下,指標都要比變數本身佔用更小的空間。
通常,指標大小和系統的架構體系保持一致。32位系統就是32bit,64位系統,即是64bit大小。像bool、float等標量型別,佔用的空間都小於等於指標;而多欄位的符合型別,指標佔的空間更少。
所有,我們的想法就基於複製指標比複製原值更高效。一定程度上,這樣想沒問題。但是效能問題涉及廣泛,除了複製成本之外,還有很多因素要考慮。
指標是否會對效能產生負面影響?
會。主要出於兩方面的考量:
- 解引用雖然耗能很小,但積少成多,不得不慮。
- 通過指標共享的資料,是放在堆上的。堆資料的清理是GC負責的,這也會產生開銷。隨著堆上資料增多,GC的工作量變大,對專案的效能影響也不容忽視。
堆與棧
堆和棧是兩個讓人頭疼的概念,但是我們不得不直面它們。我在這儘量用簡短的篇幅討論完。如果沒能快速理解也沒關係,我曾經也沒能很快理解。
棧:函式區域性空間
每當函式被呼叫,都會分配一塊棧空間來儲存函式區域性變數。函式佔用的棧空間大小在編譯時已經確定。函式返回時,這塊空間就給下一個函式呼叫使用了,也無需立刻清理。雖然這個分配和使用過程要耗費資源,但相對來講,消耗很小。
堆:共享資料空間
如上所述,函式返回後,區域性變數會被銷燬(譯者注:空間被複用或者徹底回收,區域性變數不再存在)。如果返回是非指標變數,返回值會被複制給呼叫者,存在於呼叫者的棧空間中。
但是,如果返回的是指標(譯者注:也就是函式區域性空間的地址),指標指向的資料就要儲存在棧空間之外,這樣才能保證函式返回後,資料仍然可以訪問。這就是堆的用處。
與堆相關的效能問題有這些:
- 堆空間需要從runtime申請,雖然開銷很小,也不能不考慮;
- 如果執行時沒有足夠空間,就需要系統呼叫了,這是額外的開銷;
- 一旦資料佔用了堆空間,就要一直佔用到沒有指標再指向它。這時候,需要GC來清理。GC會找到堆中所有沒有被飲用的值,標記為空閒(譯者注:請參考Go垃圾回收的三色標記)。垃圾越多,GC耗時越大,系統性能越差。
那為什麼還要用指標?
- 指標能修改傳參,提供了一種共享資料的方式。
- 指標能區分零值,確定你的變數是否被賦值了。
總結
指標可以節省複製的開銷,但同時要考慮解引用和垃圾回收帶來的影響。在我看來,效能分析結果顯示覆制是瓶頸之前,不應該考慮把指標作為優化方案。計算機在複製方面的速度可是極快的。
希望這篇文章可以讓你認識到指標是可以派上用場的,但也不要因為要用而用。
預設還是使用值而不是地址吧。除非語法滿足不了你了。