为了账号安全,请及时绑定邮箱和手机立即绑定

Go map 与 C# 字典

Go map 与 C# 字典

Go
回首忆惘然 2021-12-07 16:18:49
我写了一个快速而肮脏的测试来检查 Go 与 C# 在并发查找访问方面的性能,结果令我感到惊讶。这是一个非常简单的例子,我不是 Go 专家,但测试只是在地图上执行 1,000,000 次锁定/检查/添加/解锁操作,它只是单线程的,因为我只检查这些功能:package mainimport (    "fmt"    "sync"    "time")var mu sync.Mutexfunc 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 毫秒。
查看完整描述

3 回答

?
喵喵时光机

TA贡献1846条经验 获得超7个赞

[S]o 我想知道我是否可以做些什么来提高 Go 地图的性能?

不,那里没有。Go 基本上没有性能旋钮。

(请注意,Go 的map类型是一个非常通用且健壮的哈希映射,它使用强加密哈希(如果可能)来防止攻击并强制使用随机密钥/迭代顺序。它是“完全通用的”,而不仅仅是“快速字典”。)

完全正确:有环境变量GOGC可以“调整”GC。


查看完整回答
反对 回复 2021-12-07
?
蛊毒传说

TA贡献1895条经验 获得超3个赞

可能有一件事被忽视了,把整个练习变成了苹果和橘子:同步。在 Go 方面,您使用 Mutex,它在每次访问时都会进入内核。在 C# 方面,您使用 lock(){},它使用 SpinLock 的组合,并且仅在需要时回退到内核调用。由于您的测试无论如何都是在单个线程中执行的,因此 C# 甚至从未进入内核。

在 Go 中不鼓励使用互斥锁,而应该使用通道来进行同步。

几个建议: 1. 如果您想自己对地图/字典进行基准测试,请删除同步。2. 如果您想对并发性能进行基准测试,请使用正确的构造和范式编写您的测试。

干杯!


查看完整回答
反对 回复 2021-12-07
?
料青山看我应如是

TA贡献1772条经验 获得超8个赞

我发现如果我缩小 1000000 到 100000,golang 速度会从 151.0087ms 变为 10.0005ms(15.1 乘法),而 csharp 版本从 65ms 变为 9ms(7.22 乘法),所以这意味着 golang 的 hashmap 难以处理大地图?


我写了一个简单的 go benchmark 程序,像这样


func BenchmarkIntMapGet100(b *testing.B) {

    count := 100

    setupIntMap(b, count)


    b.ResetTimer()

    for i:=0; i<b.N; i++{

        _, _ = intMap[i%count]

    }

}

我得到了结果


BenchmarkIntMapGet10-4          100000000           15.6 ns/op

BenchmarkIntMapGet100-4         100000000           17.1 ns/op

BenchmarkIntMapGet1000-4        50000000            25.7 ns/op

BenchmarkIntMapGet10000-4       50000000            32.3 ns/op

BenchmarkIntMapGet100000-4      30000000            39.2 ns/op

BenchmarkIntMapGet1000000-4     20000000            67.2 ns/op

BenchmarkIntMapGet10000000-4    20000000            82.3 ns/op


查看完整回答
反对 回复 2021-12-07
  • 3 回答
  • 0 关注
  • 166 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信