這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Dave Cheney 寫了一篇文章 Five things that make Go fast
從 values, inline function, Escape analysis,Groutine,Segment And Copyings Stack四個方面介紹了go中的go高效能的一些知識.首先需要知道幾個概念inline function 內嵌函式 本文會帶一些相關講解具體詳情還請Google變數Go: var gocon int32 = 1024 佔用四個位元組Python: gocon=2014 getsizeof(2014) ---> 24個位元組Java : int gocon = 2014 雖然是4個位元組但是如果使用到List<Integer>時 Integer在32OS上16byte64OS上24byte去計算這樣的開銷有意義嗎 文中指出是有意義的 --具體請看連結這裡只做筆記CPU之所以發展迅猛更多的是依賴與緩衝 那麼如果緩衝的有用資料能更多那麼緩衝的效能也就能隨之提高緩衝的效能提高將會帶來更佳的程式效能通常動態類型的語言 數組中的所有元素類型各不相同 需要被單獨的存放到堆中 而不是一個連續的儲存數組這就使CPU緩衝無用武之地 因為CPU緩衝會把一片連續的記憶體空間讀入 而這種分散在不同的記憶體位址中的資料緩衝幫不上忙就CPU只能去讀取記憶體 緩衝讀取一個資料實在3個CPU刻度 而從記憶體讀取一個資料則需要100CPU刻度所以程式效能降低也就是理所應當的了關於CPU緩衝方面的文章 這裡給出一個連結 作為參考
內嵌函式要知道什麼是內嵌函式首先要知道函數調用是有開銷的比如A()中調用了B()在調用B函數時又有開銷--開銷大致可分兩部分 傳遞參數的開銷和儲存當前程式上下文資訊所花費的開銷。對於傳遞參數的開銷而言,傳遞的參數越多開銷就越大;對於儲存當前程式上下文所花費的開銷而言,函數越複雜需要花費的開銷就越大。如果一個很簡單的函數其函數功能的開銷甚至比函數調用的開銷還要來的小的多,那就極其的不划算了。而內嵌函式的目的就在於在編譯器會將一些功能極其簡單的被調用函數代碼內嵌到調用函數中。如下代碼所示
func Max(a, b int) int {if a>b {return a}return b}func Double(a, b int) int {return Max(a,b)}因為Max函數功能極其簡單其功能的消耗遠小於函數調用的消耗所以就會把Max函數內嵌到Double函數中而編譯器會將上述代碼轉換為如下func Double(a, b int) int {temp := bif a>b {temp = a}return 2 * temp}可能有人會有疑問把所有的代碼都內嵌不就好了--但這其實是有問題的在 five things thar make go fast 中有一段話講述了原因 原文如下
The Go compiler inlines a function by treating the body of the function as if it were part of the caller.
Inlining has a cost; it increases binary size.
It only makes sense to inline when the overhead of calling a function is large relative to the work the function does, so only simple functions are candidates for inlining.
Complicated functions are usually not dominated by the overhead of calling them and are therefore not inlined.
大致的意思就是說Go的編譯器通過函數的主體內聯一個函數就好像這個函數就是調用者的一部分內嵌函式也是有開銷的 他會增長二進位碼的大小調用一個函數的開銷遠大於該函數所做的工作時內聯才有意義所以只有那些功能簡單的函數才會被作為內斂的候選一個複雜的函數函數的開銷對其來說並不佔據主導因此也就沒必要內聯這種負責的函數
總結上面的四段話就是如果複雜的函數也內聯那麼二進位碼增加佔用的記憶體大小也就更大了並且這種內聯毫無意義因為相對於複雜的函數這點函數調用的開銷基本可以忽略不計而對於Go 文中也做了介紹The Go compiler can automatically inline functions across files and even across packages. This includes code that calls inlinable functions from the standard library.
Go的編譯器會自動的通過檔案和包內斂那些簡單的函數 這些函數包括標準庫中的函數
Escape analysis (逃逸分析)
關於逃逸分析的文章請戳這篇連結
什麼是逃逸分析
(Escape Analysis)
逃逸分析的最佳化原理:編譯器在編譯期間隊未逃逸的對象進行標記運行期間並不是把對象扔到堆裡而是直接扔到棧裡這樣當線程執行結束後棧的空間就被回收 站內的局部變數也被回收了 這遠比GC來回收要有效率的多。並且棧分配頁遠比堆分配要來的快得多經過逃逸分析最佳化的程式效能必然提高不少
原文中的一段描述如下Because escape analysis is performed at compile time, not run time, stack allocation will always be faster than heap allocation, no matter how efficient your garbage collector is.
Goroutine 這種從語言層面就支援並發的語言確實讓人很歡喜 這比Java的線程池還要來的高端線程池也就只是控制線程而Go不但控制線程還具體到控制協程之間的合作文中提到的幾點先記錄在此
Places where Goroutines may yield to others are:
- Channel send and receive operations, if those operations would block.
- The Go statement, although there is no guarantee that new goroutine will be scheduled immediately.
- Blocking syscalls like file and network operations.
- After being stopped for a garbage collection cycle.
- 可能產生 協程的地方有以下幾種情況
- 管道發送接收資料時如果操作是阻塞的就會有協程的產生
- 使用go關鍵字的地方 並不能保證立即執行
- 阻塞調用時 比如 檔案操作和網路操作
- 停止了記憶體回收之後
Segment And Copyings Stack 還沒看完 以後再補上