猿问

Go map 与 C# 字典

我写了一个快速而肮脏的测试来检查 Go 与 C# 在并发查找访问方面的性能,结果令我感到惊讶。


这是一个非常简单的例子,我不是 Go 专家,但测试只是在地图上执行 1,000,000 次锁定/检查/添加/解锁操作,它只是单线程的,因为我只检查这些功能:


package main


import (

    "fmt"

    "sync"

    "time"

)


var mu sync.Mutex


func main() {

    cache := make(map[int]int, 1000000)

    start := time.Now()


    for i := 0; i < 1000000; i++ {

        mu.Lock()

        if _, ok := cache[i]; ok == false {

            cache[i] = i

        }

        mu.Unlock()

    }


    end := time.Since(start)

    fmt.Println(end)


    var sum int64

    for _, v := range cache {

        sum += int64(v)

    }


    fmt.Println(sum)

}

在 C# 中也是同样的事情(通过 LINQPad):


void Main()

{

    var cache = new Dictionary<int, int>(1000000);

    var sw = Stopwatch.StartNew();


    for (var i = 0; i < 1000000; i++)

    {

        lock (cache)

        {

            int d;

            if (cache.TryGetValue(i, out d) == false)

            {

                cache.Add(i, i);

            }

        }

    }


    $"{sw.ElapsedMilliseconds:N0}ms".Dump();


    var sum = 0L;

    foreach (var kvp in cache)

    {

        sum += kvp.Value;

    }

    sum.Dump();

}

我将两个集合的元素相加以确保它们匹配 (499,999,500,000) 并打印花费的时间。结果如下:


C#:56 毫秒

运行时间:327 毫秒

我已经检查过无法初始化地图的大小,只能初始化容量,所以我想知道我是否可以做些什么来提高 Go 地图的性能?


在没有地图访问权限的情况下,Go 执行 1,000,000 次锁定/解锁操作需要 32 毫秒。


回首忆惘然
浏览 167回答 3
3回答

喵喵时光机

[S]o 我想知道我是否可以做些什么来提高 Go 地图的性能?不,那里没有。Go 基本上没有性能旋钮。(请注意,Go 的map类型是一个非常通用且健壮的哈希映射,它使用强加密哈希(如果可能)来防止攻击并强制使用随机密钥/迭代顺序。它是“完全通用的”,而不仅仅是“快速字典”。)完全正确:有环境变量GOGC可以“调整”GC。

蛊毒传说

可能有一件事被忽视了,把整个练习变成了苹果和橘子:同步。在 Go 方面,您使用 Mutex,它在每次访问时都会进入内核。在 C# 方面,您使用 lock(){},它使用 SpinLock 的组合,并且仅在需要时回退到内核调用。由于您的测试无论如何都是在单个线程中执行的,因此 C# 甚至从未进入内核。在 Go 中不鼓励使用互斥锁,而应该使用通道来进行同步。几个建议: 1. 如果您想自己对地图/字典进行基准测试,请删除同步。2. 如果您想对并发性能进行基准测试,请使用正确的构造和范式编写您的测试。干杯!

料青山看我应如是

我发现如果我缩小 1000000 到 100000,golang 速度会从 151.0087ms 变为 10.0005ms(15.1 乘法),而 csharp 版本从 65ms 变为 9ms(7.22 乘法),所以这意味着 golang 的 hashmap 难以处理大地图?我写了一个简单的 go benchmark 程序,像这样func BenchmarkIntMapGet100(b *testing.B) {&nbsp; &nbsp; count := 100&nbsp; &nbsp; setupIntMap(b, count)&nbsp; &nbsp; b.ResetTimer()&nbsp; &nbsp; for i:=0; i<b.N; i++{&nbsp; &nbsp; &nbsp; &nbsp; _, _ = intMap[i%count]&nbsp; &nbsp; }}我得到了结果BenchmarkIntMapGet10-4&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 100000000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;15.6 ns/opBenchmarkIntMapGet100-4&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;100000000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;17.1 ns/opBenchmarkIntMapGet1000-4&nbsp; &nbsp; &nbsp; &nbsp; 50000000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 25.7 ns/opBenchmarkIntMapGet10000-4&nbsp; &nbsp; &nbsp; &nbsp;50000000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 32.3 ns/opBenchmarkIntMapGet100000-4&nbsp; &nbsp; &nbsp; 30000000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 39.2 ns/opBenchmarkIntMapGet1000000-4&nbsp; &nbsp; &nbsp;20000000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 67.2 ns/opBenchmarkIntMapGet10000000-4&nbsp; &nbsp; 20000000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 82.3 ns/op
随时随地看视频慕课网APP

相关分类

Go
我要回答