我试图使用sync.Pool重用[]byte。但事实证明它比单纯的制造要慢得多。代码:
package main
import (
"sync"
"testing"
)
func BenchmarkMakeStack(b *testing.B) {
for N := 0; N < b.N; N++ {
obj := make([]byte, 1024)
_ = obj
}
}
var bytePool = sync.Pool{
New: func() interface{} {
b := make([]byte, 1024)
return &b
},
}
func BenchmarkBytePool(b *testing.B) {
for N := 0; N < b.N; N++ {
obj := bytePool.Get().(*[]byte)
_ = obj
bytePool.Put(obj)
}
}结果:
$ go test pool_test.go -bench=. -benchmem
BenchmarkMakeStack-4 2000000000 0.29 ns/op 0 B/op 0 allocs/op
BenchmarkBytePool-4 100000000 17.2 ns/op 0 B/op 0 allocs/op根据Go文档,sync.Pool应该更快,但我的测试显示并非如此。有人能帮我解释一下吗?
更新: 1.使用go基准测试更新有问题的代码。2. 堆栈和堆中的答案,参见peterSO的答案。
发布于 2018-12-04 13:42:39
基准第一定律:无意义的微基准产生无意义的结果。
你不切实际的微基准是没有意义的。
包同步 导入“同步” 类型池 池是一组临时对象,可以单独保存和检索。 存储在池中的任何项目可以在任何时候自动删除,无需通知。如果在发生这种情况时池保存了唯一的引用,则项目可能会被解除分配。 一个池是安全的,供多个峡谷同时使用。 池的目的是缓存已分配但未使用的项以供以后重用,从而减轻垃圾收集器的压力。也就是说,它使得构建高效的、线程安全的自由列表变得更加容易。然而,它并不适合所有的自由列表。 池的适当使用是管理一组临时项,这些临时项在包的并发独立客户端之间默默地共享,并可能被重用。池提供了一种方法来摊销许多客户端的分配开销。 一个很好地使用池的例子是fmt包,它维护一个动态大小的临时输出缓冲区存储。商店的规模在负荷下(当许多猩猩正在积极印刷),当静止时缩小。 另一方面,作为短期对象的一部分维护的空闲列表并不适合池的使用,因为在这种情况下,开销并不能很好地摊销。让这样的对象实现自己的自由列表更有效。
sync.Pool是否适合您的用例?sync.Pool是否适合您的基准测试?用例和基准测试是否相同?您的用例是一个微基准吗?
使用Go testing包进行人工基准测试,为make堆栈和堆分配提供单独的基准测试,make比sync.Pool更快也更慢。
输出:
$ go test pool_test.go -bench=. -benchmem
BenchmarkMakeStack-4 2000000000 0.29 ns/op 0 B/op 0 allocs/op
BenchmarkMakeHeap-4 10000000 136 ns/op 1024 B/op 1 allocs/op
BenchmarkBytePool-4 100000000 17.2 ns/op 0 B/op 0 allocs/op
$pool_test.go
package main
import (
"sync"
"testing"
)
func BenchmarkMakeStack(b *testing.B) {
for N := 0; N < b.N; N++ {
obj := make([]byte, 1024)
_ = obj
}
}
var obj []byte
func BenchmarkMakeHeap(b *testing.B) {
for N := 0; N < b.N; N++ {
obj = make([]byte, 1024)
_ = obj
}
}
var bytePool = sync.Pool{
New: func() interface{} {
b := make([]byte, 1024)
return &b
},
}
func BenchmarkBytePool(b *testing.B) {
for N := 0; N < b.N; N++ {
obj := bytePool.Get().(*[]byte)
_ = obj
bytePool.Put(obj)
}
}https://stackoverflow.com/questions/53613556
复制相似问题