Skip to content

perf(store): replace sync.Map with plain map in cachekv stores#2822

Draft
pdrobnjak wants to merge 1 commit intoperf/remove-vis-sortedstorefrom
perf/replace-syncmap-cachekv
Draft

perf(store): replace sync.Map with plain map in cachekv stores#2822
pdrobnjak wants to merge 1 commit intoperf/remove-vis-sortedstorefrom
perf/replace-syncmap-cachekv

Conversation

@pdrobnjak
Copy link
Contributor

Summary

  • Replace *sync.Map with typed Go maps (map[string]*types.CValue, map[string]struct{}) in both sei-cosmos and giga cachekv implementations
  • Within OCC, each worker gets its own cachekv.Store — zero concurrent access makes sync.Map thread-safety pure overhead
  • Eliminates ~27.5 GB allocation and 370M objects per profiling window from sync.Map internal nodes (newIndirectNode, newEntryNode)
  • Keeps sync.RWMutex for defense-in-depth (uncontested, <20ns)

Files changed: sei-cosmos/store/cachekv/store.go, sei-cosmos/store/cachekv/memiterator.go, giga/deps/store/cachekv.go

Micro-benchmarks (cachekv, 3 runs)

Benchmark Before (sync.Map) After (map) Speedup Allocs
BlankParentAppend (Set) 868 ns/op 383 ns/op 56% faster 5 → 1
GetKeyFound 252.6 ns/op 123.3 ns/op 51% faster 1 → 1
SetKeySize32 (Set+Iter) 2545 ns/op 1525 ns/op 40% faster 9 → 5
IteratorNext 1970 ns/op 1180 ns/op 40% faster 4 → 4
IteratorOnParent1MDeletes 2670 ns/op 1599 ns/op 40% faster 5 → 5
GetNoKeyFound 53.8 ns/op 36.7 ns/op 32% faster 2 → 2

4 allocs/op eliminated from Set paths — exactly the sync.Map internal node allocations.

TPS benchmark (GIGA+OCC, EVMTransfer 1000 txs/batch)

Metric Before (after #2820) After this PR Delta
TPS (avg, 2300+ blocks) ~8,400 ~8,936 +536 TPS (+6.4%)
TPS (range) 8,000–8,800 7,800–9,602
Block process avg ~82ms ~77ms -6%
Block time avg ~118ms ~112ms -5%

Cumulative from origin/main

Metric Origin/main This PR Cumulative
TPS (avg) ~7,800 ~8,936 +14.6%

Test plan

  • sei-cosmos/store/cachekv tests (11 tests pass)
  • sei-cosmos/store/... full suite (15 packages pass)
  • giga/tests (all pass)
  • gofmt -s -l clean
  • Cachekv micro-benchmarks (32-56% faster, 4 allocs/op eliminated)
  • TPS benchmark 2300+ blocks steady state

🤖 Generated with Claude Code

@github-actions
Copy link

github-actions bot commented Feb 6, 2026

The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedFeb 6, 2026, 6:42 PM

@pdrobnjak pdrobnjak self-assigned this Feb 6, 2026
@pdrobnjak pdrobnjak force-pushed the perf/remove-vis-sortedstore branch from 2f8f30d to 024e61c Compare February 6, 2026 12:28
@pdrobnjak pdrobnjak force-pushed the perf/replace-syncmap-cachekv branch from 0e0765c to 3171cd1 Compare February 6, 2026 12:28
Comment on lines +117 to +120
for key, cv := range store.cache {
// Replace with a clean (non-dirty) version of the same value
store.cache.Store(key, types.NewCValue(cv.Value(), false))
return true
})
store.cache[key] = types.NewCValue(cv.Value(), false)
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
Comment on lines +173 to +184
for key, cv := range store.cache {
kbz := []byte(key)
if bytes.Compare(kbz, start) < 0 || bytes.Compare(kbz, end) >= 0 {
// we don't want to break out of the iteration since cache isn't sorted
return true
continue
}
cv := value.(*types.CValue)
if cv.Value() == nil {
delete(keyStrs, key.(string))
delete(keyStrs, key)
} else {
keyStrs[key.(string)] = struct{}{}
keyStrs[key] = struct{}{}
}
return true
})
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
Comment on lines +283 to +289
for cKey := range store.unsortedCache {
if dbm.IsKeyInDomain(conv.UnsafeStrToBytes(cKey), start, end) {
cacheValue, ok := store.cache.Load(key)
if ok {
unsorted = append(unsorted, &kv.Pair{Key: []byte(cKey), Value: cacheValue.(*types.CValue).Value()})
if cacheValue, ok := store.cache[cKey]; ok {
unsorted = append(unsorted, &kv.Pair{Key: []byte(cKey), Value: cacheValue.Value()})
}
}
return true
})
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
Comment on lines +366 to +377
for key, cv := range store.cache {
kbz := []byte(key)
if bytes.Compare(kbz, start) < 0 || bytes.Compare(kbz, end) >= 0 {
// we don't want to break out of the iteration since cache isn't sorted
return true
continue
}
cv := value.(*types.CValue)
if cv.Value() == nil {
delete(keyStrs, key.(string))
delete(keyStrs, key)
} else {
keyStrs[key.(string)] = struct{}{}
keyStrs[key] = struct{}{}
}
return true
})
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
@pdrobnjak pdrobnjak marked this pull request as draft February 6, 2026 15:25
@pdrobnjak pdrobnjak force-pushed the perf/remove-vis-sortedstore branch from 024e61c to 19fc5b8 Compare February 6, 2026 17:20
@pdrobnjak pdrobnjak force-pushed the perf/replace-syncmap-cachekv branch from 3171cd1 to 7ddc9f6 Compare February 6, 2026 17:20
@pdrobnjak pdrobnjak force-pushed the perf/remove-vis-sortedstore branch from 19fc5b8 to f6066c0 Compare February 6, 2026 17:43
@pdrobnjak pdrobnjak force-pushed the perf/replace-syncmap-cachekv branch from 7ddc9f6 to 915aaaf Compare February 6, 2026 17:43
@pdrobnjak pdrobnjak force-pushed the perf/remove-vis-sortedstore branch from f6066c0 to f2970af Compare February 6, 2026 18:41
Within OCC, each worker gets its own CacheMultiStore chain with
dedicated cachekv.Store instances — zero concurrent access. The
sync.Map thread-safety overhead (CAS, internal node allocs) is
pure waste in this single-goroutine context.

