在go生態已經有很多web框架,但感覺沒有乙個符合我們的想法,我們想要乙個簡潔高效
的核心框架,提供路由
,context
,中介軟體
和依賴注入
,而且拒絕使用正則
和反射
,於是我們開始構建baa框架。一開始使用最簡單的通俗寫法實現了第一版的功能,基本可用,但是效能爛到爆,優化之路漫漫開啟。
最好的文章應該是每一步都加上優化前後的benchmark對比結果,給讀者以最直觀的感受。我先bs一下自己,因為我懶了,沒有再回頭一步步去對比這個結果圖。這是我們做這個框架時的乙個基本原則,整個實現中沒有使用過regexp、reflect包。這是我們對效能追求的基礎。帶來的另乙個收益是,沒有魔法,都是非常容易理解的實現,讓整個框架變得簡單。
在我上次翻譯的文章cockroachdb gc優化總結中介紹過這些方法,在《go語言聖經》中作者也介紹了這個方法,使用 sync.pool 可以在一次gc之間重用物件,避免物件的頻繁建立和記憶體分配。我們在追求效能的過程中,要盡可能減少甚至達到記憶體零分配,這是乙個最重要的用法。
在baa中有如下**片段:
b.pool = sync.pool ,
}
使用的時候:
c := b.pool.get().(*context)
c.reset(w, r)
使用完:
b.pool.put(c)
slice的本質就是就是乙個可變長度的array,根據儲存的容量會動態的重新分配記憶體遷移資料。如果長度不斷變化,會導致不斷的重新分配記憶體,在特定場景下,如果我們可以使用乙個定長的array來優化記憶體分配。
var namearr [1024]string
pnames := namearr[0:0]
pnames 是乙個slice,但資料操作總是在array namearr上完成,在整個使用過程中不會重新分配記憶體。
上面的偽**,在baa中已經不存在了,baa改用了下面的技巧來取代定長的array。slice的重用,其實和上面的利用array優化基本一致,就是初始分配乙個較大的容量,盡可能在使用的過程中都不會超出容量,當然也不用擔心,萬一不夠用了,會自動擴容,只不過會進行一次記憶體分配。
在baa中有如下**片段:
func (c *context) reset(w http.responsewriter, r *http.request)注意newcontext中的 c.pnames和c.pvalues 以及 reset中的 c.pnames和c.pvalues,通過 slice[:0] 來重用之前的slice,避免記憶體重新分配。至於上面的長度32,是根據經驗得來的乙個值,盡可能保證長度滿足大部分情況下的需求又不太大。
在第一版中,路由就是乙個map,路由匹配就是乙個range,簡單,清晰,但效能自然不好。參考了macaron
和echo
框架的設計,都是使用基數樹(radix tree)
來實現的,只是實現的細節不同,這裡我們也有不同的細節實現,但思路基本沒變。具體實現可以參考 wiki,和 baa router部分 router.go
很多文章介紹過了,盡量使用 byte 替代 string,這裡我們也是這麼做的。
map和slice的range效能差乙個數量級啊,所以,你會發現我們取消了大量的map改為了slice,在slice也能重用
這一節的**示例中 pnames和pvalues就是用來取代原來的 map[string]string,因為map range的效率太低了。
slice的迭代是很快,可是總還是迭代,是迭代就有開銷,為了追求極致的效能也是瘋了。在路由匹配時,我們給所有的路由pattern設定了單位元組的index,如果首字母都不匹配,就沒有必要繼續後面的字元匹配了。
路由條目建立:
// newroute create a route item
func newroute(pattern string, handles handlerfunc, router *router) *route
路由條目匹配:
// findchild find child static route
func (r *route) findchild(b byte) *route
}return nil
}
注意r.alpha
就是用來盡可能避免迭代進一步提高效能的。
離目標越來越近,但還有一點差距,我們也越來越瘋狂,最後居然乾成了這樣,我們把部分頻繁呼叫的函式取消,改為直接在乙個函式中完成,因為我們發現,即使只是乙個函式呼叫,***也是開銷呀。
在整個過程中,如何一步步分析效能問題,定位可優化的地方,go test -cpuprofile, go test -memprofile, go test -bench 就是最好的工具,每修改一次,bench看結果,profile看效能分析。
Go語言效能優化 For Range 效能研究
如果我們要遍歷某個陣列,map集合,slice切片等,go語言 golang 為我們提供了比較好用的for range方式。range是乙個關鍵字,表示範圍,和for配合使用可以迭代陣列,map等集合。它的用法簡潔,而且map channel等也都是用for range的方式,所以在編碼中我們使用f...
程式語言效能實測,Go 和 Python誰更牛?
網際網路上有非常多的精彩 它們成了構建各種基礎設施的基礎。你正在閱讀的這個平台同樣也在致力於建立出色的 儘管普通使用者一般不會注意到這一點,但另一方面,優秀的開發人員始終在尋求使他們的應用程式 以及他們的 有更好的方法。編寫出色 的基礎取決於開發人員的技能及其選擇的語言。這不可避免地導致開發人員之間...
總結boss中對go語言開發的要求
總結boss中對go語言開發的要求 1,go基礎知識,web,後端伺服器開發 2,熟悉網路程式設計,執行緒 協程技術,高併發伺服器程式開發。3,熟悉web程式設計,http tcp等協議 4,熟悉mysql,redis,mongodbdeng 5,熟悉docker容器技術。6,熟悉http,rest...