Skip to content

perf: Use O(1) ring buffer cache for block hashes instead of BTreeMap#3299

Merged
rakita merged 4 commits intobluealloy:mainfrom
malik672:ife
Jan 15, 2026
Merged

perf: Use O(1) ring buffer cache for block hashes instead of BTreeMap#3299
rakita merged 4 commits intobluealloy:mainfrom
malik672:ife

Conversation

@malik672
Copy link
Contributor

Replaces the BTreeMap<u64, B256> used for block hash caching in State with a new BlockHashCache struct that implements a fixed-size ring buffer(Technically anyway)

from benchmark:

Benchmark | Ring Buffer | BTreeMap | Speedup
-- | -- | -- | --
Insert 256 blocks | 382 ns | 4.35 µs | 11.4×
Insert 1000 continuous | 1.17 µs | 30.2 µs | 25.8×
Sequential get (256) | 93.8 ns | 1.58 µs | 16.8×
Get last 10 blocks | 6.37 ns | 82.1 ns | 12.9×
Get miss (256) | 94.3 ns | 2.60 µs | 27.6×
Single lookup | 1.11 ns | 10.2 ns | 9.2×
Mixed workload | 1.46 ns | 31.4 ns | 21.5×

@codspeed-hq
Copy link

codspeed-hq bot commented Jan 10, 2026

Merging this PR will degrade performance by 3.07%

❌ 1 regressed benchmark
✅ 172 untouched benchmarks

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
EXTCODEHASH_50 42.1 µs 43.5 µs -3.07%

Comparing malik672:ife (f6a3769) with main (50405a2)

Open in CodSpeed

@malik672
Copy link
Contributor Author

Merging this PR will not alter performance

✅ 173 untouched benchmarks


Comparing malik672:ife (0071d21) with main (a31fa98)

Open in CodSpeed

Fascinating should 100% move the needle

@rakita
Copy link
Member

rakita commented Jan 14, 2026

Fascinating should 100% move the needle

I dont thing benchmarks cover past block hashes so this is okay

Copy link
Member

@rakita rakita left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left few nits, but in general lgtm

@rakita
Copy link
Member

rakita commented Jan 14, 2026

do git reset --soft HEAD^^ and than git commit -m "bump" "git push origin HEAD:ife -f

@malik672
Copy link
Contributor Author

do git reset --soft HEAD^^ and than git commit -m "bump" "git push origin HEAD:ife -f

Fascinating

if let Some(entry) = self.block_hashes.get(&number) {
return Ok(*entry);
if let Some(entry) = self.block_hashes.get(number) {
return Ok(FixedBytes(*entry));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting artefact, can fix it later

Copy link
Member

@rakita rakita left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@rakita rakita merged commit 4b81144 into bluealloy:main Jan 15, 2026
30 of 31 checks passed
@github-actions github-actions bot mentioned this pull request Jan 15, 2026
bshastry added a commit to bshastry/revm that referenced this pull request Jan 16, 2026
The ring buffer cache introduced in bluealloy#3299 was initialized with
`(0, B256::ZERO)` for all entries. This caused `get(0)` to incorrectly
match the default entry and return `B256::ZERO` instead of `None`,
preventing the database fallback from being invoked.

This is a consensus bug for any chain at low block numbers (< 256)
where BLOCKHASH(0) should return the genesis block hash.

Fix: Use `Option<B256>` instead of `B256` to explicitly distinguish
between "not cached" (`None`) and "cached with value" (`Some(hash)`).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
bshastry added a commit to bshastry/revm that referenced this pull request Jan 16, 2026
The ring buffer cache introduced in bluealloy#3299 was initialized with
`(0, B256::ZERO)` for all entries. This caused `get(0)` to incorrectly
match the default entry and return `B256::ZERO` instead of `None`,
preventing the database fallback from being invoked.

This is a consensus bug for any chain at low block numbers (< 256)
where BLOCKHASH(0) should return the genesis block hash.

Fix: Use `Option<B256>` instead of `B256` to explicitly distinguish
between "not cached" (`None`) and "cached with value" (`Some(hash)`).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
rakita added a commit that referenced this pull request Jan 16, 2026
…3319)

* fix(database): BlockHashCache incorrectly returns zero for block 0

The ring buffer cache introduced in #3299 was initialized with
`(0, B256::ZERO)` for all entries. This caused `get(0)` to incorrectly
match the default entry and return `B256::ZERO` instead of `None`,
preventing the database fallback from being invoked.

This is a consensus bug for any chain at low block numbers (< 256)
where BLOCKHASH(0) should return the genesis block hash.

Fix: Use `Option<B256>` instead of `B256` to explicitly distinguish
between "not cached" (`None`) and "cached with value" (`Some(hash)`).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: use Option<u64> for block number in BlockHashCache

Address review feedback:
- Use Option<u64> for block number instead of Option<B256> for hash
- Keep const on insert and get functions

* style: simplify get() comparison

Use direct Option comparison as suggested in review.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: rakita <dragan0rakita@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants