Skip to content

Conversation

@pdrobnjak
Copy link
Contributor

Summary

  • Skip the redundant initial Snapshot() call in NewDBImpl for the OCC executor hot path
  • Add NewDBImplWithoutSnapshot() used only by executeEVMTxWithGigaExecutor
  • Original NewDBImpl preserved for all other callers (tests, RPC, ante handlers)

Problem

In the OCC executor path, each EVM transaction creates 3 CacheMultiStore layers:

prepareTask:   CMS1 (VIS-wrapped)       ← needed for OCC isolation
NewDBImpl:     CMS2 (wrapping CMS1)     ← REDUNDANT
Prepare:       CMS3 (wrapping CMS2)     ← needed for EVM revert support

CMS2 exists to provide a GetCommittedState baseline via snapshottedCtxs[0]. But no state changes occur between NewDBImpl and Prepare (called by go-ethereum's StateTransition.Execute), so Prepare's Snapshot provides the same baseline.

Profiling after #2808 (30s, M4 Max, 1000 EVM transfers/block):

Metric Value
DBImpl.Snapshot alloc_space (cum) 44.8 GB
cachemulti.newStoreWithoutGiga alloc_space 22.5 GB
CMS layers per tx 3 (only 2 needed)

Changes

  • giga/deps/xevm/state/statedb.go: Add NewDBImplWithoutSnapshot() constructor + defensive guard on flushEvents
  • giga/deps/xevm/state/state.go: Defensive guard on GetCommittedState for empty snapshottedCtxs
  • app/app.go: Hot path uses NewDBImplWithoutSnapshot

Benchmark Results (M4 Max, 1000 EVM transfers/block, 30s profile)

Metric Before (after #2808) After Delta
TPS (steady-state range) 7,800–9,000 7,200–8,600 median ~8,000
DBImpl.Snapshot alloc_space 44.8 GB cum 19.5 GB cum -9.8 GB flat
cachemulti.newStoreWithoutGiga alloc 22.5 GB 12.0 GB -10.5 GB
newCacheMultiStoreFromCMS alloc (cum) 38.8 GB 14.2 GB cum -17.9 GB cum
Total alloc_space 457 GB 313 GB -144 GB (-31%)
Idle CPU (usleep+kevent+pthread_cond_wait) 42.9s 30.1s -30% idle
memclrNoHeapPointers CPU 2.1s 10.7s +8.6s (more fresh spans)

Note: TPS is flat despite -31% alloc because freed CPU shifts from idle to allocation overhead (memclrNoHeapPointers). Workers complete faster but the GC/allocator remains the bottleneck.

pprof -alloc_space -diff_base highlights

 -9,840 MB  DBImpl.Snapshot              (direct savings — one fewer CMS per tx)
-10,482 MB  cachemulti.newStoreWithoutGiga (cascading)
 -5,807 MB  btree.NewFreeListG            (cascading)
 -6,992 MB  sync.newIndirectNode          (cascading)
 -4,622 MB  sync.newEntryNode             (cascading)
 -4,194 MB  sei-db utils.Clone            (cascading)

pprof -top -diff_base highlights (CPU)

 +8.60s  runtime.memclrNoHeapPointers  (more fresh spans allocated)
 -8.07s  runtime.usleep                (less idle — workers busier)
 -2.51s  runtime.kevent                (less idle)
 -2.22s  runtime.pthread_cond_wait     (less contention)
 +2.50s  runtime.heapBitsSmallForAddr  (more heap metadata tracking)

Why safe

  • In the OCC path, core.ApplyMessageStateTransition.Execute always calls Prepare()Snapshot() before any EVM state reads/writes
  • GetCommittedState uses snapshottedCtxs[0] which is set by Prepare's Snapshot() — same committed state baseline
  • All other NewDBImpl callers (tests, RPC, ante) keep the initial Snapshot() for isolation
  • Defensive guards prevent panics if snapshottedCtxs is empty

Test plan

  • go test ./giga/deps/xevm/state/... -count=1 — pass (including TestSnapshot)
  • go test ./giga/tests/... -count=1 — pass (all 14 giga tests)
  • gofmt -s -w clean
  • Benchmark 2000+ blocks with heap profiling

🤖 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 11, 2026, 1:20 PM

@codecov
Copy link

codecov bot commented Feb 6, 2026

Codecov Report

❌ Patch coverage is 75.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 56.78%. Comparing base (f1c3021) to head (08c4efe).
⚠️ Report is 6 commits behind head on perf/lazy-cachemultistore.

Files with missing lines Patch % Lines
giga/deps/xevm/state/state.go 0.00% 1 Missing and 1 partial ⚠️
giga/deps/xevm/state/statedb.go 84.61% 1 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@                      Coverage Diff                      @@
##           perf/lazy-cachemultistore    #2819      +/-   ##
=============================================================
+ Coverage                      56.77%   56.78%   +0.01%     
=============================================================
  Files                           2070     2070              
  Lines                         168336   168351      +15     
=============================================================
+ Hits                           95572    95600      +28     
+ Misses                         64280    64259      -21     
- Partials                        8484     8492       +8     
Flag Coverage Δ
sei-chain 40.89% <75.00%> (+0.01%) ⬆️
sei-cosmos 48.17% <ø> (+<0.01%) ⬆️
sei-db 68.72% <ø> (ø)
sei-tendermint 58.68% <ø> (-0.04%) ⬇️

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

Files with missing lines Coverage Δ
app/app.go 65.88% <100.00%> (+0.07%) ⬆️
giga/deps/xevm/state/state.go 95.12% <0.00%> (-1.58%) ⬇️
giga/deps/xevm/state/statedb.go 43.82% <84.61%> (+3.55%) ⬆️

... and 32 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.

@pdrobnjak pdrobnjak marked this pull request as draft February 6, 2026 15:25
@pdrobnjak pdrobnjak force-pushed the perf/cache-gaskv-context branch from b620ffb to 13f343e Compare February 6, 2026 17:19
@pdrobnjak pdrobnjak force-pushed the perf/skip-redundant-snapshot branch 3 times, most recently from e91f6e4 to ae6bbe8 Compare February 6, 2026 18:41
In the OCC executor path, each EVM transaction creates 3 CacheMultiStore
layers:

  1. prepareTask: CMS with VersionIndexedStore wrappers (OCC isolation)
  2. NewDBImpl: Snapshot() creates CMS wrapping #1 (GetCommittedState baseline)
  3. Prepare: Snapshot() creates CMS wrapping #2 (EVM revert support)

Layer #2 is redundant because no state changes occur between NewDBImpl
and Prepare (called by go-ethereum's StateTransition.Execute). Prepare's
Snapshot provides the same GetCommittedState baseline.

Add NewDBImplWithoutSnapshot() that skips the initial Snapshot, used only
in the OCC hot path (app.go executeEVMTxWithGigaExecutor). The original
NewDBImpl is preserved for all other callers (tests, RPC, ante handlers)
that may write state before the first explicit Snapshot.

Also add defensive guards on GetCommittedState and flushEvents for the
case where snapshottedCtxs is empty.

Results (M4 Max benchmark, diff vs previous):
- DBImpl.Snapshot alloc: -9.8 GB
- cachemulti.newStoreWithoutGiga: -10.5 GB
- Total alloc_space: 457 GB → 313 GB (-31%)
- Idle CPU (usleep+kevent): 42.9s → 30.1s (-30%, workers busier)
- TPS steady-state: 7,200-8,600 (median ~8,000)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@pdrobnjak pdrobnjak force-pushed the perf/cache-gaskv-context branch from de7b59d to f1c3021 Compare February 11, 2026 13:19
@pdrobnjak pdrobnjak force-pushed the perf/skip-redundant-snapshot branch from ae6bbe8 to 08c4efe Compare February 11, 2026 13:19
Base automatically changed from perf/cache-gaskv-context to perf/lazy-cachemultistore February 11, 2026 13:19
@pdrobnjak pdrobnjak merged commit 08c4efe into perf/lazy-cachemultistore Feb 11, 2026
@pdrobnjak pdrobnjak deleted the perf/skip-redundant-snapshot branch February 11, 2026 13:19
@pdrobnjak pdrobnjak restored the perf/skip-redundant-snapshot branch February 11, 2026 13:22
@pdrobnjak
Copy link
Contributor Author

This PR was auto-merged by GitHub during a stack reorder (branch force-push made head appear merged into base). No code was actually merged to main. This PR has been superseded — see the restacked PR chain starting at #2849.

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