go - Go sync.pool 比 make 慢很多吗?
问题描述
我尝试使用sync.Pool
来重用[]byte
. 但事实证明它比 make 慢。代码:
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 benchmark 更新有问题的代码。2.答案放在堆栈和堆中,请参阅peterSO的答案。
解决方案
基准第一定律:无意义的微基准会产生无意义的结果。
你不切实际的微基准没有意义。
import "sync"
池是一组可以单独保存和检索的临时对象。
存储在池中的任何项目都可能随时自动删除,恕不另行通知。如果在发生这种情况时 Pool 拥有唯一的引用,则该项目可能会被释放。
一个 Pool 可以安全地同时被多个 goroutine 使用。
Pool 的目的是缓存已分配但未使用的项目以供以后重用,减轻垃圾收集器的压力。也就是说,它使构建高效、线程安全的空闲列表变得容易。但是,它并不适用于所有空闲列表。
Pool 的适当用途是管理一组在包的并发独立客户端之间静默共享并可能被重用的临时项目。Pool 提供了一种在许多客户端之间分摊分配开销的方法。
一个很好地使用池的例子是在 fmt 包中,它维护一个动态大小的临时输出缓冲区存储。存储在负载下扩展(当许多 goroutine 正在积极打印时)并在静止时缩小。
另一方面,作为短期对象的一部分维护的空闲列表不适合用于池,因为在这种情况下开销不能很好地摊销。让这些对象实现它们自己的空闲列表更有效。
sync.Pool
适合您的用例吗?sync.Pool
适合您的基准吗?您的用例和基准测试是否相同?您的用例是微基准测试吗?
使用 Gotesting
包进行人工基准测试,对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)
}
}
推荐阅读
- typescript - 如何为接口创建索引签名
- android - Android Studio 数据包解析问题 .apk
- javascript - requestFullscreen 后等待回流结束
- python - 循环遍历 2 个字典列表
- c# - AutoFixure Fixture.Build().With() 具有不同的值
- python - 在 2.0 的会话中迭代 tf.data.Dataset 的正确方法
- redis - 如何扫描自上次 SCAN 以来其值已更新的键
- javascript - 为 Alexa APL 智能显示设备设置背景颜色
- azure - 无法撤销 Azure 应用程序的用户权限
- html - Objective-C 中的 HTML 字符串