Go sync.pool是否比make慢得多?

时间:2018-12-04 12:58:53

标签: go

我尝试使用 upstream docker-wallabag { server wallabag:80; } upstream topic { server topic:1000; } server { listen 80; server_name stage.leaves.anant.us; location / { proxy_pass http://topic/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /admin/ { proxy_pass http://admin.stage.leaves.anant.us/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } server { listen 80; server_name admin.stage.leaves.anant.us; location / { proxy_pass http://docker-wallabag/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }} 重用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文档,$ 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 应该会更快,但是我的测试却显示出其他情况。有人可以帮我解释一下吗?

更新: 1.通过使用基准测试更新有问题的代码。 2.答案位于 stack heap 中,请参阅peterSO的答案。

1 个答案:

答案 0 :(得分:7)

基准第一定律:毫无意义的微基准产生了毫无意义的结果。

您不切实际的微基准测试没有意义。


  

Package sync

import "sync"
     

type Pool

     

池是一组临时对象,可以单独保存   并取回。

     

存储在池中的任何项目都可以随时自动删除   恕不另行通知。如果池在此时拥有唯一参考   发生,该项目可能会被释放。

     

一个池可以安全地同时被多个goroutine使用。

     

Pool的用途是缓存已分配但未使用的项目,以供以后重用,   减轻垃圾收集器的压力。也就是说,这很容易   建立有效的,线程安全的空闲列表。但是,不是   适用于所有免费列表。

     

池的适当用途是管理一组临时项目   在并发的独立服务器之间静默共享并有可能重用   包裹的客户。池提供了摊销分配的方法   许多客户的开销。

     

在fmt软件包中可以很好地使用Pool的示例   维护动态大小的临时输出缓冲区存储。的   在负载下存储秤(当许多goroutine正在打印时)   并在静止时收缩。

     

另一方面,作为短命的一部分维护的空闲列表   对象不适合作为Pool使用,因为开销不会   在这种情况下可以摊销。拥有这样更有效   对象实现自己的空闲列表。

sync.Pool是否适合您的用例? sync.Pool是否适合您的基准测试?您的用例和基准是否相同?您的用例是微基准吗?


使用Go testing软件包作为人工基准测试,并为make堆栈和堆分配使用单独的基准测试,makesync.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)
    }
}