Skip to content

feat(btreemap): allow choosing the page size of a new StableBTreeMap - #442

Open
hpeebles wants to merge 1 commit into
dfinity:mainfrom
hpeebles:btreemap-page-size
Open

feat(btreemap): allow choosing the page size of a new StableBTreeMap#442
hpeebles wants to merge 1 commit into
dfinity:mainfrom
hpeebles:btreemap-page-size

Conversation

@hpeebles

Copy link
Copy Markdown
Contributor

Within OpenChat we have a a few large maps where the entries are unbounded but are ~20 bytes the vast majority of the time. A map whose keys or values are unbounded stores its nodes in 1024-byte pages. When entries are typically small that wastes most of each page: a full node of ~20-byte entries needs ~420 bytes, and most nodes aren't full. Inserting 100k such entries uses 13.4 MiB with the default page size, against 5.2 MiB (-61%) with 384-byte pages, for ~0.2% more stable memory reads per get.

This PR add BTreeMap::new_with_page_size and BTreeMap::init_with_page_size so that users can pick a page size to fit their entries. Nodes that outgrow their page already continue into overflow pages, so entries of any size remain supported at any page size.

The V2 header already stores an arbitrary page size, which load reads back. The page size is fixed once a map is created, so init_with_page_size ignores its argument when loading an existing map. Page sizes below the existing 128-byte minimum are rejected up front with a clear message.

New proptest variants cover random operations and memory leaks with 128-byte pages, where nodes routinely spill into overflow pages.

A map whose keys or values are unbounded stores its nodes in 1024-byte
pages. When entries are typically small that wastes most of each page:
a full node of ~20-byte entries (key and value combined) needs ~420
bytes, and most nodes aren't full. Inserting 100k such entries uses
13.4 MiB with the default page size, against 5.2 MiB (-61%) with
384-byte pages, for ~0.2% more stable memory reads per `get`.

Add `BTreeMap::new_with_page_size` and `BTreeMap::init_with_page_size`
so that users can pick a page size to fit their entries. Nodes that
outgrow their page already continue into overflow pages, so entries of
any size remain supported at any page size.

No change to the memory layout is needed: the V2 header already stores
an arbitrary page size, which `load` reads back. The page size is fixed
once a map is created, so `init_with_page_size` ignores its argument
when loading an existing map. Page sizes below the existing 128-byte
minimum are rejected up front with a clear message.

New proptest variants cover random operations and memory leaks with
128-byte pages, where nodes routinely spill into overflow pages.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@hpeebles
hpeebles requested a review from a team as a code owner September 11, 2026 09:15
@github-actions

Copy link
Copy Markdown

canbench 🏋 (dir: ./benchmarks/io_chunks) 7afa8db 2026-09-11 09:23:27 UTC

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

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

Summary:
  instructions:
    status:   No significant changes 👍
    counts:   [total 18 | regressed 0 | improved 0 | new 0 | unchanged 18]
    change:   [max +34.00M | p75 +56 | median 0 | p25 0 | min 0]
    change %: [max +0.73% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  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%]

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

@github-actions

Copy link
Copy Markdown

canbench 🏋 (dir: ./benchmarks/nns) 7afa8db 2026-09-11 09:22:53 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 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
Copy Markdown

canbench 🏋 (dir: ./benchmarks/btreemap) 7afa8db 2026-09-11 09:24:08 UTC

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

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

Summary:
  instructions:
    status:   No significant changes 👍
    counts:   [total 252 | regressed 0 | improved 0 | new 0 | unchanged 252]
    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 252 | regressed 0 | improved 0 | new 0 | unchanged 252]
    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 252 | regressed 0 | improved 0 | new 0 | unchanged 252]
    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
Copy Markdown

canbench 🏋 (dir: ./benchmarks/btreeset) 7afa8db 2026-09-11 09:23:01 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 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 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%]

  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%]

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

@github-actions

Copy link
Copy Markdown

canbench 🏋 (dir: ./benchmarks/vec) 7afa8db 2026-09-11 09:22:50 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
Copy Markdown

canbench 🏋 (dir: ./benchmarks/memory_manager) 7afa8db 2026-09-11 09:22:55 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

Comment thread src/btreemap.rs
@hpeebles

Copy link
Copy Markdown
Contributor Author

Claude just did some analysis of where we should use custom page sizes which really highlights the improvements -

Screenshot 2026-09-11 at 11 11 13

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.

2 participants