Releases: puzpuzpuz/xsync
v4.5.0
- Apply integer hash inlining in
Mapto more types #195 - Fix trivial hash flooding in integer hash function in
Map#199 - Use power-of-two capacity to replace modulo with bitwise ops in
MPMCQueue#200 - Avoid false sharing in
MPMCQueue#203
Kudos to @llxisdsh, @jeremiah-masters, and @huynhanx03 for making this release happen.
Breaking changes
MPMCQueue's capacity specified in NewMPMCQueue is now implicitly rounded up to the next power of 2. For example, xsync.NewMPMCQueue[string](1000) means that the queue's capacity will be set to 1024.
v4.4.0
- Micro-optimize
Mapfor integer keys #185 - Add
Map.RangeRelaxedmethod for faster map iteration #187 - Add
Map.DeleteMatchingmethod for batch entry deletion #186
Read-heavy operations on Map with integer keys are now 24-29% faster due to a more efficient hash function, as well as a number of micro-optimizations.
RangeRelaxed is a much faster (~11x), lock-free alternative to Range. The downside is that the same key may be visited by RangeRelaxed more than once if it is concurrently deleted and re-inserted during the iteration. RangeRelaxed should be preferred over Range in all cases when weaker consistency is acceptable.
m := xsync.NewMap[string, int]()
m.Store("alice", 10)
m.Store("bob", 20)
m.Store("carol", 30)
m.Store("dave", 40)
// Iterate map entries and calculate sum of all values.
sum := 0
m.RangeRelaxed(func(key string, value int) bool {
sum += value
return true // continue iteration
})DeleteMatching deletes all entries for which the delete return value of the input function is true. If the cancel return value is true, the iteration stops immediately. The function returns the number of deleted entries. The call locks a hash table bucket for the duration of evaluating the function for all entries in the bucket and performing deletions. It performs up to 20% faster than Range + Delete, yet if the percentage of the entries to-be-deleted is low, RangeRelaxed + Delete combination should be more efficient.
// Delete entries with value greater than 25.
deleted := m.DeleteMatching(func(key string, value int) (delete, cancel bool) {
return value > 25, false
})v4.3.0
All is similar to Range, but returns an iter.Seq2, so is compatible with Go 1.23+ iterators. All of the same caveats and behavior from Range apply to All.
m := xsync.NewMap[string, int]()
for i := range 100 {
m.Store(strconv.Itoa(i), i)
}
// Will print all of the map entries
for key, val := range m.All() {
fmt.Printf("m[%q] = %q\n")
}Kudos to @llxisdsh and @moskyb for making this release happen.
v4.2.0
Introduces cooperative rehashing for xsync.Map this means that goroutines that execute write operations, such as Compute or Store, may participate in table rehashing when the hash table grows or shrinks. From now on, table rehashing never spawns additional goroutines.
This behavior is always enabled, so the WithSerialResize function is now marked as deprecated and acts as a no-op.
v4.1.0
- New data structure:
UMPSCQueue#168 - Speed up
LoadAndDeleteandDeletein case of non-existingMapkey #167 - Parallel
Mapresize #170
UMPSCQueue is meant to serve as a replacement for a channel. However, crucially, it has infinite capacity. This is a very bad idea in many cases as it means that it never exhibits backpressure. In other words, if nothing is consuming elements from the queue, it will eventually consume all available memory and crash the process. However, there are also cases where this is desired behavior as it means the queue will dynamically allocate more memory to store temporary bursts, allowing producers to never block while the consumer catches up.
From now on, Map spawns additional goroutines to speed up resizing the hash table. This can be disabled when creating a Map with the new WithSerialResize setting:
m := xsync.NewMap[int, int](xsync.WithSerialResize())
// resize will take place on the current goroutine only
for i := 0; i < 10000; i++ {
m.Store(i, i)
}Thanks @PapaCharlie and @llxisdsh for the contributions!
v4.0.0
- Minimal Golang version is now 1.24.
- All non-generic data structures are now removed. Generic versions should be used instead - they use the old names, but type aliases are present to simplify v3-to-v4 code migration.
MapOf's hasher API is gone. The default and only hash function is now based onmaphash.Comparable.Map'sComputeAPI now supports no-op (cancel) compute operation.
Thanks @PapaCharlie for making this release happen
Migration notes
- The old
*Oftypes are kept as type aliases for the renamed data structures to simplify the migration, e.g.MapOfis an alias forMap. NewMapOfPresizedfunction is gone.NewMapcombined withWithPresizeshould be used instead.Map.Computemethod now expectsvalueFnto return aComputeOpvalue instead of a boolean flag. That's to support compute operation cancellation, so that the call does nothing.Map.LoadOrTryComputemethod is renamed toLoadOrCompute. The oldLoadOrComputemethod is removed as it was redundant.
v3.5.1
v3.5.0
v3.4.1
v3.4.0
- Add optimistic locking methods to
RBMutex#138 and #140 - Fix
Map/MapOfcapacity calculation forWithPresize#139
RBMutex now has methods for optimistic locking:
mu := xsync.NewRBMutex()
if locked, t := mu.TryRLock(); locked {
// critical reader section...
mu.RUnlock(t)
}
if mu.TryLock() {
// critical writer section...
mu.Unlock()
}Thanks @kkroo for the contribution.