如果我知道许多tmp切片的最大大小,那么在创建它们时是否应该设置容量?

时间:2019-03-03 02:27:58

标签: go

如果我需要在一个函数中使用tmp slice,并且该函数将被调用多次,它们的最大容量将不会超过10。但是它们的长度是变化的。举例来说,也许其中80%的尺寸为1。10%的尺寸为3,而10%的尺寸为10。

我可以想到一个示例函数,如下所示:

func getDataFromDb(s []string) []string {
    tmpSlice := make([]string, 0, 10)
    for _, v := range s {
        if check(v) {
            tmpSlice = append(tmpSlice, v)
        }
    }
    ......
    return searchDb(tmpSlice)
}

所以我应该做var tmpSlice []stringtmpSlice := make([]string, 0, 0)tmpSlice := make([]string, 0, 5)tmpSlice := make([]string, 0, 10)吗?或其他建议?

3 个答案:

答案 0 :(得分:3)

最快的情况是代码没有在堆上分配。

创建在堆栈上分配且不进行转义的变量(按值传递变量,否则它们将转义)。

通过在建筑物上添加-gcflags "-m -l"可以进行转义。

这是一个示例,显示如果我们用数组替换slice并按值传递它,那么将得到快速分配的代码,无需分配(在堆上)。

package main

import "testing"

func BenchmarkAllocation(b *testing.B) {
    b.Run("Slice", func(b2 *testing.B) {
        for i := 0; i < b2.N; i++ {
            _ = getDataFromDbSlice([]string{"one", "two"})
        }
    })
    b.Run("Array", func(b2 *testing.B) {
        for i := 0; i < b2.N; i++ {
            _ = getDataFromDbArray([]string{"one", "two"})
        }
    })
}

type DbQuery [10]string
type DbQueryResult [10]string

func getDataFromDbArray(s []string) DbQueryResult {
    q := DbQuery{}
    return processQueryArray(q)
}

func processQueryArray(q DbQuery) DbQueryResult {
    return (DbQueryResult)(q)
}

func getDataFromDbSlice(s []string) []string {
    tmpArray := make([]string, 0, 10)
    return processQuerySlice(tmpArray)
}

func processQuerySlice(q []string) []string {
    return q
}

使用benchmem运行基准测试可以得出以下结果:

BenchmarkAllocation/Slice-6             30000000            51.8 ns/op       160 B/op          1 allocs/op
BenchmarkAllocation/Array-6             100000000           15.7 ns/op         0 B/op          0 allocs/op

答案 1 :(得分:1)

此答案假定searchDB不保留对传递给它的片的引用。给定变量和函数名称的函数似乎不太可能保留引用。

这些选项具有相同的内存和性能特征:

 var tmpSlice []string
 tmpSlice := []string{}
 tmpSlice := make([]string, 0)
 tmpSlice := make([]string, 0, 0)

在第一次追加操作之前,它们都不分配内存。如果只有这些选项,请选择前两个选项之一,因为它们更易于阅读。

此选项将具有最佳性能:

tmpSlice := make([]string, 0, 10)

这确保切片的后备阵列分配一次。附加值后,将不会重新分配支持数组。

如果searchDB的参数没有转义,则将在堆栈上为后备数组分配一次。这是最佳的性能。您可以通过使用-gcflags "-m -l"选项进行构建来确定参数是否转义。

考虑到getDataFromDb调用数据库操作,选项之间的任何性能差异都将在噪音中。编写清晰,简单的代码比优化代码更为重要。

我可能会选择var tmpSlice []string而不是tmpSlice := make([]string, 0, 10),因为不需要了解值10来自何处。

答案 2 :(得分:0)

我会

var tmpSlice []string

这将为您提供一个空字符串切片,您可以根据需要追加。 除非切片范围变大并且您事先知道尺寸,否则我不会为此预先分配内存