Go语言中for和range的性能比较

 更新时间:2023年07月31日 09:37:06   作者:搬运工李  
这篇文章主要为大家详细介绍了Go语言中for和range语句的使用以及性能比较,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解一下

能GET到的知识点

什么场景使用for和range

1. 从一个遍历开始

1.1万能的range遍历

1.遍历array/slice/strings

array

package main  
import "fmt"  
func main() {  
    var UserIDList = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}  
    for i, v := range UserIDList {  
        fmt.Println(i, v)  
    }  
}

输出:

0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10

slice

package main  
import "fmt"  
func main() {  
    var UserIDList = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}  
    var UerSlice = UserIDList[:]  
    for i, v := range UerSlice {  
        fmt.Println(i, v)  
    }  
}

输出:

0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10

字符串

func main(){
    var Username = "斑斑砖abc"  
    for i, v := range Username {  
        fmt.Println(i, v)  
    }
}

输出:

0 26001
3 26001
6 30742
9 97
10 98
11 99

range进行对array、slice类型遍历一切都正常,但是到了对字符串进行遍历时这里就出问题了,出问题主要在索引这一块。可以看出索引是每个字节的位置,在go语言中的字符串是UTF-8编码的字节序列。而不是单个的Unicode字符。遇到中文字符时需要使用多个字节表示,英文字符一个字节进行表示,索引0-3表示了一个字符及以此完后。

2.遍历map

func ByMap() {  
    m := map[string]int{  
    "one": 1,  
    "two": 2,  
    "three": 3,  
    }  
    for k, v := range m {  
        delete(m, "two")  
        m["four"] = 4  
        fmt.Printf("%v: %v\n", k, v)  
    }  
}

输出:

one: 1
four: 4
three: 3

  • 和切片不同的是,迭代过程中,删除还未迭代到的键值对,则该键值对不会被迭代。
  • 在迭代过程中,如果创建新的键值对,那么新增键值对,可能被迭代,也可能不会被迭代。个人认为应该是hash的无序性问题
  • 针对 nil 字典,迭代次数为 0

3.遍历channel

func ByChannel() {  
    ch := make(chan string)  
    go func() {  
        ch <- "a"  
        ch <- "b"  
        ch <- "c"  
        ch <- "d"  
        close(ch)  
    }()  
    time.Sleep(time.Second)
    for n := range ch {  
        fmt.Println(n)  
    }  
}
  • 针对于range对关闭channel的遍历,会直到把元素都读取完成。
  • 但是在for遍历会造成阻塞,因为for变量读取一个关闭的管道并不会进行退出,而是一直进行等待,但是如果关闭了会返回一个状态值可以根据该状态值判断是否需要操作

2.for和range之间奇怪的问题

2.1 无限遍历现象

for

c := []int{1, 2, 3}  
for i := 0; i < len(c); i++ {  
    c = append(c, i)  
    fmt.Println(i)  
}

输出:

1
2
3
.
.
.
15096
15097
15098
15099
15100
15101
15102
15103
15104

range

c := []int{1, 2, 3}  
for _, v := range c {  
    c = append(c, v)  
    fmt.Println(v)  
}

输出:

1
2
3

可以看出for循环一直在永无止境的进行追加元素。 range循环正常。原因:for循环的i < len(c)-1都会进行重新计算一次,造成了永远都不成立。range循环遍历在开始前只会计算一次,如果在循环进行修改也不会影响正常变量。

2.2 在for和range进行修改操作

for

type UserInfo struct {  
    Name string  
    Age int  
}
var UserInfoList = [3]UserInfo{  
    {Name: "John", Age: 25},  
    {Name: "Jane", Age: 30},  
    {Name: "Mike", Age: 28},  
    }  
for i := 0; i < len(UserInfoList); i++ {  
    UserInfoList[i].Age += i  
}  
fmt.Println(UserInfoList)

输出:

0
1
2
[{John 25} {Jane 31} {Mike 30}]

range

var UserInfoList = [3]UserInfo{  
    {Name: "John", Age: 25},  
    {Name: "Jane", Age: 30},  
    {Name: "Mike", Age: 28},  
    }  
for i, info := range UserInfoList {  
    info.Age += i  
}  
fmt.Println(UserInfoList)

输出:

[{John 25} {Jane 30} {Mike 28}]

可以看出for循环进行修改了成功,但是在range循环修改失效,为什么呢?因为range循环返回的是对该值的拷贝,所以修改失效。for循环修相当于进行原地修改了。但如果在for循环里面进行赋值修改操作,那么修改也会进行失效 具体如下

var UserInfoList = [3]UserInfo{  
    {Name: "John", Age: 25},  
    {Name: "Jane", Age: 30},  
    {Name: "Mike", Age: 28},  
}  
for i := 0; i < len(UserInfoList); i++ {  
    fmt.Println(i)  
    item := UserInfoList[i]  
    item.Age += i  
}  
fmt.Println(UserInfoList)

输出:

> [{John 25} {Jane 30} {Mike 28}]

3. Benchmark大比拼

主要是针对大类型结构体

type Item struct {  
    id int  
    val [4096]byte  
}

for_test.go

func BenchmarkForStruct(b *testing.B) {  
    var items [1024]Item  
    for i := 0; i < b.N; i++ {  
        length := len(items)  
        var tmp int  
        for k := 0; k < length; k++ {  
            tmp = items[k].id  
        }  
        _ = tmp  
    }  
}
func BenchmarkRangeStruct(b *testing.B) {  
    var items [1024]Item  
    for i := 0; i < b.N; i++ {  
        var tmp int  
        for _, item := range items {  
            tmp = item.id  
        }  
        _ = tmp  
    }  
}

goos: windows
goarch: amd64
pkg: article/02fortest
cpu: AMD Ryzen 5 5600G with Radeon Graphics
BenchmarkForStruct-12            2503378               474.8 ns/op             0 B/op          0 allocs/op
BenchmarkRangeStruct-12             4983            232744 ns/op               0 B/op          0 allocs/op
PASS
ok      article/02fortest       3.268s

可以看出 for 的性能大约是 range600 倍。

为什么会产生这么大呢?

上述也说过,range遍历会对迭代的值创建一个拷贝。在占据占用较大的结构时每次都需要进行做一次拷贝,取申请大约4kb的内存,显然是大可不必的。所以在对于占据较大的结构时,应该使用for进行变量操作。

总结

如何选择合适的遍历,在针对与测试场景的情况下,图便捷可以使用range,毕竟for循环需要写一堆的条件,初始值等。但是如果遍历的元素是个占用大个内存的结构的话,避免使用range进行遍历。且如果需要进行修改操作的话只能用for遍历来修改,其实range也可以进行索引遍历的,在本文为写,读者可以去尝试一下。

到此这篇关于Go语言中for和range的性能比较的文章就介绍到这了,更多相关Go for range内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go程序的init函数在什么时候执行

    Go程序的init函数在什么时候执行

    在Go语言中,init 函数是一个特殊的函数,它用于执行程序的初始化任务,本文主要介绍了Go程序的init函数在什么时候执行,感兴趣的可以了解一下
    2023-10-10
  • Go语言常见错误之误用init函数实例解析

    Go语言常见错误之误用init函数实例解析

    Go语言中的init函数为开发者提供了一种在程序正式运行前初始化包级变量的机制,然而,由于init函数的特殊性,不当地使用它可能引起一系列问题,本文将深入探讨如何有效地使用init函数,列举常见误用并提供相应的避免策略
    2024-01-01
  • golang 如何用反射reflect操作结构体

    golang 如何用反射reflect操作结构体

    这篇文章主要介绍了golang 用反射reflect操作结构体的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • Golang中自定义json序列化时间格式的示例代码

    Golang中自定义json序列化时间格式的示例代码

    Go语言作为一个由Google开发,号称互联网的C语言的语言,自然也对JSON格式支持很好,下面这篇文章主要介绍了关于Golang中自定义json序列化时间格式的相关内容,下面话不多说了,来一起看看详细的介绍吧
    2024-08-08
  • GO语言异常处理机制panic和recover分析

    GO语言异常处理机制panic和recover分析

    这篇文章主要介绍了GO语言异常处理机制panic和recover,分析了捕获运行时发生错误的方法,是非常实用的技巧,需要的朋友可以参考下
    2014-12-12
  • linux下通过go语言获得系统进程cpu使用情况的方法

    linux下通过go语言获得系统进程cpu使用情况的方法

    这篇文章主要介绍了linux下通过go语言获得系统进程cpu使用情况的方法,实例分析了Go语言使用linux的系统命令ps来分析cpu使用情况的技巧,需要的朋友可以参考下
    2015-03-03
  • Golang 分割字符串的实现示例

    Golang 分割字符串的实现示例

    本文主要介绍了Golang 分割字符串的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • golang 设置web请求状态码操作

    golang 设置web请求状态码操作

    这篇文章主要介绍了golang 设置web请求状态码操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Golang channel死锁的几种情况小结

    Golang channel死锁的几种情况小结

    本文主要介绍了Golang channel死锁的几种情况小结,详细的介绍了六种情况,具有一定的参考价值,感兴趣的可以了解一下
    2024-08-08
  • 详解Golang中使用map时的注意问题

    详解Golang中使用map时的注意问题

    Golang中的map是一种数据结构,它允许你使用键值对的形式存储和访问数据,map在Go中是非排序的,提供了高效查找、插入和删除元素的能力,特别是当键是不可变类型,本文给大家详细介绍了Golang中使用map时的注意问题,需要的朋友可以参考下
    2024-06-06

最新评论