最快和最节省内存的golang并发哈希图

735 阅读2分钟

HaxMap

一个极快的并发哈希图

这项工作源于Cornelk-hashmap,并对性能和API进行了进一步的改进。

安装

你需要Golang1.19.x或以上版本。

$ go get github.com/alphadose/haxmap

使用方法

支持的地图键类型 ->int,uint,uintptrstring

package main

import (
	"fmt"

	"github.com/alphadose/haxmap"
)

func main() {
	// initialize map with key type `int` and value type `string`
	mep := haxmap.New[int, string]()

	// set a value (overwrites existing value if present)
	mep.Set(1, "one")

	// get the value and print it
	val, ok := mep.Get(1)
	if ok {
		println(val)
	}

	mep.Set(2, "two")
	mep.Set(3, "three")

	// ForEach loop to iterate over all key-value pairs and execute the given lambda
	mep.ForEach(func(key int, value string) {
		fmt.Printf("Key -> %d | Value -> %s\n", key, value)
	})

	// delete values
	mep.Del(1)
	mep.Del(2)
	mep.Del(3)
	mep.Del(0) // delete is safe even if a key doesn't exists

	if mep.Len() == 0 {
		println("cleanup complete")
	}
}

基准测试

基准测试是针对golang sync.Mapcornelk-hashmap进行的。

所有的结果都是从30次运行的benchstat中计算出来的(代码可在此获得)。

  1. 仅限并发读取
name                         time/op
HaxMapReadsOnly-8            11.1µs ±12%
GoSyncMapReadsOnly-8         22.0µs ±13%
CornelkMapReadsOnly-8        16.7µs ± 6%

name                         alloc/op
HaxMapReadsOnly-8             0.00B
GoSyncMapReadsOnly-8          0.00B
CornelkMapReadsOnly-8         7.43B ± 8%

name                         allocs/op
HaxMapReadsOnly-8              0.00
GoSyncMapReadsOnly-8           0.00
CornelkMapReadsOnly-8          0.00
  1. 并发读与写
name                         time/op
HaxMapReadsWithWrites-8      13.1µs ±11%
GoSyncMapReadsWithWrites-8   25.0µs ±12%
CornelkMapReadsWithWrites-8  20.0µs ± 6%

name                         alloc/op
HaxMapReadsWithWrites-8      6.71kB ± 9%
GoSyncMapReadsWithWrites-8   6.32kB ± 6%
CornelkMapReadsWithWrites-8  10.0kB ± 9%

name                         allocs/op
HaxMapReadsWithWrites-8         239 ± 9%
GoSyncMapReadsWithWrites-8      585 ± 6%
CornelkMapReadsWithWrites-8     407 ± 9%

从以上结果可以看出,haxmap 是目前最快的golang并发哈希图,它具有最少的allocs/op ,并且动态内存占用率低。

提示

  1. HaxMap默认使用xxHash算法,但你可以覆盖它,并插入你自己的自定义哈希函数。下面是一个同样的例子:
package main

import (
	"github.com/alphadose/haxmap"
)

// your custom hash function
// the hash function signature must adhere to `func(keyType) uintptr`
// where keyType ∈ {int, uint, uintptr, string}
func customStringHasher(s string) uintptr {
	return uintptr(len(s))
}

func main() {
	// initialize a string-string map with your custom hash function
	// this overrides the default xxHash algorithm
	m := &haxmap.HashMap[string, string]{Hasher: customStringHasher}

	m.Set("one", "1")
	val, ok := m.Get("one")
	if ok {
		println(val)
	}
}
  1. 你可以预先分配地图的大小,这在某些情况下会提高性能:
package main

import (
	"github.com/alphadose/haxmap"
)

func main() {
	const initialSize = 1 << 10

	// pre-allocating the size of the map will prevent all grow operations
	// until that limit is hit thereby improving performance
	m := haxmap.New[int, string](initialSize)

	m.Set(1, "1")
	val, ok := m.Get(1)
	if ok {
		println(val)
	}
}