Skip to content

Commit eafe317

Browse files
authored
feat: add btreemap example (#836)
Add BTreeMap workload with complex memory access patterns (insertions, deletions, range scans) for profiling.
1 parent 0369981 commit eafe317

File tree

8 files changed

+215
-10
lines changed

8 files changed

+215
-10
lines changed

Cargo.lock

Lines changed: 71 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ members = [
2828
"jolt-sdk",
2929
"jolt-sdk/macros",
3030
"jolt-verifier",
31+
"examples/btreemap/host",
32+
"examples/btreemap/guest",
3133
"examples/collatz",
3234
"examples/collatz/guest",
3335
"examples/fibonacci",
@@ -104,3 +106,6 @@ ark-bn254 = { git = "https://github.com/a16z/arkworks-algebra", branch = "dev/tw
104106

105107
[workspace.metadata.cargo-machete]
106108
ignored = ["jolt-sdk"]
109+
110+
[workspace.dependencies]
111+
spinners = { version= "4.1.1", default-features = false }

examples/btreemap/guest/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "btreemap-guest"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[features]
7+
guest = []
8+
9+
[dependencies]
10+
jolt = { package = "jolt-sdk", path = "../../../jolt-sdk" }

examples/btreemap/guest/src/lib.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#![cfg_attr(feature = "guest", no_std)]
2+
3+
extern crate alloc;
4+
5+
use core::option::Option::None;
6+
7+
/// Fast hash function implementation using wyhash64 algorithm.
8+
///
9+
/// This function provides a high-quality, fast hash suitable for hashmap operations.
10+
/// It uses the wyhash64 algorithm which offers good distribution properties and
11+
/// performance characteristics.
12+
fn wyhash64(mut x: u64) -> u64 {
13+
x ^= x >> 32;
14+
x = x.wrapping_mul(0xd6e8feb86659fd93);
15+
x ^= x >> 32;
16+
x = x.wrapping_mul(0xd6e8feb86659fd93);
17+
x
18+
}
19+
20+
#[jolt::provable(stack_size = 10000, memory_size = 10000000)]
21+
pub fn btreemap(n: u32) -> u128 {
22+
use alloc::collections::BTreeMap;
23+
24+
let mut map = BTreeMap::new();
25+
let mut inserted_keys = alloc::vec::Vec::with_capacity(n as usize);
26+
27+
// Phase 1: Insert N entries with high-entropy keys
28+
for i in 0..n {
29+
let key = wyhash64(i as u64); // Use u64 directly, not usize
30+
inserted_keys.push(key);
31+
map.insert(key, i as u64);
32+
}
33+
34+
// Phase 2: Delete 25% of the inserted keys to trigger rebalancing
35+
let delete_count = n / 4;
36+
for i in 0..delete_count {
37+
let key = inserted_keys[i as usize];
38+
map.remove(&key);
39+
}
40+
41+
// Phase 3: Insert N/2 new entries with new hashed keys
42+
for i in 0..(n / 2) {
43+
let key = wyhash64((i + n * 2) as u64); // Non-overlapping seed
44+
map.insert(key, (i + n) as u64);
45+
}
46+
47+
// Phase 4: Range scan over middle 25% of key space
48+
let mut range_sum = 0u64;
49+
if let Some((&min_key, _)) = map.first_key_value() {
50+
if let Some((&max_key, _)) = map.last_key_value() {
51+
let range_size = (max_key - min_key) / 4;
52+
let start = min_key + range_size;
53+
let end = start + range_size;
54+
55+
for (_, value) in map.range(start..end) {
56+
range_sum = range_sum.wrapping_add(*value);
57+
}
58+
}
59+
}
60+
61+
// Combine size and range sum into a single return value
62+
(map.len() as u128).wrapping_add(range_sum as u128)
63+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#![cfg_attr(feature = "guest", no_std)]
2+
#![no_main]
3+
4+
#[allow(unused_imports)]
5+
use btreemap_guest::*;

examples/btreemap/host/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "btreemap"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
jolt-sdk = { path = "../../../jolt-sdk", features = ["host"] }
8+
guest = { package = "btreemap-guest", path = "../guest" }
9+
spinners = { workspace = true }

examples/btreemap/host/src/main.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
use spinners::{Spinner, Spinners};
2+
3+
macro_rules! step {
4+
($msg:expr, $action:expr) => {{
5+
let mut sp = Spinner::new(Spinners::Dots9, $msg.to_string());
6+
let result = $action;
7+
sp.stop_with_message(format!("✓ {}", $msg));
8+
result
9+
}};
10+
}
11+
12+
pub fn btreemap() {
13+
let target_dir = "/tmp/jolt-guest-targets";
14+
15+
let mut program = step!("Compiling guest code", {
16+
guest::compile_btreemap(target_dir)
17+
});
18+
19+
let prover_preprocessing = step!("Preprocessing prover", {
20+
guest::preprocess_prover_btreemap(&mut program)
21+
});
22+
23+
let verifier_preprocessing = step!("Preprocessing verifier", {
24+
guest::verifier_preprocessing_from_prover_btreemap(&prover_preprocessing)
25+
});
26+
27+
let prove = step!("Building prover", {
28+
guest::build_prover_btreemap(program, prover_preprocessing)
29+
});
30+
31+
let verify = step!("Building verifier", {
32+
guest::build_verifier_btreemap(verifier_preprocessing)
33+
});
34+
35+
let n = 50;
36+
let (output, proof, io_device) = step!("Proving", { prove(n) });
37+
assert!(output >= 1);
38+
39+
let is_valid = step!("Verifying", { verify(n, output, io_device.panic,proof) });
40+
assert!(is_valid);
41+
}
42+
43+
fn main() {
44+
println!("BTreeMap");
45+
btreemap();
46+
}

jolt-core/src/benches/bench.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rand_distr::{Distribution, Zipf};
1212

1313
#[derive(Debug, Copy, Clone, clap::ValueEnum)]
1414
pub enum BenchType {
15+
Btreemap,
1516
Fibonacci,
1617
Sha2,
1718
Sha3,
@@ -22,6 +23,7 @@ pub enum BenchType {
2223

2324
pub fn benchmarks(bench_type: BenchType) -> Vec<(tracing::Span, Box<dyn FnOnce()>)> {
2425
match bench_type {
26+
BenchType::Btreemap => btreemap(),
2527
BenchType::Sha2 => sha2(),
2628
BenchType::Sha3 => sha3(),
2729
BenchType::Sha2Chain => sha2_chain(),
@@ -115,6 +117,10 @@ fn sha3() -> Vec<(tracing::Span, Box<dyn FnOnce()>)> {
115117
prove_example("sha3-guest", postcard::to_stdvec(&vec![5u8; 2048]).unwrap())
116118
}
117119

120+
fn btreemap() -> Vec<(tracing::Span, Box<dyn FnOnce()>)> {
121+
prove_example("btreemap-guest", postcard::to_stdvec(&50u32).unwrap())
122+
}
123+
118124
fn sha2_chain() -> Vec<(tracing::Span, Box<dyn FnOnce()>)> {
119125
let mut inputs = vec![];
120126
inputs.append(&mut postcard::to_stdvec(&[5u8; 32]).unwrap());

0 commit comments

Comments
 (0)