cmap

package module
v1.5.0 Latest Latest
Warning

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

Go to latest
Published: Apr 3, 2025 License: MIT Imports: 4 Imported by: 1

README

concurrent map Build Status

As explained here and here, the map type in Go doesn't support concurrent reads and writes. go-cmap provides a high-performance solution to this by sharding the map with minimal time spent waiting for locks.

Prior to Go 1.9, there was no concurrent map implementation in the stdlib. In Go 1.9, sync.Map was introduced. The new sync.Map has a few key differences from this map. The stdlib sync.Map is designed for append-only scenarios. So if you want to use the map for something more like in-memory db, you might benefit from using our version. You can read more about it in the golang repo, for example here and here

Why fork

Because I felt that some of the methods written by the original author were not very user-friendly, I refactored a version myself and added a separate thread-safe map. It is convenient to use simpler thread-safe map in scenarios that do not require high concurrency.

Usage

Import the package:

import (
	"github.com/lockp111/go-cmap"
)

go get "github.com/lockp111/go-cmap"

The package is now imported under the "cmap" namespace.

Example


	// Create a new map.
	m := cmap.New[string]()

	// Sets item within map, sets "bar" under key "foo"
	m.Set("foo", "bar")

	// Retrieve item from map.
	bar, ok := m.Get("foo")

	// Removes item under key "foo"
	m.Remove("foo")

For more examples have a look at cmap_test.go.

Running tests:

go test "github.com/lockp111/go-cmap"

Guidelines for contributing

Contributions are highly welcome. In order for a contribution to be merged, please follow these guidelines:

  • Open an issue and describe what you are after (fixing a bug, adding an enhancement, etc.).
  • According to the core team's feedback on the above mentioned issue, submit a pull request, describing the changes and linking to the issue.
  • New code must have test coverage.
  • If the code is about performance issues, you must include benchmarks in the process (either in the issue or in the PR).
  • In general, we would like to keep concurrent-map as simple as possible and as similar to the native map. Please keep this in mind when opening issues.

Language

License

MIT (see LICENSE file)

Performance Comparison

We conducted performance tests on ConcurrentMap, sync.Map, and standard map+lock in various scenarios. Here's an analysis of the results:

Read/Write Ratio Tests
Scenario ConcurrentMap sync.Map Standard map+lock
Read Heavy (90% reads) 59.28 ns/op 22.89 ns/op 195.2 ns/op
Balanced (50% reads) 85.47 ns/op 72.72 ns/op 176.0 ns/op
Write Heavy (90% writes) 99.25 ns/op 123.9 ns/op 242.6 ns/op
Scale and Concurrency Tests (Read Heavy Scenario)
Size Goroutines ConcurrentMap sync.Map Standard map+lock
1000 10 49.80 ns/op 12.52 ns/op 70.11 ns/op
1000 50 55.00 ns/op 18.91 ns/op 187.1 ns/op
1000 100 58.60 ns/op 23.26 ns/op 193.8 ns/op
10000 10 51.14 ns/op 8.948 ns/op 86.93 ns/op
10000 50 55.77 ns/op 9.228 ns/op 218.2 ns/op
10000 100 59.46 ns/op 9.408 ns/op 210.5 ns/op
Performance Characteristics Analysis
  1. Read-dominant scenarios:

    • sync.Map performs best, especially with large data sets
    • ConcurrentMap shows moderate performance, taking about 2-6 times longer than sync.Map
    • Standard map+lock performs worst, with performance deteriorating significantly as concurrency increases
  2. Balanced read/write scenarios:

    • sync.Map and ConcurrentMap perform similarly
    • Standard map+lock still lags behind
  3. Write-dominant scenarios:

    • ConcurrentMap performs best in write-intensive scenarios
    • sync.Map has weaker write performance and higher memory allocation
    • Standard map+lock performs worst in high-concurrency write operations
  4. Scalability:

    • Standard map+lock performance drops significantly with increased concurrency, making it unsuitable for high-concurrency scenarios
    • ConcurrentMap performance slightly decreases with increased concurrency but remains relatively stable
    • sync.Map excels in high-concurrency read scenarios but underperforms with heavy writes
