snix/snix/eval/benches/eval.rs
Starnick4444 e97cf628a3 refactor(eval): switch NixAttrs implementation to HashMap
Using hashmap seems to give a decent speedup overall.

hello outpath           time:   [528.01 ms 529.17 ms 530.64 ms]
                        change: [-22.932% -22.563% -22.181%] (p = 0.00 < 0.05)
                        Performance has improved.

firefox outpath         time:   [4.7647 s 4.8149 s 4.8917 s]
                        change: [-21.251% -20.408% -18.914%] (p = 0.00 < 0.05)
                        Performance has improved.

But it slows down derivation parsing by about 1-1.5%
Added another attr merge benchmark that helped me while profiling,
not sure if we want to keep that.

Change-Id: Icb9f1e2d40bbb7150af1b8df192bf3c860bae79b
Reviewed-on: https://cl.snix.dev/c/snix/+/30309
Tested-by: besadii
Reviewed-by: Florian Klink <flokli@flokli.de>
2025-05-07 12:08:50 +00:00

108 lines
2.9 KiB
Rust

use criterion::{black_box, criterion_group, criterion_main, Criterion};
use itertools::Itertools;
use mimalloc::MiMalloc;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
fn interpret(code: &str) {
snix_eval::Evaluation::builder_pure()
.build()
.evaluate(code, None);
}
fn eval_literals(c: &mut Criterion) {
c.bench_function("int", |b| {
b.iter(|| {
interpret(black_box("42"));
})
});
}
fn eval_merge_attrs(c: &mut Criterion) {
c.bench_function("merge small attrs", |b| {
b.iter(|| {
interpret(black_box("{ a = 1; b = 2; } // { c = 3; }"));
})
});
c.bench_function("merge large attrs with small attrs", |b| {
let large_attrs = format!(
"{{{}}}",
(0..10000).map(|n| format!("a{n} = {n};")).join(" ")
);
let expr = format!("{large_attrs} // {{ c = 3; }}");
b.iter(move || {
interpret(black_box(&expr));
})
});
c.bench_function("merge small attrs with large attrs", |b| {
let large_attrs = format!(
"{{{}}}",
(0..10000).map(|n| format!("a{n} = {n};")).join(" ")
);
let expr = format!("{{ c = 3 }} // {large_attrs}");
b.iter(move || {
interpret(black_box(&expr));
})
});
}
fn eval_intersect_attrs(c: &mut Criterion) {
c.bench_function("intersect small attrs", |b| {
b.iter(|| {
interpret(black_box(
"builtins.intersectAttrs { a = 1; b = 2; } { c = 3; }",
));
})
});
c.bench_function("intersect large attrs with small attrs", |b| {
let large_attrs = format!(
"{{{}}}",
(0..10000).map(|n| format!("a{n} = {n};")).join(" ")
);
let expr = format!("builtins.intersectAttrs {large_attrs} {{ c = 3; }}");
b.iter(move || {
interpret(black_box(&expr));
})
});
c.bench_function("intersect large attrs with large attrs", |b| {
// the intersection is 2n=3m, which is about ~⅓ of the union, and ~¼ of the elements < 2*1e4
let left_attrs = format!(
"{{{}}}",
(0..10000)
.map(|n| {
let i = 2 * n;
format!("a{i} = {i};")
})
.join(" ")
);
let right_attrs = format!(
"{{{}}}",
(0..10000)
.map(|m| {
let j = 3 * m;
format!("a{j} = {j};")
})
.join(" ")
);
let expr = format!("builtins.intersectAttrs {left_attrs} {right_attrs}");
b.iter(move || {
interpret(black_box(&expr));
})
});
}
criterion_group!(
benches,
eval_literals,
eval_merge_attrs,
eval_intersect_attrs
);
criterion_main!(benches);