意外的同步池分配

时间:2019-02-22 17:03:29

标签: go benchmarking

所有人,已经学习Golang一个月了,遇到了这个问题。基本上,我正在尝试重用sync.Pool中的[]个字节以进行一些哈希处理。

我添加了一个最小的可行样本,以便在下面进行复制:

type mWriter struct {
    pool *sync.Pool
}

func (m *mWriter) writeSpan(span interface{}) {
    haha := m.pool.Get().([]byte)
    // in real code some actions here instead of simply setting haha[0] = 1
    haha[0] = 1
    m.pool.Put(haha)
}

func NewWriter() *mWriter {
    bytepool := &sync.Pool{
        New: func() interface{} {
            return make([]byte, 16)
        },
    }
    return &mWriter{
        pool: bytepool,
    }
}

func Benchmark_WriteSpan(b *testing.B) {
    c := NewWriter()

    b.ResetTimer()
    b.ReportAllocs()
    for it := 0; it < b.N; it++ {
        c.writeSpan(nil)
    }
}

在我的印象同步下。Pool并没有为[] byte分配新的内存,但是我在这里看到m.pool.get()的额外分配。

  

Benchmark_WriteSpan-12 30000000 47.6 ns / op 32 B / op 1分配/ op   通过

这背后的解释是什么?我还在这里尝试了一些更简单的基准测试:

func Benchmark_2(b *testing.B) {
    // Initializing pool
    pool := &sync.Pool{
        New: func() interface{} {
            return make([]byte, 4)
        },
    }
    b.ResetTimer()
    b.ReportAllocs()
    // Get hold of instance one
    one := pool.Get().([]byte)
    one[1] = 1
    one[2] = 2
    // Submit back the instance after using
    pool.Put(one)
}

但这没有分配:

  

Benchmark_2-12 2000000000 0.00 ns / op 0 B / op 0 allocs / op

在这里感谢任何帮助! (如果这不是使用sync.Pool的方式,有什么建议吗?)

已编辑:

好的,我在基准内向writeSpan添加了一个简单的循环,现在它提供了以下基准:

func Benchmark_WriteSpan(b *testing.B) {
    c := NewWriter()

    b.ResetTimer()
    b.ReportAllocs()
    for it := 0; it < b.N; it++ {
        for i := 0; i < 5; i++ {
            c.writeSpan(nil)
        }
    }
}
  

Benchmark_WriteSpan-12 5000000 226 ns / op 160 B / op 5 allocs / op

好像池在每次写操作中分配32B,难道它不应该在第一次获取后重用相同的byte []吗?

更新 @JimB我在真实代码中确实有一些逻辑,它确实更新了字节片并对其进行了哈希处理。类似于以下内容:

    byteSlice := ds.poolInstance.bytepool.Get().([]byte)
    copy(byteSlice[:len(ds.hashSalt)], ds.hashSalt)
    span.TraceID.MarshalTo(byteSlice[len(ds.hashSalt):])
    hashVal := ds.hashBytes(byteSlice) 
    ds.poolInstance.bytepool.Put(byteSlice)

我不确定这是否可以作为空闲列表的一部分保存为空闲列表,您能对此进行更具体的说明吗?

1 个答案:

答案 0 :(得分:0)

我想我找出了背后的原因。在我的真实代码中,我曾经有:

bytepool: &sync.Pool{
    New: func() interface{} {
        return make([]byte, length)
    },
},
...
...
bytepool.Get().([]byte)

当我将其更改为

bytepool: &sync.Pool{
    New: func() interface{} {
        byteP := make([]byte, length)
        return &byteP
    },
},
...
...
bytepool.Get().(*[]byte)

分配下降到0。因此,对不起人们看起来不是sync.Pool的问题,而是基本的数据结构。不过谢谢您的评论!