猿问

切片性能附加比分配具有更好的性能

我试图了解 Golang 中切片追加与分配的性能,我认为通常分配会更好,但在这段代码中,看起来追加更好。我试图找出原因 - 但我正在努力解决这个问题。


这是 Leetcode 中的合并排序数组,下面的版本 1 给了我 3 毫秒的运行时间


func merge(nums1 []int, m int, nums2 []int, n int)  {

    

    tmpSlice := make([]int, m+n)

    tmpIndex := 0

    index1 := 0

    index2 := 0

    

    for index1 < m {

        value1 := nums1[index1]

        for index2 < n {

            value2 := nums2[index2]

            if value1 <= value2 {

                break

            } else {

                tmpSlice[tmpIndex] = value2    \\ <-- Assign

                index2++

                tmpIndex++          

            }

        }

        tmpSlice[tmpIndex] = value1    \\ <-- Assign

        index1++

        tmpIndex++ 

    }

    


    copy(nums1, tmpSlice[:tmpIndex])

    copy(nums1[tmpIndex:], nums2[index2:])


}

下面的版本 2 给了我 0 毫秒的运行时间


func merge(nums1 []int, m int, nums2 []int, n int)  {

   

    tmpSlice := make([]int, 0, m+n)

    tmpIndex := 0

    index1 := 0

    index2 := 0

    

    for index1 < m {

        value1 := nums1[index1]

        for index2 < n {

            value2 := nums2[index2]

            if value1 <= value2 {

                break

            } else {

                tmpSlice = append(tmpSlice, value2)    \\ <-- Append

                index2++

                tmpIndex++          

            }

        }

        tmpSlice = append (tmpSlice, value1)    \\ <-- Append

        index1++

        tmpIndex++ 

    }

    


    copy(nums1, tmpSlice[:tmpIndex])

    copy(nums1[tmpIndex:], nums2[index2:])


}

两个版本的唯一区别就是append vs assign,append速度更快。Append 正在检查内存分配,然后进行分配,对吗?Append 不应该更慢吗?


慕妹3146593
浏览 127回答 1
1回答

白衣非少年

我将两者都放在基准测试中,两者之间的性能几乎相等,append速度较慢,但几乎可以忽略不计。package main_testimport "testing"func BenchmarkMerge1(b *testing.B) {&nbsp; &nbsp; for i := 0; i < b.N; i++ {&nbsp; &nbsp; &nbsp; &nbsp; num1 := []int{1, 2, 3}&nbsp; &nbsp; &nbsp; &nbsp; num2 := []int{4, 5, 6}&nbsp; &nbsp; &nbsp; &nbsp; merge1(num1, len(num1), num2, len(num2))&nbsp; &nbsp; }}func merge1(nums1 []int, m int, nums2 []int, n int) {&nbsp; &nbsp; tmpSlice := make([]int, m+n)&nbsp; &nbsp; tmpIndex := 0&nbsp; &nbsp; index1 := 0&nbsp; &nbsp; index2 := 0&nbsp; &nbsp; for index1 < m {&nbsp; &nbsp; &nbsp; &nbsp; value1 := nums1[index1]&nbsp; &nbsp; &nbsp; &nbsp; for index2 < n {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; value2 := nums2[index2]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if value1 <= value2 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tmpSlice[tmpIndex] = value2 // <-- Assign&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; index2++&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tmpIndex++&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; tmpSlice[tmpIndex] = value1 // <-- Assign&nbsp; &nbsp; &nbsp; &nbsp; index1++&nbsp; &nbsp; &nbsp; &nbsp; tmpIndex++&nbsp; &nbsp; }&nbsp; &nbsp; copy(nums1, tmpSlice[:tmpIndex])&nbsp; &nbsp; copy(nums1[tmpIndex:], nums2[index2:])}func BenchmarkMerge2(b *testing.B) {&nbsp; &nbsp; for i := 0; i < b.N; i++ {&nbsp; &nbsp; &nbsp; &nbsp; num1 := []int{1, 2, 3}&nbsp; &nbsp; &nbsp; &nbsp; num2 := []int{4, 5, 6}&nbsp; &nbsp; &nbsp; &nbsp; merge2(num1, len(num1), num2, len(num2))&nbsp; &nbsp; }}func merge2(nums1 []int, m int, nums2 []int, n int) {&nbsp; &nbsp; tmpSlice := make([]int, 0, m+n)&nbsp; &nbsp; tmpIndex := 0&nbsp; &nbsp; index1 := 0&nbsp; &nbsp; index2 := 0&nbsp; &nbsp; for index1 < m {&nbsp; &nbsp; &nbsp; &nbsp; value1 := nums1[index1]&nbsp; &nbsp; &nbsp; &nbsp; for index2 < n {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; value2 := nums2[index2]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if value1 <= value2 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tmpSlice = append(tmpSlice, value2) // <-- Append&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; index2++&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tmpIndex++&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; tmpSlice = append(tmpSlice, value1) // <-- Append&nbsp; &nbsp; &nbsp; &nbsp; index1++&nbsp; &nbsp; &nbsp; &nbsp; tmpIndex++&nbsp; &nbsp; }&nbsp; &nbsp; copy(nums1, tmpSlice[:tmpIndex])&nbsp; &nbsp; copy(nums1[tmpIndex:], nums2[index2:])}Running tool: /usr/local/go/bin/go test -benchmem -run=^$ -bench ^(BenchmarkMerge1|BenchmarkMerge2)$ example.com/mgoos: linuxgoarch: amd64pkg: example.com/mcpu: Intel(R) Core(TM) i7-10870H CPU @ 2.20GHzBenchmarkMerge1-16&nbsp; &nbsp; &nbsp; 34586568&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 36.40 ns/op&nbsp; &nbsp; &nbsp; &nbsp;48 B/op&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 1 allocs/opBenchmarkMerge2-16&nbsp; &nbsp; &nbsp; 32561293&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 36.77 ns/op&nbsp; &nbsp; &nbsp; &nbsp;48 B/op&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 1 allocs/opPASSok&nbsp; &nbsp; &nbsp; example.com/m&nbsp; &nbsp;2.533s这是意料之中的,因为只要切片有容量,append 基本上就会进行分配。append还增加len切片标头中的字段(该提示感谢@rustyx),这解释了差异。当没有在切片上设置初始容量并使用追加时,您会看到更大的差异,因为它会“增长”需要时间的底层数组。如果我们更改tmpSlice := make([]int, 0, m+n)为tmpSlice := make([]int, 0)inmerge2我们会得到以下结果:Running tool: /usr/local/go/bin/go test -benchmem -run=^$ -bench ^(BenchmarkMerge1|BenchmarkMerge2)$ example.com/mgoos: linuxgoarch: amd64pkg: example.com/mcpu: Intel(R) Core(TM) i7-10870H CPU @ 2.20GHzBenchmarkMerge1-16&nbsp; &nbsp; &nbsp; 37319397&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 32.34 ns/op&nbsp; &nbsp; &nbsp; &nbsp;48 B/op&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 1 allocs/opBenchmarkMerge2-16&nbsp; &nbsp; &nbsp; 14543529&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 87.75 ns/op&nbsp; &nbsp; &nbsp; &nbsp;56 B/op&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 3 allocs/opPASSok&nbsp; &nbsp; &nbsp; example.com/m&nbsp; &nbsp;2.604sTL;DR,只要切片有容量,append就比分配慢(因为切片中的字段递增)几乎可以忽略不计len
随时随地看视频慕课网APP

相关分类

Go
我要回答