对于大量关键点,go maps无性能

对于大量关键点,go maps无性能,go,hashmap,maps,hash-collision,Go,Hashmap,Maps,Hash Collision,最近我发现围棋地图的行为非常奇怪。用例是创建一组整数,并对IsMember(id int)进行O(1)检查 目前的实施是: func convertToMap(v []int64) map[int64]void { out := make(map[int64]void, len(v)) for _, i := range v { out[i] = void{} } return out } type Group struct { membe

最近我发现围棋地图的行为非常奇怪。用例是创建一组整数,并对IsMember(id int)进行O(1)检查

目前的实施是:

func convertToMap(v []int64) map[int64]void {
    out := make(map[int64]void, len(v))
    for _, i := range v {
        out[i] = void{}
    }
   return out
}

type Group struct {
    members map[int64]void
}

type void struct{}

func (g *Group) IsMember(input string) (ok bool) {
    memberID, _ := strconv.ParseInt(input, 10, 64)      
    _, ok = g.members[memberID]
    return
}
当我对IsMember方法进行基准测试时,直到有600万成员,一切看起来都很好。但除此之外,每次查找地图都需要1秒的时间

基准测试:

func BenchmarkIsMember(b *testing.B) {
    b.ReportAllocs()
    b.ResetTimer()
    g := &Group{}
    g.members = convertToMap(benchmarkV)

    for N := 0; N < b.N && N < sizeOfGroup; N++ {
        g.IsMember(benchmarkKVString[N])
    }
}

var benchmarkV, benchmarkKVString = func(size int) ([]int64, []string{
    v := make([]int64, size)
    s := make([]string, size)
    for i := range v {
        val := rand.Int63()
        v[i] = val
        s[i] = strconv.FormatInt(val, 10)
    }
return v, s
}(sizeOfGroup)
超过680万的团体规模也会产生同样的结果

有人能帮我解释一下为什么会发生这种情况吗?在仍然使用地图的情况下,能做些什么来让它发挥作用吗


而且,我不明白为什么要分配这么多内存?即使所花费的时间是由于冲突和链表遍历,也不应该有任何mem分配,我的思维过程是否错误?

无需测量将切片转换为映射所需的额外分配,因为我们只想测量查找操作

我稍微修改了基准:

func BenchmarkIsMember(b *testing.B) {
    fn := func(size int) ([]int64, []string) {
        v := make([]int64, size)
        s := make([]string, size)

        for i := range v {
            val := rand.Int63()
            v[i] = val
            s[i] = strconv.FormatInt(val, 10)
        }

        return v, s
    }

    for _, size := range []int{
        6000000,
        6800000,
        6830000,
        60000000,
    } {
        b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) {
            var benchmarkV, benchmarkKVString = fn(size)

            g := &deltaGroup{}
            g.members = convertToMap(benchmarkV)

            b.ReportAllocs()
            b.ResetTimer()

            for N := 0; N < b.N && N < size; N++ {
                g.IsMember(benchmarkKVString[N])
            }
        })
    }
}

降级并不像您的示例中那样严重。

您的代码无法编译。
我不明白为什么要分配这么多内存。
。这是一个微妙的说法,因为您已经编写了
make(map[int64]void,len(v))
。另见@mh cbon如果是这样,那么600万的基准也应该显示相同的mem分配。600万和680万的基准在查找时间和内存分配方面都有很大的差异well@mh-在cbon nevermind上,基准也在计算将切片转换为映射的分配。了解问题
600万和680万的基准在查找时间和内存分配方面都有很大的差异
。一些运行时行为,但不确定。请参阅pool.Buffer。
func BenchmarkIsMember(b *testing.B) {
    fn := func(size int) ([]int64, []string) {
        v := make([]int64, size)
        s := make([]string, size)

        for i := range v {
            val := rand.Int63()
            v[i] = val
            s[i] = strconv.FormatInt(val, 10)
        }

        return v, s
    }

    for _, size := range []int{
        6000000,
        6800000,
        6830000,
        60000000,
    } {
        b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) {
            var benchmarkV, benchmarkKVString = fn(size)

            g := &deltaGroup{}
            g.members = convertToMap(benchmarkV)

            b.ReportAllocs()
            b.ResetTimer()

            for N := 0; N < b.N && N < size; N++ {
                g.IsMember(benchmarkKVString[N])
            }
        })
    }
}
go test ./... -bench=. -benchtime=10s -cpu=1
goos: linux
goarch: amd64
pkg: trash
BenchmarkIsMember/size=6000000          2000000000               0.55 ns/op            0 B/op          0 allocs/op
BenchmarkIsMember/size=6800000          1000000000               1.27 ns/op            0 B/op          0 allocs/op
BenchmarkIsMember/size=6830000          1000000000               1.23 ns/op            0 B/op          0 allocs/op
BenchmarkIsMember/size=60000000         100000000                136 ns/op               0 B/op          0 allocs/op
PASS
ok      trash   167.578s