Go語言高效的5個特性

原文地址:https://www.jianshu.com/p/d70e6f6b13c8程序員

本文只是對原文的簡單總結,查看詳細信息請移步原文算法


前言

爲何選擇GO語言

最多見的回答:緩存

  • Concurrency (併發)
  • Ease of deployment (部署容易)
  • Performance (性能)

下面咱們來討論下對Go語言性能有所幫助的5個特性數據結構


1. 變量的處理和存儲

  • 存儲:以 int32 爲例,Go中佔4字節,Python中佔24字節,Java雖然int類型也佔用4字節,可是在List或者Map這樣的集合裏存儲int的話,編譯器會把它轉換成Integer對象(32位JVM上佔16字節,64位JVM上佔24字節)
  • Go語言讓你定義很是緊湊的數據結構,避免了無謂的指針跳轉。緊湊的數據結構可以使緩存更加有效。高效的緩存最終給咱們帶來了更高的效率。

2. 函數調用不是免費的

函數調用的三個步驟:併發

  • 建立一個新的堆棧框(stack frame)並把調用者的詳細信息記錄下來。
  • 把任何會被被調用函數用到的寄存器內容保存到堆棧。
  • 計算被調用函數的地址,並執行跳轉指令到那個新的地址。

內聯(inlining): Go語言的實現很是簡單。當一個包(package)被編譯的時候,任何適合內聯的小函數都被標記而且按正常狀況編譯。而後將源代碼和編譯後的二進制同時保存下來。 Go編譯器可以自動在多個文件或者包(package)之間實現函數內聯。若是某些代碼調用了來自標準庫的可內聯函數,Go編譯器一樣能夠將這些函數內聯進來。函數


3. 垃圾回收機制(Garbage Collection)

  • 分配在堆(Heap)上的須要垃圾回收,分配在棧(Stack)上的不須要垃圾回收;性能

  • 不管垃圾回收機制多麼高效,棧的分配老是比堆的分配要快;操作系統

  • 逃逸分析(Escape Analysis)技術能自動判斷是否須要在堆(Heap)上分配空間,若是不須要的話就分配在棧(Stack)上,即便是使用new,make等建立的線程

  • 三色標記算法:go 1.5 版本開始採用 「非分代的、非移動的、併發的、三色的標記清除垃圾收集器」指針


4. Goroutines

開銷大小:進程 > 線程(共享內存) > 協程

每一個Go進程只須要少許的操做系統線程。Go的運行環境來負責將可運行的goroutine分配到空閒的操做系統線程上


5. Goroutine的棧管理

  • 保護頁機制:Go語言沒有使用保護頁機制。Go的編譯器會在每一個函數調用的時候插入一段代碼來檢查是否有足夠的棧空間來運行被調用的函數。若是空間不足,Go的運行環境就會分配更多的棧空間。由於有了這個檢查機制,一個goroutine的初始棧能夠很小。這樣Go程序員就能夠把goroutine做爲相對廉價的資源來使用。

  • 熱分裂問題(hot split problem):當G調用H的時候,沒有足夠的棧空間來讓H運行,這時候Go運行環境就會從堆裏分配一個新的棧內存塊去讓H運行。在H返回到G以前,新分配的內存塊被釋放回堆。這種管理棧的方法通常都工做得很好。但對有些代碼,特別是遞歸調用,它會形成程序不停地分配和釋放新的內存空間。舉個例子,在一個程序裏,函數G會在一個循環裏調用不少次H函數。每次調用都會分配一塊新的內存空間。這就是熱分裂問題(hot split problem)。

  • 解決方法:Go 1.3採用了新的棧管理方法。若是goroutine的棧過小了,它會去分配一塊新的更大的棧,而不是分配和釋法額外的內存空間。老的棧裏的內容被複制到新的棧裏,goroutine會在新的棧上繼續執行。在第一次調用H函數以後,將會有足夠大的棧空間,這樣之後的棧空間大小檢查都不會有問題了。這就解決了熱分裂問題。


總結:

這些特性個個都頗有效,他們之間還相互依賴。好比,若是沒有了可衍生的棧,運行環境將多個goroutine複用到線程上面就不會頗有效。內聯在把多個小函數合併成大函數的時候也避免了棧大小的檢查開銷。逃逸分析用棧代替堆來存儲局部變量,這樣也減小來垃圾回收機制的壓力。逃逸分析還提高了緩存的性能(cache locality)。沒有可衍生的棧,逃逸分析又會對棧形成很大的壓力。

相關文章
相關標籤/搜索