2015-09-03 42 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

相关问题