Usage Recommendations
  • For read-dominant workloads (>90% reads), sync.Map is recommended
  • For balanced or write-heavy workloads, ConcurrentMap is recommended
  • Avoid using standard map with locks in any high-concurrency scenario
  • For large-scale data with mostly read operations, sync.Map's performance advantage is most significant

Documentation

Index

Constants

This section is empty.

Variables

View Source
var SHARD_COUNT = 32

Functions

This section is empty.

Types

type ConcurrentMap

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

A "thread" safe map of type string:Anything. To avoid lock bottlenecks this map is dived to several (SHARD_COUNT) map shards.

func New

func New[V any]() ConcurrentMap[string, V]

Creates a new concurrent map.

func NewStringer

func NewStringer[K Stringer, V any]() ConcurrentMap[K, V]

Creates a new concurrent map.

func NewWithCustom added in v1.4.0

func NewWithCustom[K comparable, V any](sharding ShardingFunc[K, V]) ConcurrentMap[K, V]

Creates a new concurrent map.

func (ConcurrentMap[K, V]) Clear

func (m ConcurrentMap[K, V]) Clear()

Clear removes all items from map.

func (ConcurrentMap[K, V]) Count

func (m ConcurrentMap[K, V]) Count() int

Count returns the number of elements within the map.

func (ConcurrentMap[K, V]) Get

func (m ConcurrentMap[K, V]) Get(key K) (V, bool)

Get retrieves an element from map under given key.

func (ConcurrentMap[K, V]) GetCb added in v1.3.0

func (m ConcurrentMap[K, V]) GetCb(key K, cb GetCb[V])

GetCb locks the shard containing the key, retrieves its current value and calls the callback with those params

func (ConcurrentMap[K, V]) GetOrInsert

func (m ConcurrentMap[K, V]) GetOrInsert(key K, cb InsertCb[V]) V

GetOrInsert The method is used to retrieve the value corresponding to a key in ConcurrentMap. If the key does not exist, a new value will be inserted using the provided callback function.

func (ConcurrentMap[K, V]) GetShard

func (m ConcurrentMap[K, V]) GetShard(key K) *SafeMap[K, V]

GetShard returns shard under given key

func (ConcurrentMap[K, V]) Has

func (m ConcurrentMap[K, V]) Has(key K) bool

Looks up an item under specified key

func (ConcurrentMap[K, V]) IsEmpty

func (m ConcurrentMap[K, V]) IsEmpty() bool

IsEmpty checks if map is empty.

func (ConcurrentMap[K, V]) Items

func (m ConcurrentMap[K, V]) Items() map[K]V

Items returns all items as map[K]V

func (ConcurrentMap[K, V]) IterBuffered

func (m ConcurrentMap[K, V]) IterBuffered() <-chan Tuple[K, V]

IterBuffered returns a Iter iterator which could be used in a for range loop.

func (ConcurrentMap[K, V]) IterCb

func (m ConcurrentMap[K, V]) IterCb(fn IterCb[K, V])

Callback based iterator, cheapest way to read all elements in a map.

func (ConcurrentMap[K, V]) Keys

func (m ConcurrentMap[K, V]) Keys() []K

Keys returns all keys as []K

func (ConcurrentMap[K, V]) MSet

func (m ConcurrentMap[K, V]) MSet(data map[K]V)

func (ConcurrentMap[K, V]) MarshalJSON

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

Reviles ConcurrentMap "private" variables to json marshal.

func (ConcurrentMap[K, V]) Pop

func (m ConcurrentMap[K, V]) Pop(key K) (value V, exists bool)

Pop removes an element from the map and returns it

func (ConcurrentMap[K, V]) Remove

func (m ConcurrentMap[K, V]) Remove(key K)

Remove removes an element from the map.

func (ConcurrentMap[K, V]) RemoveCb

func (m ConcurrentMap[K, V]) RemoveCb(key K, cb RemoveCb[K, V]) (ok bool)

RemoveCb locks the shard containing the key, retrieves its current value and calls the callback with those params If callback returns true and element exists, it will remove it from the map Returns the value returned by the callback (even if element was not present in the map)

func (ConcurrentMap[K, V]) Set

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

