Skip to content

Commit 8e9ecf9

Browse files
committed
utils: faster add_multilinears
1 parent ad244f1 commit 8e9ecf9

File tree

5 files changed

+120
-6
lines changed

5 files changed

+120
-6
lines changed

Cargo.lock

Lines changed: 1 addition & 0 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
@@ -102,6 +102,7 @@ p3-air.workspace = true
102102
[dev-dependencies]
103103
criterion = { version = "0.7", default-features = false, features = ["cargo_bench_support"] }
104104
rec_aggregation.workspace = true
105+
rayon.workspace = true
105106

106107
[[bench]]
107108
name = "poseidon2"
@@ -114,3 +115,7 @@ harness = false
114115
[[bench]]
115116
name = "xmss"
116117
harness = false
118+
119+
[[bench]]
120+
name = "multilinear_add"
121+
harness = false

benches/multilinear_add.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use std::hint::black_box;
2+
3+
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
4+
use p3_field::PrimeCharacteristicRing;
5+
use p3_koala_bear::KoalaBear;
6+
use rand::rngs::StdRng;
7+
use rand::{Rng, SeedableRng};
8+
use utils::add_multilinears;
9+
10+
type F = KoalaBear;
11+
12+
fn generate_random_multilinear(log_size: usize, seed: u64) -> Vec<F> {
13+
let mut rng = StdRng::seed_from_u64(seed);
14+
(0..1 << log_size).map(|_| rng.random()).collect()
15+
}
16+
17+
fn bench_add_multilinears(c: &mut Criterion) {
18+
let mut group = c.benchmark_group("add_multilinears_fn");
19+
20+
// Test different polynomial sizes: 2^10, 2^14, 2^18, 2^20
21+
let log_sizes = [10, 14, 18, 20];
22+
23+
for &log_size in &log_sizes {
24+
let size = 1 << log_size;
25+
let pol1 = generate_random_multilinear(log_size, 42);
26+
let pol2 = generate_random_multilinear(log_size, 43);
27+
28+
group.throughput(Throughput::Elements(size as u64));
29+
30+
// Benchmark optimized version
31+
group.bench_with_input(
32+
BenchmarkId::new("optimized", log_size),
33+
&(pol1.clone(), pol2.clone()),
34+
|b, (p1, p2)| {
35+
b.iter(|| {
36+
let result = add_multilinears(black_box(p1), black_box(p2));
37+
black_box(result);
38+
});
39+
},
40+
);
41+
}
42+
43+
group.finish();
44+
}
45+
46+
fn bench_add_multilinears_sparse(c: &mut Criterion) {
47+
let mut group = c.benchmark_group("add_multilinears_sparse");
48+
49+
let log_size = 16;
50+
let sparsity_levels = [0.1, 0.5, 0.9, 1.0]; // Fraction of non-zero elements
51+
52+
for &sparsity in &sparsity_levels {
53+
let mut rng = StdRng::seed_from_u64(42);
54+
let size = 1 << log_size;
55+
56+
let mut pol1 = vec![F::ZERO; size];
57+
let mut pol2 = vec![F::ZERO; size];
58+
59+
// Fill with random non-zero values according to sparsity
60+
let non_zero_count = (size as f64 * sparsity) as usize;
61+
for i in 0..non_zero_count {
62+
pol1[i] = rng.random();
63+
pol2[i] = rng.random();
64+
}
65+
66+
group.throughput(Throughput::Elements(size as u64));
67+
68+
group.bench_function(
69+
BenchmarkId::new("optimized", (sparsity * 100.0) as u32),
70+
|b| {
71+
b.iter(|| {
72+
let result = add_multilinears(black_box(&pol1), black_box(&pol2));
73+
black_box(result);
74+
});
75+
},
76+
);
77+
}
78+
79+
group.finish();
80+
}
81+
82+
criterion_group!(
83+
benches,
84+
bench_add_multilinears,
85+
bench_add_multilinears_sparse
86+
);
87+
criterion_main!(benches);

crates/lean_compiler/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub fn compile_program(program: &str) -> (Bytecode, BTreeMap<usize, String>) {
2424
let intermediate_bytecode = compile_to_intermediate_bytecode(simple_program).unwrap();
2525
// println!("Intermediate Bytecode:\n\n{}", intermediate_bytecode.to_string());
2626
let compiled = compile_to_low_level_bytecode(intermediate_bytecode).unwrap();
27-
println!("Compiled Program:\n\n{}", compiled.to_string());
27+
println!("Compiled Program:\n\n{compiled}");
2828
(compiled, function_locations)
2929
}
3030

crates/utils/src/multilinear.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -214,11 +214,32 @@ pub fn multilinear_eval_constants_at_right<F: Field>(limit: usize, point: &[F])
214214

215215
pub fn add_multilinears<F: Field>(pol1: &[F], pol2: &[F]) -> Vec<F> {
216216
assert_eq!(pol1.len(), pol2.len());
217-
let mut dst = pol1.to_vec();
218-
dst.par_iter_mut()
219-
.zip(pol2.par_iter())
220-
.for_each(|(a, b)| *a += *b);
221-
dst
217+
218+
if pol1.is_empty() {
219+
return Vec::new();
220+
}
221+
222+
let len = pol1.len();
223+
let mut result = Vec::with_capacity(len);
224+
#[allow(clippy::uninit_vec)]
225+
unsafe {
226+
result.set_len(len);
227+
}
228+
229+
result
230+
.par_iter_mut()
231+
.zip(pol1.par_iter().zip(pol2.par_iter()))
232+
.for_each(|(dst, (a, b))| {
233+
*dst = if a.is_zero() {
234+
*b
235+
} else if b.is_zero() {
236+
*a
237+
} else {
238+
*a + *b
239+
}
240+
});
241+
242+
result
222243
}
223244

224245
pub fn padd_with_zero_to_next_power_of_two<F: Field>(pol: &[F]) -> Vec<F> {

0 commit comments

Comments
 (0)