在Golang中,使用make初始化的map和使用{}初始化的map之间是否存在性能差异?

7

我们知道有两种初始化Map的方式(如下所示)。我想知道这两种方式之间是否存在性能差异。

var myMap map[string]int

那么

myMap = map[string]int{}

vs

myMap = make(map[string]int)

3
根据文档,使用地图字面量进行初始化与使用 'make' 初始化是“功能上相同”的。我认为这可以视为性能相等。 - Mario Santini
3个回答

5

在我的机器上,它们看起来差不多。

您可以轻松进行基准测试以进行比较。例如:

package bench

import "testing"

var result map[string]int

func BenchmarkMakeLiteral(b *testing.B) {
        var m map[string]int
        for n := 0; n < b.N; n++ {
                m = InitMapLiteral()
        }
        result = m
}

func BenchmarkMakeMake(b *testing.B) {
        var m map[string]int
        for n := 0; n < b.N; n++ {
                m = InitMapMake()
        }
        result = m
}

func InitMapLiteral() map[string]int {
        return map[string]int{}
}

func InitMapMake() map[string]int {
        return make(map[string]int)
}

在三次不同的运行中,得出的结果非常接近,可以被视为无意义:

第一次运行

$ go test -bench=.
testing: warning: no tests to run
PASS
BenchmarkMakeLiteral-8  10000000               160 ns/op
BenchmarkMakeMake-8     10000000               171 ns/op
ok      github.com/johnweldon/bench     3.664s

第二次运行

$ go test -bench=.
testing: warning: no tests to run
PASS
BenchmarkMakeLiteral-8  10000000               182 ns/op
BenchmarkMakeMake-8     10000000               173 ns/op
ok      github.com/johnweldon/bench     3.945s

第三次运行

$ go test -bench=.
testing: warning: no tests to run
PASS
BenchmarkMakeLiteral-8  10000000               170 ns/op
BenchmarkMakeMake-8     10000000               170 ns/op
ok      github.com/johnweldon/bench     3.751s

4

分配空映射表时没有区别,但是使用make命令,您可以传递第二个参数以预先分配映射表中的空间。这将节省在填充映射表时进行大量重新分配的时间。

基准测试

package maps

import "testing"

const SIZE = 10000

func fill(m map[int]bool, size int) {
    for i := 0; i < size; i++ {
        m[i] = true
    }
}

func BenchmarkEmpty(b *testing.B) {
    for n := 0; n < b.N; n++ {
        m := make(map[int]bool)
        fill(m, SIZE)
    }
}

func BenchmarkAllocated(b *testing.B) {
    for n := 0; n < b.N; n++ {
        m := make(map[int]bool, 2*SIZE)
        fill(m, SIZE)
    }
}

结果

go test -benchmem -bench .
BenchmarkEmpty-8             500       2988680 ns/op      431848 B/op        625 allocs/op
BenchmarkAllocated-8        1000       1618251 ns/op      360949 B/op         11 allocs/op

0
一年前,我碰巧发现如果您的值不是静态的,则使用显式分配空间的make比使用映射字面量更好。
因此,执行以下操作:
return map[string]float {
    "key1": SOME_COMPUTED_ABOVE_VALUE,
    "key2": SOME_COMPUTED_ABOVE_VALUE,
    // more keys here
    "keyN": SOME_COMPUTED_ABOVE_VALUE,
}

比...慢

// some code above
result := make(map[string]float, SIZE) // SIZE >= N
result["key1"] = SOME_COMPUTED_ABOVE_VALUE
result["key2"] = SOME_COMPUTED_ABOVE_VALUE
// more keys here
result["keyN"] = SOME_COMPUTED_ABOVE_VALUE
return result

对于很大的N(在我的用例中为N=300),这是一个问题。原因是编译器无法理解在第一种情况下至少需要分配N个插槽。

我写了一篇关于此的博客文章https://trams.github.io/golang-map-literal-performance/,并向社区报告了一个错误https://github.com/golang/go/issues/43020

截至golang 1.17,这仍然是一个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接