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

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

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

Go
慕妹3146593 2022-10-24 15:38:05
我试图了解 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 不应该更慢吗?
查看完整描述

1 回答

?
白衣非少年

TA贡献1155条经验 获得超0个赞

我将两者都放在基准测试中,两者之间的性能几乎相等,append速度较慢,但几乎可以忽略不计。


package main_test


import "testing"


func BenchmarkMerge1(b *testing.B) {

    for i := 0; i < b.N; i++ {

        num1 := []int{1, 2, 3}

        num2 := []int{4, 5, 6}

        merge1(num1, len(num1), num2, len(num2))

    }

}


func merge1(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:])

}


func BenchmarkMerge2(b *testing.B) {

    for i := 0; i < b.N; i++ {

        num1 := []int{1, 2, 3}

        num2 := []int{4, 5, 6}

        merge2(num1, len(num1), num2, len(num2))

    }

}


func merge2(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:])

}

Running tool: /usr/local/go/bin/go test -benchmem -run=^$ -bench ^(BenchmarkMerge1|BenchmarkMerge2)$ example.com/m


goos: linux

goarch: amd64

pkg: example.com/m

cpu: Intel(R) Core(TM) i7-10870H CPU @ 2.20GHz

BenchmarkMerge1-16      34586568            36.40 ns/op       48 B/op          1 allocs/op

BenchmarkMerge2-16      32561293            36.77 ns/op       48 B/op          1 allocs/op

PASS

ok      example.com/m   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/m


goos: linux

goarch: amd64

pkg: example.com/m

cpu: Intel(R) Core(TM) i7-10870H CPU @ 2.20GHz

BenchmarkMerge1-16      37319397            32.34 ns/op       48 B/op          1 allocs/op

BenchmarkMerge2-16      14543529            87.75 ns/op       56 B/op          3 allocs/op

PASS

ok      example.com/m   2.604s

TL;DR,只要切片有容量,append就比分配慢(因为切片中的字段递增)几乎可以忽略不计len


查看完整回答
反对 回复 2022-10-24
  • 1 回答
  • 0 关注
  • 126 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号