【问题标题】:Why is iterating over a map so much slower than iterating over a slice in Golang?为什么在 Golang 中迭代 map 比迭代 slice 慢得多?
【发布时间】:2015-10-20 19:47:14
【问题描述】:

我在 Golang 中使用地图实现了一个稀疏矩阵,我注意到在排除其他可能的原因之后,我的代码开始需要更长的时间才能完成此更改,似乎罪魁祸首是地图本身的迭代。 Go Playground link(由于某种原因不起作用)。

package main

import (
    "fmt"
    "time"
    "math"
)

func main() {
    z := 50000000
    a := make(map[int]int, z)
    b := make([]int, z)

    for i := 0; i < z; i++ {
        a[i] = i
        b[i] = i
    }

    t0 := time.Now()
    for key, value := range a {
        if key != value { // never happens
            fmt.Println("a", key, value)
        }
    }
    d0 := time.Now().Sub(t0)

    t1 := time.Now()
    for key, value := range b {
        if key != value { // never happens
            fmt.Println("b", key, value)
        }
    }
    d1 := time.Now().Sub(t1)

    fmt.Println(
        "a:", d0,
        "b:", d1,
        "diff:", math.Max(float64(d0), float64(d1)) / math.Min(float64(d0), float64(d1)),
    )
}

迭代超过 50M 项会返回以下时间:

alix@local:~/Go/src$ go version
go version go1.3.3 linux/amd64
alix@local:~/Go/src$ go run b.go 
a: 1.195424429s b: 68.588488ms diff: 17.777154632611037

我想知道,与切片相比,为什么在地图上迭代几乎要慢 20 倍?

【问题讨论】:

  • 为什么不会在地图上迭代要慢得多?切片只是连续的内存,而哈希图是一种更复杂的数据结构。
  • 嗯,显而易见的答案是底层结构是一个数组和一个哈希表。在一种情况下,您正在迭代键并且(在范围抽象中)访问每个键的值。另一方面,你正在走过一段连续的记忆。

标签: performance dictionary go slice sparse-matrix


【解决方案1】:

这归结为内存中的表示。您对不同数据结构的表示和算法复杂性的概念有多熟悉?遍历数组或切片很简单。值在内存中是连续的。然而,遍历映射需要遍历键空间并查找哈希表结构。

map 的动态能力可以插入任何值的键,而不会占用分配稀疏数组的大量空间,而且尽管不如数组快,但可以在键空间上高效地进行查找,是为什么哈希表有时比数组更受欢迎,尽管数组(和切片)在给定索引的情况下具有更快的“常数”(O(1)) 查找时间。

这一切都取决于您是否需要这种或那种数据结构的特性,以及您是否愿意处理所涉及的副作用或陷阱。

【讨论】:

  • 哈希表被认为是O(1),但具有比数组更高的常数。索引数组的时间复杂度被正确分类为Θ(1)(大θ)。
  • 谢谢我已经编辑过了。已经有一段时间了,我很模糊,但这是完全正确的。
【解决方案2】:

将我的评论作为答案似乎是合理的。您正在比较的迭代性能的底层结构是哈希表和数组(https://en.wikipedia.org/wiki/Hash_tablehttps://en.wikipedia.org/wiki/Array_data_structure)。范围抽象实际上是(推测,找不到代码)迭代所有键,访问每个值,并将两者分配给k,v :=。如果您不熟悉在数组中的访问,则它是常数时间,因为您只需将 sizeof(type)*i 添加到起始指针即可获取项目。我不知道 golang 中 map 的内部结构是什么,但我知道它是内存表示,因此访问效率没有那么高。

关于该主题的规范声明并不多; http://golang.org/ref/spec#For_statements

如果我有时间查找 map 和 slice/array 的范围实现,我会提供更多技术细节。

【讨论】:

  • 这是一个很好的答案,更多地处理了具体的 golang 细节。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-30
  • 1970-01-01
  • 2019-04-09
  • 1970-01-01
  • 2021-01-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多