erigontech / erigontech/erigon

execution/cache: avoid rebuilding cache views on direct reads

Open
#23,145 0 comments 0 reactions 1 assignee Claimed by @yperbasis View on GitHub
performance
Dominant language
Go
Stars
3.6k
Forks
1.5k
Avg merge
1d 16h
Merged PRs (30d)
455

Description

Follow-up to #23095. This is an optimization; it is not required for the correctness of that PR.

## Problem

#23095 binds each cache view to the database state and immutable-files view pinned by its transaction. Most hot callers already construct a `TemporalGetter` once and reuse its bound views, but some direct read paths still repeat view construction:

- `asOfStateReader` calls `SharedDomains.GetLatest` for every commitment-domain read.
- `SharedDomains.GetLatest` and `GetLatestContext` derive both cache generations even when the requested domain can use only one cache.
- View construction inspects the transaction even when no relevant cache is attached.
- The single-domain calls to variadic `TxNumsInFiles` allocate when made through `TemporalDebugTx`.

The work is small, but it is paid on reads whose cache-hit path is otherwise about 50 ns.

## Proposed change

- Bind one `TemporalGetter` when constructing an `asOfStateReader`. Reuse it when only `txNum` changes, and bind a new getter when a clone receives a different transaction.
- Return immediately when no relevant cache is attached.
- For direct reads, derive only the state-cache or branch-cache view needed by the requested domain. Code helpers need only the state-cache view.
- Consider a clearly named, non-variadic accessor for one pinned domain-files end, so generation construction does not allocate through the debug interface.
- Keep a benchmark that compares direct and bound cache hits.

## Correctness constraints

- A view must always be derived from the transaction that performs the read.
- A state-cache generation must still contain the Accounts, Storage, and Code file ends, even when the current read targets only one of those domains. The cache has one shared generation.
- A branch-cache generation uses the Commitment file end.
- Transactions without exact domain frontiers must remain ineligible for the relevant cache.
- Worker clones must preserve context-based metrics, and clones using another transaction must not reuse the source transaction's view.
- Publication and token-revocation semantics must remain unchanged.

## Prototype measurements

Indicative local results on darwin/arm64, Apple M2 Max:

| Path | Current #23095 | Prototype |
|---|---:|---:|
| Fresh getter construction | about 0.69 us, 96 B, 5 allocs | 0.34-0.42 us, 80 B, 1 alloc |
| Direct cache hit | about 111 ns, 0 allocs | 77-79 ns, 0 allocs |
| Bound-getter cache hit | about 49 ns, 0 allocs | about 49 ns, 0 allocs |
| No-cache unique miss | about 220 ns, 8 B, 4 allocs | about 116 ns, 0 allocs |
| Cache-backed unique miss | about 324 ns, 32 B, 5 allocs | about 295 ns, 24 B, 1 alloc |

The main expected benefit is that commitment reads through `asOfStateReader` use the bound-getter path. Before merging, repeat the microbenchmarks on amd64 and measure a representative commitment workload; the extra interface method is worthwhile only if the allocation reduction remains useful outside the microbenchmark.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.