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類型遍歷一切都正常,但是到了對字符串進行遍歷時這里就出問題了,出問題主要在索引這一塊??梢钥闯鏊饕敲總€字節(jié)的位置,在go語言中的字符串是UTF-8編碼的字節(jié)序列。而不是單個的Unicode字符。遇到中文字符時需要使用多個字節(jié)表示,英文字符一個字節(jié)進行表示,索引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
- 和切片不同的是,迭代過程中,刪除還未迭代到的鍵值對,則該鍵值對不會被迭代。
- 在迭代過程中,如果創(chuàng)建新的鍵值對,那么新增鍵值對,可能被迭代,也可能不會被迭代。個人認為應該是hash的無序性問題
- 針對 nil 字典,迭代次數(shù)為 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變量讀取一個關閉的管道并不會進行退出,而是一直進行等待,但是如果關閉了會返回一個狀態(tài)值可以根據(jù)該狀態(tài)值判斷是否需要操作
2.for和range之間奇怪的問題
2.1 無限遍歷現(xiàn)象
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循環(huán)一直在永無止境的進行追加元素。 range循環(huán)正常。原因:for循環(huán)的i < len(c)-1都會進行重新計算一次,造成了永遠都不成立。range循環(huán)遍歷在開始前只會計算一次,如果在循環(huán)進行修改也不會影響正常變量。
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循環(huán)進行修改了成功,但是在range循環(huán)修改失效,為什么呢?因為range循環(huán)返回的是對該值的拷貝,所以修改失效。for循環(huán)修相當于進行原地修改了。但如果在for循環(huán)里面進行賦值修改操作,那么修改也會進行失效 具體如下
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 的性能大約是 range 的 600 倍。
為什么會產(chǎn)生這么大呢?
上述也說過,range遍歷會對迭代的值創(chuàng)建一個拷貝。在占據(jù)占用較大的結構時每次都需要進行做一次拷貝,取申請大約4kb的內存,顯然是大可不必的。所以在對于占據(jù)較大的結構時,應該使用for進行變量操作。
總結
如何選擇合適的遍歷,在針對與測試場景的情況下,圖便捷可以使用range,畢竟for循環(huán)需要寫一堆的條件,初始值等。但是如果遍歷的元素是個占用大個內存的結構的話,避免使用range進行遍歷。且如果需要進行修改操作的話只能用for遍歷來修改,其實range也可以進行索引遍歷的,在本文為寫,讀者可以去嘗試一下。
到此這篇關于Go語言中for和range的性能比較的文章就介紹到這了,更多相關Go for range內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
linux下通過go語言獲得系統(tǒng)進程cpu使用情況的方法
這篇文章主要介紹了linux下通過go語言獲得系統(tǒng)進程cpu使用情況的方法,實例分析了Go語言使用linux的系統(tǒng)命令ps來分析cpu使用情況的技巧,需要的朋友可以參考下2015-03-03