Replace *sync.Map with typed Go maps in both sei-cosmos and giga
cachekv implementations:
- cache:         map[string]*types.CValue
- deleted:       map[string]struct{}
- unsortedCache: map[string]struct{}

Keep sync.RWMutex for defense-in-depth (uncontested, <20ns).

Micro-benchmarks: 32-56% faster across all cachekv operations,
4 allocs/op eliminated from Set paths.
TPS benchmark: ~8,936 avg (+14.6% cumulative over origin/main).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@pdrobnjak pdrobnjak force-pushed the perf/replace-syncmap-cachekv branch from 915aaaf to 6ab07c6 Compare February 6, 2026 18:41
@codecov
Copy link

codecov bot commented Feb 6, 2026

Codecov Report

❌ Patch coverage is 96.07843% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 46.95%. Comparing base (f2970af) to head (6ab07c6).

Files with missing lines Patch % Lines
giga/deps/store/cachekv.go 95.45% 1 Missing ⚠️
sei-cosmos/store/cachekv/store.go 96.42% 1 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (f2970af) and HEAD (6ab07c6). Click for more details.

HEAD has 1 upload less than BASE
Flag BASE (f2970af) HEAD (6ab07c6)
sei-tendermint 1 0
Additional details and impacted files

Impacted file tree graph

@@                       Coverage Diff                       @@
##           perf/remove-vis-sortedstore    #2822      +/-   ##
===============================================================
- Coverage                        56.67%   46.95%   -9.73%     
===============================================================
  Files                             2031     1965      -66     
  Lines                           165978   160637    -5341     
===============================================================
- Hits                             94074    75432   -18642     
- Misses                           63660    78667   +15007     
+ Partials                          8244     6538    -1706     
Flag Coverage Δ
sei-chain 41.53% <96.07%> (-0.02%) ⬇️
sei-cosmos 48.15% <96.55%> (-0.01%) ⬇️
sei-db 68.72% <ø> (ø)
sei-tendermint ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
sei-cosmos/store/cachekv/memiterator.go 81.81% <100.00%> (ø)
giga/deps/store/cachekv.go 78.40% <95.45%> (-1.38%) ⬇️
sei-cosmos/store/cachekv/store.go 88.46% <96.42%> (-0.37%) ⬇️

... and 315 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant