haxmap

package module
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 6, 2024 License: MIT Imports: 9 Imported by: 47

README

HaxMap

Main Actions Status Go Report Card License

A lightning fast concurrent hashmap

The hashing algorithm used was xxHash and the hashmap's buckets were implemented using Harris lock-free list

Installation

You need Golang 1.18.x or above

$ go get github.com/alphadose/haxmap

Usage

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")
	mep.Set(4, "four")

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

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

	// bulk deletion is supported too in the same API call
	// has better performance than deleting keys one by one
	mep.Del(2, 3, 4)

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

Benchmarks

Benchmarks were performed against golang sync.Map and the latest cornelk-hashmap

All results were computed from benchstat of 20 runs (code available here)

  1. Concurrent Reads Only
name                         time/op
HaxMapReadsOnly-8            6.94µs ± 4%
GoSyncMapReadsOnly-8         21.5µs ± 3%
CornelkMapReadsOnly-8        8.39µs ± 8%
  1. Concurrent Reads with Writes
name                         time/op
HaxMapReadsWithWrites-8      8.23µs ± 3%
GoSyncMapReadsWithWrites-8   25.0µs ± 2%
CornelkMapReadsWithWrites-8  8.83µs ±20%

name                         alloc/op
HaxMapReadsWithWrites-8      1.25kB ± 5%
GoSyncMapReadsWithWrites-8   6.20kB ± 7%
CornelkMapReadsWithWrites-8  1.53kB ± 9%

name                         allocs/op
HaxMapReadsWithWrites-8         156 ± 5%
GoSyncMapReadsWithWrites-8      574 ± 7%
CornelkMapReadsWithWrites-8     191 ± 9%

From the above results it is evident that haxmap takes the least time, memory and allocations in all cases making it the best golang concurrent hashmap in this period of time

Tips

  1. HaxMap by default uses xxHash algorithm, but you can override this and plug-in your own custom hash function. Beneath lies an example for the same.
package main

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

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

func main() {
	m := haxmap.New[string, string]() // initialize a string-string map
	m.SetHasher(customStringHasher) // this overrides the default xxHash algorithm

	m.Set("one", "1")
	val, ok := m.Get("one")
	if ok {
		println(val)
	}
}
  1. You can pre-allocate the size of the map which will improve performance in some cases.
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)
	}
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Map added in v1.0.0

type Map[K hashable, V any] struct {
	// contains filtered or unexported fields
}

Map implements the concurrent hashmap

func New

func New[K hashable, V any](size ...uintptr) *Map[K, V]

New returns a new HashMap instance with an optional specific initialization size

func (*Map[K, V]) Clear added in v1.4.0

func (m *Map[K, V]) Clear()

Clear the map by removing all entries in the map. This operation resets the underlying metadata to its initial state.

func (*Map[K, V]) CompareAndSwap added in v1.2.0

func (m *Map[K, V]) CompareAndSwap(key K, oldValue, newValue V) bool

CompareAndSwap atomically updates a map entry given its key by comparing current value to `oldValue` and setting it to `newValue` if the above comparison is successful It returns a boolean indicating whether the CompareAndSwap was successful or not

func (*Map[K, V]) Del added in v1.0.0

func (m *Map[K, V]) Del(keys ...K)

Del deletes key/keys from the map Bulk deletion is more efficient than deleting keys one by one

func (*Map[K, V]) Fillrate added in v1.0.0

func (m *Map[K, V]) Fillrate() uintptr

Fillrate returns the fill rate of the map as an percentage integer

func (*Map[K, V]) ForEach added in v1.0.0

func (m *Map[K, V]) ForEach(lambda func(K, V) bool)

ForEach iterates over key-value pairs and executes the lambda provided for each such pair lambda must return `true` to continue iteration and `false` to break iteration

func (*Map[K, V]) Get added in v1.0.0

func (m *Map[K, V]) Get(key K) (value V, ok bool)

Get retrieves an element from the map returns `false“ if element is absent

func (*Map[K, V]) GetAndDel added in v1.3.0

func (m *Map[K, V]) GetAndDel(key K) (value V, ok bool)

GetAndDel deletes the key from the map, returning the previous value if any.

func (*Map[K, V]) GetOrCompute added in v1.2.0

func (m *Map[K, V]) GetOrCompute(key K, valueFn func() V) (actual V, loaded bool)

GetOrCompute is similar to GetOrSet but the value to be set is obtained from a constructor the value constructor is called only once

func (*Map[K, V]) GetOrSet added in v1.1.0

func (m *Map[K, V]) GetOrSet(key K, value V) (actual V, loaded bool)

GetOrSet returns the existing value for the key if present Otherwise, it stores and returns the given value The loaded result is true if the value was loaded, false if stored

func (*Map[K, V]) Grow added in v1.0.0

func (m *Map[K, V]) Grow(newSize uintptr)

Grow resizes the hashmap to a new size, gets rounded up to next power of 2 To double the size of the hashmap use newSize 0 No resizing is done in case of another resize operation already being in progress Growth and map bucket policy is inspired from https://github.com/cornelk/hashmap

func (*Map[K, V]) Len added in v1.0.0

func (m *Map[K, V]) Len() uintptr

Len returns the number of key-value pairs within the map

func (*Map[K, V]) MarshalJSON added in v1.3.0

func (m *Map[K, V]) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (*Map[K, V]) Set added in v1.0.0

func (m *Map[K, V]) Set(key K, value V)

Set tries to update an element if key is present else it inserts a new element If a resizing operation is happening concurrently while calling Set() then the item might show up in the map only after the resize operation is finished

func (*Map[K, V]) SetHasher added in v1.0.0

func (m *Map[K, V]) SetHasher(hs func(K) uintptr)

SetHasher sets the hash function to the one provided by the user

func (*Map[K, V]) Swap added in v1.2.0

func (m *Map[K, V]) Swap(key K, newValue V) (oldValue V, swapped bool)

Swap atomically swaps the value of a map entry given its key It returns the old value if swap was successful and a boolean `swapped` indicating whether the swap was successful or not

func (*Map[K, V]) UnmarshalJSON added in v1.3.0

func (m *Map[K, V]) UnmarshalJSON(i []byte) error

UnmarshalJSON implements the json.Unmarshaler interface.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL