切片的有效分配(上限与长度)

时间:2019-06-07 07:44:36

标签: go slice

假设我正在创建一个切片,我事先知道我想通过对1e5的连续调用通过一个append元素的for循环来填充:

// Append 1e5 strings to the slice

for i := 0; i<= 1e5; i++ {
    value := fmt.Sprintf("Entry: %d", i)
    myslice = append(myslice, value)
}

这是初始化切片的更有效方式,原因为何:

a。声明零个字符串切片?

var myslice []string

b。预先将其长度设置为1e5

myslice = make([]string, 1e5)

c。是否将其长度和容量都设置为1e5

myslice = make([]string, 1e5, 1e5)

2 个答案:

答案 0 :(得分:7)

您的bc解决方案是相同的:使用make()创建一个未指定容量的切片,“缺失”容量默认为给定长度。

还请注意,如果您事先创建了一个具有一定长度的切片,则无法使用append()来填充切片,因为它将向切片中添加 new 元素,并且不会“重用”分配的元素。因此,在这种情况下,您必须使用index expression为元素分配值,例如myslice[i] = value

如果从容量为0的切片开始,则每当您添加不适合容量的元素时,都必须分配一个新的后备阵列,并且必须复制“旧”内容,因此解决方案的速度必须较慢本质上。

我将定义并考虑以下不同的解决方案(我使用[]int分片来避免fmt.Sprintf()干预/干扰我们的基准测试)

var s []int

func BenchmarkA(b *testing.B) {
    for i := 0; i < b.N; i++ {
        s = nil
        for j := 0; j < 1e5; j++ {
            s = append(s, j)
        }
    }
}

func BenchmarkB(b *testing.B) {
    for i := 0; i < b.N; i++ {
        s = make([]int, 0, 1e5)
        for j := 0; j < 1e5; j++ {
            s = append(s, j)
        }
    }
}

func BenchmarkBLocal(b *testing.B) {
    for i := 0; i < b.N; i++ {
        s := make([]int, 0, 1e5)
        for j := 0; j < 1e5; j++ {
            s = append(s, j)
        }
    }
}

func BenchmarkD(b *testing.B) {
    for i := 0; i < b.N; i++ {
        s = make([]int, 1e5)
        for j := range s {
            s[j] = j
        }
    }
}

注意:我在基准测试中使用程序包级别的变量(BLocal除外),因为在使用局部切片变量时可能会(并且确实会)发生一些优化)。

基准测试结果:

BenchmarkA-4         1000     1081599 ns/op     4654332 B/op     30 allocs/op
BenchmarkB-4         3000      371096 ns/op      802816 B/op      1 allocs/op
BenchmarkBLocal-4   10000      172427 ns/op      802816 B/op      1 allocs/op
BenchmarkD-4        10000      167305 ns/op      802816 B/op      1 allocs/op

A:如您所见,从nil切片开始是最慢的,使用最多的内存和分配空间。

B:使用容量(但长度仍为0)预分配切片并使用append:它只需要一次分配,并且速度更快,几乎是三次快速

BLocal:请注意,当使用局部片而不是程序包变量时,(编译器)进行了优化,并且获得了更快的速度:速度是{{1}的两倍}

D:即使在使用非局部变量的情况下,也不使用D,而是将元素分配给预分配的切片会获胜。

答案 1 :(得分:0)

在这种情况下,由于您已经知道要分配给切片的字符串元素的数量,

我更愿意使用 b或c

因为您将无法使用这两种方法来调整切片的大小。

如果您选择使用方法 a ,则在len等于容量之后,每次添加新元素时,切片的大小都会增加一倍。

https://play.golang.org/p/kSuX7cE176j