Skip to content

perf: add direct-mapped node cache to BTreeMap#416

Open
sasa-tomic wants to merge 5 commits intomainfrom
perf/direct-mapped-node-cache
Open

perf: add direct-mapped node cache to BTreeMap#416
sasa-tomic wants to merge 5 commits intomainfrom
perf/direct-mapped-node-cache

Conversation

@sasa-tomic
Copy link
Contributor

@sasa-tomic sasa-tomic commented Mar 18, 2026

Summary

  • Add a 32-slot direct-mapped node cache to BTreeMap, modeled after CPU caches: O(1) lookup via (address / page_size) % 32, collision = eviction (no LRU tracking)
  • Read paths (get, contains_key, first/last_key_value) use a take+return pattern to avoid re-loading hot upper-tree nodes from stable memory
  • Write paths invalidate affected cache slots in save_node, deallocate_node, merge, and clear_new
  • Switch get() from destructive extract_entry_at (swap_remove) to non-destructive node.value() (borrows via OnceCell)
  • Remove now-unused extract_entry_at method

This subsumes all four previous caching approaches (root-only, LRU+clone, LRU+Rc, page cache) into a single design that:

  • Has ~5 instructions overhead per cache lookup (vs ~330 for the Rc LRU's linear scan)
  • Stores Node<K> directly (no Rc, no Clone, no heap allocation per cache entry)
  • Uses cache.get_mut() on write paths (zero RefCell overhead)

Expected improvement: ~15-20% for random reads, ~65% for hot-key workloads, ~0% overhead for writes.

Add a 32-slot direct-mapped node cache to BTreeMap that avoids
re-loading hot nodes from stable memory. Modeled after CPU caches:
O(1) lookup via (address / page_size) % 32, collision = eviction.

Read paths (get, contains_key, first/last_key_value) use a
take+return pattern to borrow nodes from the cache without
RefCell lifetime issues. Write paths (insert, remove, split,
merge) invalidate affected cache slots.

Key changes:
- Switch get() from destructive extract_entry_at to node.value()
- Remove unused extract_entry_at method
- Change traverse() closure from Fn(&mut Node) to Fn(&Node)
- Invalidate cache in save_node, deallocate_node, merge, clear_new

Expected improvement: ~15-20% for random reads, ~65% for hot-key
workloads, ~0% overhead for writes (cache.get_mut() bypasses RefCell).
@sasa-tomic sasa-tomic requested a review from a team as a code owner March 18, 2026 17:46
@sasa-tomic sasa-tomic marked this pull request as draft March 18, 2026 17:52
@github-actions
Copy link

github-actions bot commented Mar 19, 2026

canbench 🏋 (dir: ./benchmarks/btreeset) fbf763d 2026-03-20 11:20:45 UTC

./benchmarks/btreeset/canbench_results.yml is up to date
📦 canbench_results_btreeset.csv available in artifacts

---------------------------------------------------

Summary:
  instructions:
    status:   No significant changes 👍
    counts:   [total 100 | regressed 0 | improved 0 | new 0 | unchanged 100]
    change:   [max +886.54K | p75 +2 | median 0 | p25 0 | min 0]
    change %: [max +0.16% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  heap_increase:
    status:   Regressions detected 🔴
    counts:   [total 100 | regressed 1 | improved 0 | new 0 | unchanged 99]
    change:   [max +1 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max +inf% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  stable_memory_increase:
    status:   No significant changes 👍
    counts:   [total 100 | regressed 0 | improved 0 | new 0 | unchanged 100]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

---------------------------------------------------

Only significant changes:
| status | name                     | calls |   ins |  ins Δ% | HI |  HI Δ% | SMI |  SMI Δ% |
|--------|--------------------------|-------|-------|---------|----|--------|-----|---------|
|   +    | btreeset_remove_blob_512 |       | 4.26B |  +0.02% |  1 |  +inf% |   0 |   0.00% |

ins = instructions, HI = heap_increase, SMI = stable_memory_increase, Δ% = percent change

---------------------------------------------------
CSV results saved to canbench_results.csv

@github-actions
Copy link

github-actions bot commented Mar 19, 2026

canbench 🏋 (dir: ./benchmarks/nns) fbf763d 2026-03-20 11:20:43 UTC

./benchmarks/nns/canbench_results.yml is up to date
📦 canbench_results_nns.csv available in artifacts

---------------------------------------------------

Summary:
  instructions:
    status:   No significant changes 👍
    counts:   [total 16 | regressed 0 | improved 0 | new 0 | unchanged 16]
    change:   [max +361.55K | p75 0 | median 0 | p25 -610.45K | min -19.63M]
    change %: [max +0.04% | p75 0.00% | median 0.00% | p25 -0.22% | min -0.72%]

  heap_increase:
    status:   No significant changes 👍
    counts:   [total 16 | regressed 0 | improved 0 | new 0 | unchanged 16]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  stable_memory_increase:
    status:   No significant changes 👍
    counts:   [total 16 | regressed 0 | improved 0 | new 0 | unchanged 16]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

---------------------------------------------------
CSV results saved to canbench_results.csv

@github-actions
Copy link

github-actions bot commented Mar 19, 2026

canbench 🏋 (dir: ./benchmarks/vec) fbf763d 2026-03-20 11:20:30 UTC

./benchmarks/vec/canbench_results.yml is up to date
📦 canbench_results_vec.csv available in artifacts

---------------------------------------------------

Summary:
  instructions:
    status:   No significant changes 👍
    counts:   [total 16 | regressed 0 | improved 0 | new 0 | unchanged 16]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  heap_increase:
    status:   No significant changes 👍
    counts:   [total 16 | regressed 0 | improved 0 | new 0 | unchanged 16]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  stable_memory_increase:
    status:   No significant changes 👍
    counts:   [total 16 | regressed 0 | improved 0 | new 0 | unchanged 16]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

---------------------------------------------------
CSV results saved to canbench_results.csv

@github-actions
Copy link

github-actions bot commented Mar 19, 2026

canbench 🏋 (dir: ./benchmarks/memory_manager) fbf763d 2026-03-20 11:20:30 UTC

./benchmarks/memory_manager/canbench_results.yml is up to date
📦 canbench_results_memory-manager.csv available in artifacts

---------------------------------------------------

Summary:
  instructions:
    status:   No significant changes 👍
    counts:   [total 3 | regressed 0 | improved 0 | new 0 | unchanged 3]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  heap_increase:
    status:   No significant changes 👍
    counts:   [total 3 | regressed 0 | improved 0 | new 0 | unchanged 3]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  stable_memory_increase:
    status:   No significant changes 👍
    counts:   [total 3 | regressed 0 | improved 0 | new 0 | unchanged 3]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

---------------------------------------------------
CSV results saved to canbench_results.csv

@github-actions
Copy link

github-actions bot commented Mar 20, 2026

canbench 🏋 (dir: ./benchmarks/io_chunks) fbf763d 2026-03-20 11:21:16 UTC

./benchmarks/io_chunks/canbench_results.yml is up to date
📦 canbench_results_io_chunks.csv available in artifacts

---------------------------------------------------

Summary:
  instructions:
    status:   Improvements detected 🟢
    counts:   [total 18 | regressed 0 | improved 2 | new 0 | unchanged 16]
    change:   [max +64.87M | p75 0 | median 0 | p25 -226 | min -28.81B]
    change %: [max +0.73% | p75 0.00% | median 0.00% | p25 -0.00% | min -70.38%]

  heap_increase:
    status:   No significant changes 👍
    counts:   [total 18 | regressed 0 | improved 0 | new 0 | unchanged 18]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  stable_memory_increase:
    status:   No significant changes 👍
    counts:   [total 18 | regressed 0 | improved 0 | new 0 | unchanged 18]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

---------------------------------------------------

Only significant changes:
| status | name                    | calls |     ins |  ins Δ% | HI |  HI Δ% | SMI |  SMI Δ% |
|--------|-------------------------|-------|---------|---------|----|--------|-----|---------|
|   -    | read_chunks_btreemap_1k |       | 203.41M | -59.17% |  0 |  0.00% |   0 |   0.00% |
|   -    | read_chunks_btreemap_1m |       |  12.13B | -70.38% |  0 |  0.00% |   0 |   0.00% |

ins = instructions, HI = heap_increase, SMI = stable_memory_increase, Δ% = percent change

---------------------------------------------------
CSV results saved to canbench_results.csv

@sasa-tomic sasa-tomic marked this pull request as ready for review March 20, 2026 11:23
@github-actions
Copy link

canbench 🏋 (dir: ./benchmarks/btreemap) fbf763d 2026-03-20 11:22:29 UTC

./benchmarks/btreemap/canbench_results.yml is up to date
📦 canbench_results_btreemap.csv available in artifacts

---------------------------------------------------

Summary:
  instructions:
    status:   Improvements detected 🟢
    counts:   [total 303 | regressed 0 | improved 96 | new 0 | unchanged 207]
    change:   [max +1.83M | p75 +987.79K | median +121.47K | p25 -82.33M | min -1.51B]
    change %: [max +0.25% | p75 +0.10% | median +0.01% | p25 -25.00% | min -87.00%]

  heap_increase:
    status:   Regressions detected 🔴
    counts:   [total 303 | regressed 3 | improved 0 | new 0 | unchanged 300]
    change:   [max +1 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max +inf% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  stable_memory_increase:
    status:   No significant changes 👍
    counts:   [total 303 | regressed 0 | improved 0 | new 0 | unchanged 303]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

---------------------------------------------------

Only significant changes:
| status | name                                        | calls |     ins |  ins Δ% | HI |  HI Δ% | SMI |  SMI Δ% |
|--------|---------------------------------------------|-------|---------|---------|----|--------|-----|---------|
|   +    | btreemap_v2_insert_vec_4_128                |       | 604.86M |  +0.08% |  1 |  +inf% |  16 |   0.00% |
|   +    | btreemap_v2_insert_vec_8_128                |       | 665.53M |  -0.11% |  1 |  +inf% |  23 |   0.00% |
|   +    | btreemap_v2_insert_vec_32_0                 |       | 621.09M |  -0.12% |  1 |  +inf% |  20 |   0.00% |
|   -    | btreemap_v2_contains_blob_64_128            |       | 335.67M | -16.76% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_vec_512_128                 |       |   1.04B | -17.35% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_vec_512_128            |       |   1.03B | -17.38% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_vec_32_16              |       | 299.01M | -18.70% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_vec_32_16                   |       | 303.37M | -18.82% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_blob_64_128                 |       | 340.70M | -20.53% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_vec_32_0               |       | 279.01M | -21.48% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_vec_32_0                    |       | 280.84M | -21.60% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_blob_32_512            |       | 251.69M | -21.75% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_blob_32_16             |       | 248.24M | -22.14% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_vec_1024_128           |       |   1.41B | -22.35% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_vec_32_32              |       | 274.24M | -22.81% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_vec_32_32                   |       | 278.81M | -22.84% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_mem_manager_contains_vec512_u64 |       | 922.48M | -23.21% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_blob_32_1024           |       | 250.00M | -23.33% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_blob_32_16                  |       | 252.47M | -23.64% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_blob_32_512                 |       | 260.11M | -24.10% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_vec_1024_128                |       |   1.42B | -24.27% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_blob_32_256            |       | 244.88M | -24.43% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_blob_32_128            |       | 245.03M | -24.89% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_u64_blob8              |       | 164.48M | -25.11% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_mem_manager_get_vec512_u64      |       | 930.15M | -25.13% |  0 |  0.00% |   0 |   0.00% |
|  ...   | ... 49 rows omitted ...                     |       |         |         |    |        |     |         |
|   -    | btreemap_v2_get_blob_16_128                 |       | 213.18M | -30.89% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_vec_32_64              |       | 274.93M | -30.99% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_vec_64_128                  |       | 345.75M | -31.08% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_blob_1024_128          |       |   2.94B | -31.17% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_vec_64_128             |       | 338.67M | -31.33% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_10mib_values                |       | 264.85M | -31.84% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_vec_32_128                  |       | 296.35M | -32.03% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_mem_manager_get_blob512_u64     |       |   1.67B | -32.13% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_blob_8_128             |       | 181.44M | -32.32% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_vec_32_128             |       | 289.85M | -32.33% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_blob8_u64              |       | 187.13M | -32.48% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_vec_8_128                   |       | 272.14M | -32.55% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_vec_8_128              |       | 265.94M | -32.95% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_blob_8_128                  |       | 186.61M | -33.29% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_blob_1024_128               |       |   2.95B | -33.83% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_blob8_u64                   |       | 191.79M | -35.98% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_vec8_u64                    |       | 242.54M | -36.11% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_vec8_u64               |       | 235.57M | -37.03% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_vec_16_128                  |       | 276.49M | -38.52% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_vec_16_128             |       | 270.17M | -38.99% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_blob_4_128             |       | 140.69M | -42.31% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_blob_4_128                  |       | 145.90M | -42.76% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_get_vec_4_128                   |       | 194.39M | -52.72% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_vec_4_128              |       | 188.22M | -53.44% |  0 |  0.00% |   0 |   0.00% |
|   -    | btreemap_v2_contains_10mib_values           |       |  18.48M | -87.00% |  0 |  0.00% |   0 |   0.00% |

ins = instructions, HI = heap_increase, SMI = stable_memory_increase, Δ% = percent change

---------------------------------------------------
CSV results saved to canbench_results.csv

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.

1 participant