【问题标题】:Can Go's sync.pool be used for arrays/slices?Go 的 sync.pool 可以用于数组/切片吗?
【发布时间】:2019-08-16 08:42:44
【问题描述】:

我想知道是否可以将sync.Pool 与数组或切片一起使用?例如,sync.Pool 可以在每秒处理数万个请求时加快以下速度吗?这个例子只是为了更好地理解 Go。

// Handler that uses GenerateArray
func ok(w http.ResponseWriter, r *http.Request) {
    var res [100000]uint64
    fibonacci.GenerateArray(&res)
    fmt.Fprintf(w, "OK")
}

func GenerateArray(data *[100000]uint64) {
    var start uint16 = 1000
    var counter uint32
    for start >= 1 {
        var num = 90
        var n1, n2, temp uint64 = 0, 1, 0

        for num >= 1 {
            temp = n2
            n1, n2 = temp, n1+n2
            data[counter] = n2
            counter++
            num--
        }
        start--
    }
}

编辑:这是 Icza 建议的切片版本。希望我在学习时做对了。

res := make([]uint64, 100000)
fibonacci.GenerateSlice(res)

// PopulateSlice does this...
func PopulateSlice(data []uint64) {
    var start uint16 = 1000
    var counter uint32
    for start >= 1 {
        var num = 90
        var n1, n2, temp uint64 = 0, 1, 0

        for num >= 1 {
            temp = n2
            n1, n2 = temp, n1+n2
            data[counter] = n2
            counter++
            num--
        }
        start--
    }
}

退货。

func GenerateSlice() []uint64 {
    data := make([]uint64, 0, 100000)
    var start uint16 = 1000
    var counter uint32
    for start >= 1 {
        var num = 90
        var n1, n2, temp uint64 = 0, 1, 0

        for num >= 1 {
            temp = n2
            n1, n2 = temp, n1+n2
            // data[counter] = n2
            data = append(data, n2)
            counter++
            num--
        }
        start--
    }
    return data
}

【问题讨论】:

  • 你会看到一个很常见的情况是在 go 中不鼓励使用 arrays,除了几个 very 特定的情况,最好使用切片(最好有明确的上限)。将指针传递给切片/数组也有点奇怪。您的函数称为GenerateArray,但实际上它是填充 一个已经创建的数组。只需创建 && 返回
  • @EliasVanOotegem 我忘了重命名它。最初它生成一个数组并返回它。我从切片开始,但性能非常糟糕。通过指向数组的指针,我能够将性能提高一倍以上。
  • 您只是使用了切片文字,还是指定了上限/长度?
  • data := make([]uint64, 100000) cap/len 带索引和data := make([]uint64, 0, 100000) 带附加
  • 第一个是指定长度,用 C 术语来说,相当于uint64 *data = calloc(100000, sizeof *data);(在堆上分配,并将内存初始化为零。第二个分配一块内存(乐观地),每个append 将初始化一个给定值的新偏移量。这只是粗略的笔划,但本质上就是这样。使用数组在堆栈上分配(如果可能的话),因此开销会少一些,但你正在传递一个指针,它仍然引入了一个间接级别。顺便说一句,在最后一个 sn-p 中,counter var 是多余的

标签: arrays go slice pool


【解决方案1】:

我想知道是否可以将sync.Pool 与数组或切片一起使用?

是的,sync.Pool 可以与任何 Go 值一起使用。

一个用例,引自其文档:

一个很好地使用池的例子是在 fmt 包中,它维护一个动态大小的临时输出缓冲区存储。存储在负载下扩展(当许多 goroutine 正在积极打印时)并在静止时缩小。

但是,您的情况很可能不会带来性能提升。数组是“值”,所以当你传递一个数组时,它的值(它的所有元素)都会被复制。因此,如果您将一个数组放入池中,它将被复制。当您从池中获取一个数组时,它将再次被复制。这不是改进,而是损害。

另一方面,切片是“漂亮”的标题,指向一个后备数组。所以传递/获取切片意味着只是传递/获取这个标题。使用切片,您很可能会体验到性能提升。基准测试!

查看相关:Are golang slices passed by value?

【讨论】:

  • 感谢您的回答。我更新了原始问题中的代码以传递切片。我尝试将切片传递给函数,以及从函数内部生成切片并返回它。我们可能在这里跑题了,但是在函数中生成切片或将其传入,效率比数组指针示例低 68%。我的测试服务器只处理切片的 21k r/s 和数组指针的 49k r/s。
猜你喜欢
  • 2020-10-02
  • 2012-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多