• for与for range


    传统的for和for range是golang唯二提供的能够遍历的循环结构,但是for range并不是简单的for语法糖,实际上他们之间仍然有很大的不同

    for range更新数组未成功?

    	var a = [3]int{1, 2, 3}
    	var r [3]int
    
    	for i, v := range a {
    		if i == 0 {
    			a[1] = 4
    		}
    		r[i] = v
        // 1,2,3
        t.Log(r[i])
    	}
    
    	// 1,2,3
    	t.Log(r)
    	// 1,4,3
    	t.Log(a)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    以上代码输出的r并非期望的[1,4,3],而是一个[1,2,3]。那么这是不是意味着没有更新成功呢?

    再来看另一段代码

    	a := []int{1, 2, 3}
    	for i, v := range a {
    		if i == 0 {
    			a[1] = 4
    		}
        // 输出1,4,3
    		t.Log(v)
    	}
    	// 1,4,3
    	t.Log(a)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    与前段代码不同的在于数组a在遍历过程中的值发生了改变

    那么这是怎么回事呢?

    首先,让我们看看两段代码的不同之处,除了第一段多用一个数组进行储存遍历过程中的元素之外,还有就是第一段是数组而非slice,让我们改成slice试试看

    	var a = []int{1, 2, 3}
    	r := make([]int, 3)
    
    	for i, v := range a {
    		if i == 0 {
    			a[1] = 4
    		}
    		r[i] = v
        // 1, 4, 3
    		t.Log(r[i])
    	}
    	
    	// 1, 4, 3
    	t.Log(r)
      // 1, 4, 3
    	t.Log(a)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    结果居然和第二段代码一致,无论是打印的,还是r、a都是1、4、3,也就是值确实是发生了更新

    显然我们可以得出一个结论——那就是for range对于数组和slice更新的结果并不完全一致。那这是for range针对两种不同的数据结构作出不同的处理了呢?

    或许可以回到slice和数组的区别上来。我们知道数组是固定的,最基本的结构,而slice是动态的,由一个slice header表示。

    而for range在go tour中就说的很清楚了,是元素的复制,那么对于数组来说,数组复制就是复制全部,而slice复制仅仅只会slice header结构,那么对slice的遍历更改还是会直接影响到slice,但是array却并不会,只会在遍历结束后,更新到原数组

    那么基于此,一个猜想提出了,会不会for range这种复制的形势会更加消耗资源呢?

    for range会更加消耗资源吗?

    以下测试在macOS下,cpu为Intel® Core™ i5-1038NG7 CPU @ 2.00GHz,数据量为100 000,go版本为1.18

    forfor range
    int3183036660
    1 int结构体3430438568
    2 int结构体35278183267
    3 int结构体35219200697
    4 int结构体34232322685
    5 int结构体34219620547

    从表中可以看出在int情况下for range相比于for性能慢15%

    而对于结构体,差的就更远了,在5 int结构体情况下,for range相比于for性能慢1700%,这是相当大的差距,显然这提醒我们在结构体复杂的大数据量遍历还是尽量不用for range

    不出意料的是for range确实比普通的for性能消耗更多,但是值得注意的是在结构体的情况下,for range的性能消耗以一个惊人的速度上升,这是怎么回事呢?

    为什么结构体对for range性能影响这么大?

    从https://github.com/golang/go/issues/24416来看,似乎是因为大结构体无法被ssa优化,并且这个优化在go1.18看起来仍未完成

    SSA(Single-assignment form)是一种中间表示形式属性,其要求每个变量只被赋值一次,并在使用之前被定义。

    Ref

    1. https://go.dev/tour/moretypes/16
    2. https://en.wikipedia.org/wiki/Static_single-assignment_form
  • 相关阅读:
    编程语言为什么有null?
    计算机基础知识44
    Linux 自定义文件 acl 权限
    【mia】httpserver : windows bash shell 运行 srsplayer
    逻辑回归算法
    Python学习基础笔记十三——函数
    Aspose.Words for .NET图表教程——关于使用图表数据
    kotlin基础教程:<8>类的延时成员变量、初始化代码块和陷阱
    猿创征文|【Vue五分钟】 Vuex状态管理总结
    硬盘驱动器基础知识
  • 原文地址:https://blog.csdn.net/iUcool/article/details/132806756