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

Go 如果我知道许多tmp片的最大大小,我应该在创建它们时设置容量吗?,go,Go,如果我需要在函数中使用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) { tmp

如果我需要在函数中使用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)
?还有其他建议吗?

我愿意

var tmpSlice []string
这将为您提供一个空字符串片段,您可以根据需要进行追加。
除非切片范围变大,并且您事先知道维度,否则我不会为它预先分配内存

如果代码不在堆上分配内存,那么最快的速度将是

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
}
创建在堆栈上分配且不转义的变量(按值传递变量,否则它们将转义)

您可以通过在建筑上添加
-gcflags“-m-l”
进行检查

下面的示例显示,如果我们用数组替换
slice
,并按值传递它,则会产生无需分配(在堆上)的快速代码


此答案假设
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
over
tmpSlice:=make([]string,0,10)
,因为不需要了解10的值从何而来。

非常感谢您提供这一技巧。因此,您建议首先将其设置为
nil
。好的,干杯^_^这并不能回答问题。非常感谢您的回答。但很抱歉,我不太明白你想表达什么。那么你对我的问题的最后建议是什么?您是否建议使用
var tmpSlice[10]字符串
?然后在循环中使用tmpSlice[i]=赋值?最后,当访问它时,再次循环直到最后一个非空字符串?非常感谢。我建议使用切片时不要过早优化。在你发现这个方法非常慢之后,你可以应用我的建议。想象代码的读者看到
make([]字符串,0,10)
,为什么是10?为什么不是42?如果没有理由,不要为了避免这种做法而提出更好的问题。数组的问题是很容易编写代码,使它们像切片一样慢。如果它回答了您的问题,请接受答案,这样我就可以用评论中的详细信息更新它。如果searchDb参数没有逃逸,那么编写的函数是最佳选择。该函数将在没有为片分配堆的情况下执行。如果参数真的转义了,那么最佳选择将取决于实际工作负载和运行时在append上分配额外容量的策略。在这种情况下,您需要测量以确定什么是最好的。如果searchDb调用数据库,那么选择之间的性能差异将体现在噪音中。@ThunderCat您好,非常感谢您的回复。很抱歉,我不太明白这里的
escape
是什么意思。是的,searchDb将从数据库的名称调用数据库。谢谢
tmpSlice := make([]string, 0, 10)