2015-09-03 73 views
2

現在我正在使用下面的代碼(如BenchmarkEncoder()),它很快,但我想知道是否有更快,更有效的方式。我與GOMAXPROCS=1連接幾個[]字節的最快方式是什麼?

sudo -E nice -n -20 go test -bench . -benchmem -benchtime 3s 

基準。

package blackbird 

import (
    "testing" 
    "encoding/hex" 
    "log" 
    "bytes" 
    "encoding/json" 
) 

var (
    d1, d2, d3, d4, outBytes []byte 
    toEncode [][]byte 
) 

func init() { 
    var err interface{} 
    d1, err = hex.DecodeString("6e5438fd9c3748868147d7a4f6d355dd") 
    d2, err = hex.DecodeString("0740e2dfa4b049f2beeb29cc304bdb5f") 
    d3, err = hex.DecodeString("ab6743272358467caff7d94c3cc58e8c") 
    d4, err = hex.DecodeString("7411c080762a47f49e5183af12d87330e6d0df7dd63a44808db4e250cdea0a36182fce4a309842e49f4202eb90184dd5b621d67db4a04940a29e981a5aea59be") 
    if err != nil { 
     log.Fatal("hex decoding failed: %v", err) 
    } 
    toEncode = [][]byte{d1, d2, d3, d4} 

} 

func Encode(stuff [][]byte) []byte { 
    return bytes.Join(stuff, nil) 
} 

func BenchmarkEncoderDirect(b *testing.B) { 
    for i := 0; i < b.N; i++ { 
     bytes.Join(toEncode, nil) 
    } 
} 

func BenchmarkEncoder(b *testing.B) { 
    for i := 0; i < b.N; i++ { 
     Encode(toEncode) 
    } 
} 

func BenchmarkJsonEncoder(b *testing.B) { 
    for i := 0; i < b.N; i++ { 
     outBytes, _ = json.Marshal(toEncode) 

    } 
} 

什麼是連接幾個[]byte在一起的最快方式?

+0

需要解碼的數據的大小是否可預測? – thwd

+0

d1,d2,d3的長度/大小始終爲16個字節(它們是UUID)。 d4可以是16字節到1兆字節之間的任何地方。 – Matrix

+0

嘗試寫入一個容量初始化爲所需長度的bytes.Buffer,這樣可避免進一步複製以生成基礎片。 – elithrar

回答

5

bytes.Join()非常快,但它會在可追加的字節片段之間附加分隔符做一些額外的工作。即使分隔符是空的或nil切片也是如此。因此,如果您關心最佳性能(儘管它會有輕微的改進),您可以在不添加(空)分隔符的情況下執行以下操作:分配一個足夠大的字節片,並將每個片複製到結果中使用內置的copy()功能。

嘗試它的Go Playground

func Join(s ...[]byte) []byte { 
    n := 0 
    for _, v := range s { 
     n += len(v) 
    } 

    b, i := make([]byte, n), 0 
    for _, v := range s { 
     i += copy(b[i:], v) 
    } 
    return b 
} 

使用它:

concatenated := Join(d1, d2, d3, d4) 

改進:

如果你事先知道總規模(或者你可以計算出它比循環切片快),提供它,你可以避免不得不遍歷切片,以計算所需要的尺寸:

func JoinSize(size int, s ...[]byte) []byte { 
    b, i := make([]byte, size), 0 
    for _, v := range s { 
     i += copy(b[i:], v) 
    } 
    return b 
} 

在您的情況使用它:

concatenated := JoinSize(48 + len(d4), d1, d2, d3, d4) 

注:

但是,如果最終你的目標是要將連接的字節片段寫入io.Writer,性能上最好不要連接它們,而是分別將它們寫入它們中。

+0

我剛剛對你的兩條建議進行了基準測試,結果確實比我現在的要快。 JoinSize()是迄今爲止最快的功能。它的速度是101 ns/op,而117 ns/op。 – Matrix

+1

想想你能用額外8ns做的所有事情!/s;) – elithrar

+1

@elithrar它是16 ns!執行它1.8個檯球時間('1.8e12'),現在你有一個額外的假期下班! – icza

2

一般來說,@ icza的答案是正確的。爲了您的具體使用情況,但是,你可以分配一次,並更有效地解碼到該緩衝區:

Like this:

package main 

import (
    "encoding/hex" 
) 

func main() { 
    h1 := []byte("6e5438fd9c3748868147d7a4f6d355dd") 
    h2 := []byte("0740e2dfa4b049f2beeb29cc304bdb5f") 
    h3 := []byte("ab6743272358467caff7d94c3cc58e8c") 
    h4 := []byte("7411c080762a47f49e5183af12d87330e6d0df7dd63a44808db4e250cdea0a36182fce4a309842e49f4202eb90184dd5b621d67db4a04940a29e981a5aea59be") 

    tg := make([]byte, 16+16+16+(1024*1024)) // allocate enough space for the 3 IDs and a max 1MB of extra data 

    hex.Decode(tg[:16], h1) 
    hex.Decode(tg[16:32], h2) 
    hex.Decode(tg[32:48], h3) 
    l, _ := hex.Decode(tg[48:], h4) 

    tg = tg[:48+l] 
} 

在代碼的結束,tg持有3點的ID加上可變長度第四塊數據,解碼,連續。

+0

我在測試中使用了十六進制編碼的字節作爲數據樣本。我實際上並沒有在實際代碼中使用hex.DecodeString(),這就是爲什麼它們不是基準函數的一部分。 – Matrix

+0

這個想法很好,如果我們可以指定我們想要結果的目標切片(就像你用'hex.Decode()'做的那樣)。但是請注意,即使是這樣,如果'h4'很小(例如只是幾個字節),這個解決方案可能效率非常低,因爲它分配了一個〜1MB的slice並且將使用50個字節(結果)。還要注意的是,即使你在最後重做它,只要切片將會保留在整個1 MB數組中。如果'h4'很大,那麼這是一個很好的優化。 – icza

+0

他們要求**最快**的方法。 – thwd

相關問題