首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么访问变量的速度比访问len()慢得多?

为什么访问变量的速度比访问len()慢得多?
EN

Stack Overflow用户
提问于 2022-07-11 01:55:10
回答 1查看 73关注 0票数 0

我编写了这个函数uniq,它接收int的排序切片,并返回删除重复项的切片:

代码语言:javascript
运行
复制
func uniq(x []int) []int {
    i := 0
    for i < len(x)-1 {
        if x[i] == x[i+1] {
            copy(x[i:], x[i+1:])
            x = x[:len(x)-1]
        } else {
            i++
        }
    }
    return x
}

uniq2,对uniq的重写具有相同的结果:

代码语言:javascript
运行
复制
func uniq2(x []int) []int {
    i := 0
    l := len(x)
    for i < l-1 {
        if x[i] == x[i+1] {
            copy(x[i:], x[i+1:])
            l--
        } else {
            i++
        }
    }
    return x[:l]
}

这两个函数之间唯一的区别是,在uniq2中,我将len(x)保存到变量l中,而不是每次切片和直接访问len(x),而是在转换切片时将其减少。

我认为uniq2会比uniq稍快一些,因为len(x)不再被称为迭代,但实际上,它要慢得令人费解。

通过这个测试,生成一个随机排序的切片,并在其上调用uniq/uniq2 1000次,我在Linux上运行了该测试:

代码语言:javascript
运行
复制
func main() {
    rand.Seed(time.Now().Unix())
    for i := 0; i < 1000; i++ {
        _ = uniq(genSlice())
        //_ = uniq2(genSlice())
    }
}
func genSlice() []int {
    x := make([]int, 0, 1000)
    for num := 1; num <= 10; num++ {
        amount := rand.Intn(1000)
        for i := 0; i < amount; i++ {
            x = append(x, num)
        }
    }
    return x
}
代码语言:javascript
运行
复制
$ go build uniq.go
$ time ./uniq

uniq通常需要5-6秒才能完成。而uniq2则慢了两倍多,在12-15秒之间。

为什么uniq2 (我将切片长度保存到一个变量中)比uniq慢得多,后者我直接调用了len

是不是应该再快一点?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-07-11 07:11:02

您期望的执行时间大致相同,因为您认为他们做的事情大致相同。

这两个函数之间唯一的区别是,在uniq2中,我将len(x)保存到变量l中,而不是每次切片和直接访问len(x),而是在转换切片时将其减少。

这是错误的。

第一个版本是:

代码语言:javascript
运行
复制
        copy(x[i:], x[i+1:])
        x = x[:len(x)-1]

第二点是:

代码语言:javascript
运行
复制
        copy(x[i:], x[i+1:])
        l--

第一个不同之处是,第一个分配(拷贝)一个片头,它是一个reflect.SliceHeader值,是3个整数(64位体系结构上的24个字节),而l--做了一个简单的减量,但速度要快得多。

但主要的区别并不在于此。主要区别在于,由于第一个版本更改了x切片(标头,包含的长度),最终复制的元素越来越少,而第二个版本不会更改x,总是复制到切片的末尾。x[i+1:]等同于x[x+1:len(x)]

为了演示,假设您使用length=10传递一个片段,并且拥有所有相同的元素。第一个版本将首先复制9个元素,然后再复制8个元素,然后再复制7个元素。第二个版本将首先复制9个元素,然后再复制9个元素,然后再复制9个元素,等等。

让我们修改您的函数以计算复制元素的数量:

代码语言:javascript
运行
复制
func uniq(x []int) []int {
    count := 0
    i := 0
    for i < len(x)-1 {
        if x[i] == x[i+1] {
            count += copy(x[i:], x[i+1:])
            x = x[:len(x)-1]
        } else {
            i++
        }
    }
    fmt.Println("uniq copied", count, "elements")
    return x
}

func uniq2(x []int) []int {
    count := 0
    i := 0
    l := len(x)
    for i < l-1 {
        if x[i] == x[i+1] {
            count += copy(x[i:], x[i+1:])
            l--
        } else {
            i++
        }
    }
    fmt.Println("uniq2 copied", count, "elements")
    return x[:l]
}

测试它:

代码语言:javascript
运行
复制
uniq(make([]int, 1000))
uniq2(make([]int, 1000))

产出如下:

代码语言:javascript
运行
复制
uniq copied 499500 elements
uniq2 copied 998001 elements

uniq2() 复制的元素是原来的两倍!

如果我们用随机切片测试它:

代码语言:javascript
运行
复制
uniq(genSlice())
uniq2(genSlice())

产出如下:

代码语言:javascript
运行
复制
uniq copied 7956671 elements
uniq2 copied 11900262 elements

再说一遍,uniq2() 复制的元素大约是的1.5倍!(但这在很大程度上取决于随机数)。

试试围棋游乐场上的示例。

“修正”是修改uniq2()以复制到l

代码语言:javascript
运行
复制
        copy(x[i:], x[i+1:l])
        l--

通过这种“适当”的改变,性能大致相同。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72933040

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档