Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sync: reduce contention between Map operations with new-but-disjoint keys #21035

Open
bcmills opened this issue Jul 16, 2017 · 8 comments

Comments

@bcmills
Copy link
Member

commented Jul 16, 2017

The Go 1.9 implementation of sync.Map uses a single Mutex to guard the read-write map containing new keys. That makes Store calls with different new keys always contend with each other, and also contend with Load calls with different new keys, even if the Loads and Stores for each key are confined to a single thread.

That doesn't really matter for the sync.Map use-cases in the standard library because they do not operate on new keys in the steady state, but it limits the utility of sync.Map for use-cases involving a high rate of churn. Such use-cases may include:

  • caches with high eviction rates, such as caches fronting key-value storage services
  • maps of RPC or HTTP stream ID to handler state
  • maps of opaque handles to Go pointers in order to write C-exported APIs that comply with cgo pointer-passing rules

We should explore ways to address new-key contention, such as sharding the read-write maps and associated locks (as suggested in #20360), journaling writes (and using a Bloom or HyperLogLog filter to avoid reading the journal, along the lines of #21032), or storing the read-write map in an atomic tree data structure instead of a built-in map.

@bcmills

This comment has been minimized.

Copy link
Member Author

commented Jul 16, 2017

(@orcaman and/or @OneOfOne might be interested in this one?)

@odeke-em odeke-em added this to the Go1.10 milestone Jul 16, 2017

@OneOfOne

This comment has been minimized.

Copy link
Contributor

commented Jul 17, 2017

I'm interested but I can't commit right now, the one idea that comes to my mind would require hacking runtime/hashmap* otherwise we'd have to double hash the keys.

@orcaman

This comment has been minimized.

Copy link

commented Jul 17, 2017

Very interesting task, I'd love to take this one. I think it should definitely be doable for the Go1.10 Milestone. I'll think about the design some more before I can 100% commit to doing this one in due time.

@OneOfOne

This comment has been minimized.

Copy link
Contributor

commented Jul 27, 2017

@orcaman What I wanted to do is something like https://github.com/OneOfOne/cmap/tree/master/v2,

I use a slightly modified version of this in my code, but I wanted to do that with sync.Map in a way, but that'd require a lot more runtime knowledge to be able to use runtime/map's hasher directly to do the sharding rather than double hash it.

Sadly I don't have the knowledge nor time to learn the internals of runtime/map right now.

By all means if you can do that, it'd be great or we can discuss it.

// very simple set/get benchmark using cmap, rwmutex map and sync.Map.

BenchmarkCMap/128-8     20000000               105 ns/op               0 B/op          0 allocs/op
BenchmarkCMap/256-8     20000000                88.6 ns/op             0 B/op          0 allocs/op
BenchmarkCMap/512-8     20000000                67.6 ns/op             0 B/op          0 allocs/op
BenchmarkCMap/1024-8    30000000                42.3 ns/op             0 B/op          0 allocs/op
BenchmarkCMap/2048-8    30000000                34.5 ns/op             0 B/op          0 allocs/op
BenchmarkCMap/4096-8    50000000                33.0 ns/op             0 B/op          0 allocs/op
BenchmarkMutexMap-8     10000000               196 ns/op               0 B/op          0 allocs/op
BenchmarkSyncMap-8      30000000                39.3 ns/op            16 B/op          1 allocs/op
@robaho

This comment has been minimized.

Copy link

commented Nov 26, 2018

I think you can just use a slice of locks and dirty maps with the low order bits of the hash performing the selection - but this requires access to the internal hash code.

@robaho

This comment has been minimized.

Copy link

commented Nov 27, 2018

@bcmills I am willing to give this a try. Is there a document that desribes using internal facilities when an implementation is part of the stdlib?

@bcmills

This comment has been minimized.

Copy link
Member Author

commented Nov 27, 2018

I don't know of any good starting docs, but perhaps @randall77 or @aclements can point you in the right direction.

bcho added a commit to bcho/concurrent-map that referenced this issue Dec 28, 2018
Update golang repo issue link
The `golang/go#21035` reference is no longer valid in GitHub's readme rendering.
@mojinfu

This comment has been minimized.

Copy link

commented Feb 27, 2019

really a good idea

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants
You can’t perform that action at this time.