Skip to content

perf: replace AccAddress.String() mutex+LRU with sync.Map#2902

Draft
pdrobnjak wants to merge 1 commit intomainfrom
pd/perf-accaddress-syncmap
Draft

perf: replace AccAddress.String() mutex+LRU with sync.Map#2902
pdrobnjak wants to merge 1 commit intomainfrom
pd/perf-accaddress-syncmap

Conversation

@pdrobnjak
Copy link
Contributor

Summary

  • Replace global sync.Mutex + simplelru.LRU in AccAddress.String() with sync.Map for lock-free reads
  • Eliminates 41.55s of mutex contention in the OCC hot path (bank keeper event emission: AddCoins, SubUnlockedCoins, Transfer, BuyGas)
  • Benchmark shows +9.6% median TPS improvement (6199 → 6794) in a controlled comparison run

Context

AccAddress.String() converts raw address bytes to bech32 encoding. The existing implementation uses a global sync.Mutex to protect a simplelru.LRU cache. Even cache hits require a write lock because LRU.Get() modifies the recency ordering.

Under OCC with many goroutines executing EVM transactions concurrently, this global mutex serializes all bech32 lookups across all worker goroutines, showing up as 26.79s (6.66%) in the mutex contention profile.

Change

Replace with sync.Map which provides:

  • Lock-free reads for cache hits (the common case) — zero contention
  • Atomic stores on cache misses (rare after warmup)

The tradeoff is losing LRU eviction, but practical address cardinality is bounded by on-chain accounts (similar order as the old 60k entry cap), so memory growth is bounded.

Benchmark Results

Metric Baseline Candidate Delta
Median TPS 6199 6794 +595 (+9.6%)
Avg TPS 5989 6047 +58 (+1.0%)

Mutex contention diff (focused on AccAddress.String): -41.55s eliminated

Test plan

  • go test ./sei-cosmos/types/... -run 'Addr|Bech32|Address' — all pass
  • gofmt -s -l — clean
  • CI passes

🤖 Generated with Claude Code

AccAddress.String() uses a global sync.Mutex to protect a simplelru.LRU
cache. Under OCC with many goroutines, this serializes all bech32 lookups
even on cache hits (LRU.Get modifies ordering, requiring a write lock).

The mutex profile shows 26.79s (6.66%) of contention on this path,
primarily from bank keeper event emission (AddCoins, Transfer, BuyGas).

Replace with sync.Map which provides lock-free reads for cache hits.
The tradeoff is losing LRU eviction, but practical address cardinality
is bounded by on-chain accounts (similar order as the old 60k cap).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link

github-actions bot commented Feb 17, 2026

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

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedFeb 17, 2026, 1:46 PM

@pdrobnjak pdrobnjak marked this pull request as draft February 17, 2026 13:52
@codecov
Copy link

codecov bot commented Feb 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 57.24%. Comparing base (8fc1d5b) to head (a97f560).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2902      +/-   ##
==========================================
+ Coverage   57.21%   57.24%   +0.02%     
==========================================
  Files        2093     2093              
  Lines      171755   171732      -23     
==========================================
+ Hits        98276    98312      +36     
+ Misses      64709    64667      -42     
+ Partials     8770     8753      -17     
Flag Coverage Δ
sei-chain 52.71% <ø> (+0.01%) ⬆️
sei-cosmos 48.19% <ø> (+0.04%) ⬆️
sei-db 68.72% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.
see 33 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.

// Slow path: compute bech32 and store with a stable key copy.
bech32Addr, err := bech32.ConvertAndEncode(GetConfig().GetBech32AccountAddrPrefix(), aa)
if err != nil {
panic(err)

Check warning

Code scanning / CodeQL

Panic in BeginBock or EndBlock consensus methods Warning

Possible panics in BeginBock- or EndBlock-related consensus methods could cause a chain halt
pdrobnjak added a commit that referenced this pull request Feb 17, 2026
Cosmos-level events (coin_spent, coin_received, transfer, etc.) emitted
during giga executor tx execution are never consumed: the ExecTxResult
is built without populating the Events field, and events are not
consensus-critical (stripped from deterministicExecTxResult).

EVM logs (Solidity events) flow through a completely separate path
(tempState.logs -> receipts + MsgEVMTransactionResponse) and are
unaffected.

When GIGA_SUPPRESS_COSMOS_EVENTS=true:
- EventManager.EmitEvent/EmitEvents return immediately (no mutex, no
  append, no bech32 encoding in event attributes)
- Snapshot() creates suppressed EventManagers
- Finalize() skips the flushEvents() consolidation loop

This eliminates ~55s of mutex contention per 30s profile window:
- AccAddress.String() bech32 cache mutex: ~27s
- EventManager.EmitEvents RWMutex: ~28s

Also updates benchmark/analysis/ with findings from the AccAddress.String
sync.Map optimization (PR #2902) and this event suppression discovery.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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

Comments