Sets the given value under the specified key.

func (ConcurrentMap[K, V]) SetIfAbsent

func (m ConcurrentMap[K, V]) SetIfAbsent(key K, value V) (ok bool)

Sets the given value under the specified key if no value was associated with it.

func (ConcurrentMap[K, V]) SetIfExists added in v1.4.0

func (m ConcurrentMap[K, V]) SetIfExists(key K, value V) (ok bool)

Sets the given value under the specified key if a value was associated with it.

func (*ConcurrentMap[K, V]) UnmarshalJSON

func (m *ConcurrentMap[K, V]) UnmarshalJSON(b []byte) error

Reverse process of Marshal.

func (ConcurrentMap[K, V]) Upsert

func (m ConcurrentMap[K, V]) Upsert(key K, cb UpsertCb[V]) (result V)

Insert or Update - updates existing element or inserts a new one using UpsertCb

func (ConcurrentMap[K, V]) Values

func (m ConcurrentMap[K, V]) Values() []V

Values returns all Values as []V

type GetCb added in v1.3.0

type GetCb[V any] func(value V, exists bool)

GetCb is a callback executed in a map.GetCb() call, while Lock is held If returns true, the element will be inserted into the map

type InsertCb

type InsertCb[V any] func() V

type IterCb

type IterCb[K comparable, V any] func(key K, v V)

Iterator callbacalled for every key,value found in maps. RLock is held for all calls for a given shard therefore callback sess consistent view of a shard, but not across the shards

type RemoveCb

type RemoveCb[K any, V any] func(value V, exists bool) bool

RemoveCb is a callback executed in a map.RemoveCb() call, while Lock is held If returns true, the element will be removed from the map

type SafeMap

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

func NewSafe added in v1.4.0

func NewSafe[K comparable, V any]() *SafeMap[K, V]

NewSafe 创建一个新的键和值类型为 K 和 V 的 SafeMap 类型指针

func (*SafeMap[K, V]) Clone

func (s *SafeMap[K, V]) Clone() map[K]V

func (*SafeMap[K, V]) Count

func (s *SafeMap[K, V]) Count() int

func (*SafeMap[K, V]) Del

func (s *SafeMap[K, V]) Del(key K)

Del 方法用于删除 SafeMap 中的指定键值对

func (*SafeMap[K, V]) Find

func (s *SafeMap[K, V]) Find(fn func(key K, value V, exist bool), keys ...K)

Find 允许通过特定的键值集合来查找 SafeMap 中的值,并通过提供的函数进行处理

func (*SafeMap[K, V]) Get

func (s *SafeMap[K, V]) Get(key K) (V, bool)

Get 方法用于获取键对应的值

func (*SafeMap[K, V]) GetCb added in v1.4.0

func (s *SafeMap[K, V]) GetCb(key K, cb func(value V, exists bool))

GetCb 方法用于获取键对应的值,并调用回调函数

func (*SafeMap[K, V]) MarshalJSON

func (s *SafeMap[K, V]) MarshalJSON() ([]byte, error)

func (*SafeMap[K, V]) Set

func (s *SafeMap[K, V]) Set(key K, value V)

Set 方法用于设置键值对

func (*SafeMap[K, V]) UnmarshalJSON

func (s *SafeMap[K, V]) UnmarshalJSON(b []byte) (err error)

Reverse process of Marshal.

func (*SafeMap[K, V]) Update

func (s *SafeMap[K, V]) Update(fn func(map[K]V))

Update 允许通过特定的更新逻辑更新 SafeMap 中的值

func (*SafeMap[K, V]) View

func (s *SafeMap[K, V]) View(fn func(K, V))

View 提供了对 SafeMap 中键值对的只读访问视图

type ShardingFunc

type ShardingFunc[K comparable, V any] func(key K) uint32

type Stringer

type Stringer interface {
	fmt.Stringer
	comparable
}

type Tuple

type Tuple[K comparable, V any] struct {
	Key K
	Val V
}

Used by the Iter & IterBuffered functions to wrap two variables together over a channel,

type UpsertCb

type UpsertCb[V any] func(oldValue V, exist bool) V

Jump to

Keyboard shortcuts

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