为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
摇曳的蔷薇 2023-06-12 15:49:36
如果我需要在一个函数中使用 tmp 切片,并且该函数会被多次调用,它们的最大容量不会超过 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 []string, tmpSlice := make([]string, 0, 0), tmpSlice := make([]string, 0, 5), 还是tmpSlice := make([]string, 0, 10)?或任何其他建议?
查看完整描述

3 回答

?
慕丝7291255

TA贡献1859条经验 获得超6个赞

最快的是代码不在堆上分配。


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


-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



查看完整回答
反对 回复 2023-06-12
?
收到一只叮咚

TA贡献1821条经验 获得超4个赞

    该答案假定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 []stringover tmpSlice := make([]string, 0, 10),因为没有必要了解前者的值 10 从何而来。


查看完整回答
反对 回复 2023-06-12
?
MM们

TA贡献1886条经验 获得超2个赞

我会做

var tmpSlice []string

这会给你一个空字符串切片,你可以根据需要追加。除非切片范围变大并且您事先知道维度,否则我不会为它预分配内存


查看完整回答
反对 回复 2023-06-12
  • 3 回答
  • 0 关注
  • 113 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信