我的用例是通过网络传输一组成员(整数),因此我们采用增量编码,并在接收端解码并将整个列表作为映射, map [string] struct {} O(1)复杂性的成员资格检查。
我面临的问题是,对于200万个整数,成员的实际大小仅为15MB,但是堆中的映射大小为100 + MB。好像Go的实际地图实现不适合大型地图。 由于它是一个客户端SDK,因此我不想对可用内存产生太大影响,并且可能需要将多个此类组长时间保留在内存中-大约1周。
Go中是否有更好的替代DS?
type void struct{}
func ToMap(v []int64) map[string]void {
out := map[string]void{}
for _, i := range v {
out[strconv.Itoa(int(i))] = void{}
}
return out
}
答案 0 :(得分:4)
这是地图的一种内存更有效的形式:
type void struct{}
func ToMap(v []int64) map[int64]void {
m := make(map[int64]void, len(v))
for _, i := range v {
m[i] = void{}
}
return m
}
Go映射针对整数键进行了优化。通过提供确切的地图大小作为提示来优化地图分配。
string
有一个隐式指针,这会使垃圾收集器(gc)每次扫描时都跟随该指针。
这是200万个伪随机整数的Go基准:
package main
import (
"math/rand"
"strconv"
"testing"
)
type void struct{}
func ToMap1(v []int64) map[string]void {
out := map[string]void{}
for _, i := range v {
out[strconv.Itoa(int(i))] = void{}
}
return out
}
func ToMap2(v []int64) map[int64]void {
m := make(map[int64]void, len(v))
for _, i := range v {
m[i] = void{}
}
return m
}
var benchmarkV = func() []int64 {
v := make([]int64, 2000000)
for i := range v {
v[i] = rand.Int63()
}
return v
}()
func BenchmarkToMap1(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for N := 0; N < b.N; N++ {
ToMap1(benchmarkV)
}
}
func BenchmarkToMap2(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for N := 0; N < b.N; N++ {
ToMap2(benchmarkV)
}
}
输出:
$ go test tomap_test.go -bench=.
BenchmarkToMap1-4 2 973358894 ns/op 235475280 B/op 2076779 allocs/op
BenchmarkToMap2-4 10 188489170 ns/op 44852584 B/op 23 allocs/op
$