golang []byte和string的高性能轉換
golang []byte和string的高性能轉換
在fasthttp的最佳實踐中有這麼一句話:
Avoid conversion between
[]byte
andstring
, since this may result in memory allocation+copy. Fasthttp API provides functions for both[]byte
andstring
– use these functions instead of converting manually between[]byte
andstring
. There are some exceptions – see this wiki page for more details.
大概意思就是說,要盡量避免[]byte
和string
的轉換,因為轉換過程會存在記憶體拷貝,影響性能。此外在fasthttp中還提出了一個解決方案,用於[]byte
和string
的高性能轉換。直接看下源碼:
// b2s converts byte slice to a string without memory allocation.
// See //groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
//
// Note it may break if string and/or slice header will change
// in the future go versions.
func b2s(b []byte) string {
/* #nosec G103 */
return *(*string)(unsafe.Pointer(&b))
}
// s2b converts string to a byte slice without memory allocation.
//
// Note it may break if string and/or slice header will change
// in the future go versions.
func s2b(s string) (b []byte) {
/* #nosec G103 */
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
/* #nosec G103 */
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
bh.Data = sh.Data
bh.Cap = sh.Len
bh.Len = sh.Len
return b
}
可以看到上述實現中並沒有記憶體拷貝,使用類似C語言的類型強轉實現[]byte
和string
之間的類型轉換。那麼他們和一般使用的轉換之間的性能差異有多大?看下如下性能測試:
var s = "adsfasdfadsfadsfasdfadfadfasdfasdfadsfasdfasdfasdfsadfas"
func BenchmarkB2sNew(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = s2b(s)
}
}
func BenchmarkB2sNormal(b *testing.B) {
var _ []byte
for i := 0; i < b.N; i++ {
_ = []byte(s)
}
}
var bt = []byte("adsfasdfadsfadsfasdfadfadfasdfasdfadsfasdfasdfasdfsadfas")
func BenchmarkS2BNew(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = b2s(bt)
}
}
func BenchmarkS2BNormal(b *testing.B) {
var _ []byte
for i := 0; i < b.N; i++ {
_ = string(bt)
}
}
測試結果如下:
goos: windows
goarch: amd64
pkg: awesomeProject5/lib
cpu: Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz
BenchmarkB2sNew
BenchmarkB2sNew-8 1000000000 0.2401 ns/op
BenchmarkB2sNormal
BenchmarkB2sNormal-8 40813272 27.49 ns/op
BenchmarkS2BNew
BenchmarkS2BNew-8 1000000000 0.2393 ns/op
BenchmarkS2BNormal
BenchmarkS2BNormal-8 49520067 22.56 ns/op
PASS
可以看到在相同測試條件下,其性能差異竟然達到了100倍!
可見在高頻網路訪問中,如果直接在[]byte
和string
之間進行轉換將會花費多大的性能!
需要注意的是這種方式也有弊端,在程式碼注釋中可以看到它依賴golang中的string或slice的首部定義。如果後續golang版本對此有修改,則有可能導致程式碼無法運行。