咱們觀察到從文件讀取到go對象,須要兩次拷貝:git
怎麼樣優化這個讀取速度呢?github
所謂」展開「就是一個reinterpret cast,對一個指針的類型從新解讀。json
var bytes = []byte{ 16, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 'h', 'e', 'l', 'l', 'o'}
假設有這樣一個[]byte數組。這個是直接用mmap讀取出來的。數組
var ptr = &bytes[0]
這個ptr就是這片內存區域的指針,指向了開頭的第一個元素緩存
type stringHeader struct { Data uintptr Len int } header := (*stringHeader)(unsafe.Pointer(ptr))
這樣咱們就把這個內存從新解讀爲了一個stringHeader了。利用stringHeader就能夠構造出string來。優化
header.Data = uintptr(unsafe.Pointer(&bytes[16]))
把stringHeader的指針指向實際的hello數據部分。ui
str := (*string)(unsafe.Pointer(ptr)) fmt.Println(str) // "hello"
最後再把同一片內存區域解讀爲string類型,就獲得了"hello"字符串了。整個解碼過程只作了一次header.Data的更新,沒有作任何內存分配。設計
相比Java來講,go容許咱們使用go本身的heap外的內存。甚至容許把go的對象直接在這片內存上構造出來。這使得咱們的應用能夠和文件系統的緩存共享一片內存,達到內存利用率的最大化。同時相比protobuf/thrift來講,gocodec就是把cpu對值的內存表示(little endian的integer等),以及go語言對象的內存表示(stringHeader,sliceHeader)直接拷貝了,減小了編解碼的計算成本。指針
完整的代碼,歡迎star:bloomfilter_test.gocode
設計了一個編解碼格式叫 github.com/esdb/gocodec
和protobuf的對比尚未測,和json相比,毫無懸念地不在一個量級上。
gocodec 200000 10893 ns/op 288 B/op 2 allocs/opjson 300 3746169 ns/op 910434 B/op 27 allocs/op