use std::hint::black_box;
use crate::test_support::{cmp_const, fed_view, map_of, relation_fixture, RelationFixture, TID};
use gnitz_expr::{CmpOp, LogicalProgram};
use gnitz_wire::{PkKeys, ReadBound, ReadSink, ReadSpec, SinkKind, TypeCode};
use gnitz_zset::repr::BatchBuilder;
use gnitz_zset::schema::{SchemaColumn, SchemaDescriptor};
fn schema(payload: usize) -> SchemaDescriptor {
let mut cols = vec![SchemaColumn::new(TypeCode::U64, false)];
cols.extend((0..payload).map(|_| SchemaColumn::new(TypeCode::I64, false)));
SchemaDescriptor::new(&cols, &[0])
}
fn fixture(rounds: u64, per_round: u64) -> RelationFixture {
let s = schema(3);
let kind = fed_view(1 << 32);
let mut r = relation_fixture(kind, s, &[], []);
for round in 2..2 + rounds {
let mut bb = BatchBuilder::new(&s);
for id in 0..per_round {
bb.begin_row(id as u128, 1);
bb.put_u64(id % 512);
bb.put_u64(round);
bb.put_u64(id);
bb.end_row();
}
r.ingest_at(TID, bb.finish(), Some(round), false).unwrap();
}
r
}
#[test]
#[ignore = "benchmark; run with --release --ignored --nocapture --test-threads=1"]
fn delta_read_bench() {
let counter = gnitz_foundation::perf::Counter::instructions();
let rows = |map| ReadSink { map, kind: SinkKind::Rows { cut: None } };
let slim = || map_of(LogicalProgram::copy_cols(&[3]), &schema(1));
let lt = |lit| cmp_const(CmpOp::Lt, 1, lit).to_blob_bytes();
let keys = |n: u64| {
let ks: Vec<[u8; 8]> = (0..n).map(|i| i.to_be_bytes()).collect();
ReadBound::PkSet(PkKeys::from_keys(8, ks.iter().map(|k| k.as_slice())))
};
let spec = |bound, predicate, sink| ReadSpec { bound, predicate, sink };
for (rounds, per_round) in [(1u64, 262144u64), (64, 4096), (4096, 64)] {
let r = fixture(rounds, per_round);
let band = rounds * per_round;
println!("--- {rounds} rounds of {per_round} rows");
let cells: Vec<(&str, ReadSpec, usize)> = vec![
("the view's rows", ReadSpec::all_rows(ReadBound::None), 3),
(
"c0 < 256, one row in two",
spec(ReadBound::None, lt(256), rows(None)),
3,
),
("c0 < 1, one row in 512", spec(ReadBound::None, lt(1), rows(None)), 3),
("one column", spec(ReadBound::None, vec![], rows(slim())), 1),
("one key", spec(keys(1), vec![], rows(None)), 3),
("64 keys", spec(keys(64), vec![], rows(None)), 3),
("64 keys, c0 < 256", spec(keys(64), lt(256), rows(None)), 3),
];
for (label, spec, payload) in cells {
let layout = schema(payload).layout_digest();
let run = || r.delta_read(TID, 1, spec.clone(), layout).unwrap();
black_box(run());
let (reply, instructions) = counter.measure(run);
println!(
"delta_read_bench {label:<28} {:>7} rows out {:>8.1} instr/band row",
reply.len(),
instructions as f64 / band as f64
);
}
}
let r = fixture(1, 1);
let spec = spec(ReadBound::None, lt(256), rows(slim()));
let layout = schema(1).layout_digest();
let run = || r.delta_read(TID, 1, spec.clone(), layout).unwrap();
black_box(run());
const CALLS: u64 = 1000;
let (out, instructions) = counter.measure(|| (0..CALLS).map(|_| run().len()).sum::<usize>());
println!(
"delta_read_bench one row, predicate + map {out:>7} rows out {:>8.1} instr/call",
instructions as f64 / CALLS as f64
);
}
fn keys_fixture(keys: usize, string: bool, rounds: u64, per_round: u64) -> (RelationFixture, SchemaDescriptor) {
let c = SchemaColumn::new;
let mut cols = vec![c(TypeCode::U64, false); keys];
cols.push(c(TypeCode::I64, false));
if string {
cols.push(c(TypeCode::String, false));
}
let pk: Vec<u32> = (0..keys as u32).collect();
let s = SchemaDescriptor::new(&cols, &pk);
let kind = fed_view(1 << 32);
let mut r = relation_fixture(kind, s, &[], []);
for round in 2..2 + rounds {
let mut bb = BatchBuilder::new(&s);
for id in 0..per_round {
let mut key = vec![7u128; keys];
key[0] = id as u128;
bb.begin_row_natives(&key, 1);
bb.put_u64(id % 512);
if string {
match id % 2 {
0 => bb.put_string(&format!("a-fairly-long-out-of-line-value-{id}")),
_ => bb.put_string("short"),
}
}
bb.end_row();
}
r.ingest_at(TID, bb.finish(), Some(round), false).unwrap();
}
(r, s)
}
#[test]
#[ignore = "benchmark; run with --release --ignored --nocapture --test-threads=1"]
fn delta_read_keys_bench() {
let counter = gnitz_foundation::perf::Counter::instructions();
for (keys, string) in [(1, false), (3, false), (4, false), (1, true), (3, true)] {
let lt = |lit| cmp_const(CmpOp::Lt, keys as u32, lit).to_blob_bytes();
for (rounds, per_round) in [(1u64, 262144u64), (64, 4096)] {
let (r, s) = keys_fixture(keys, string, rounds, per_round);
let band = rounds * per_round;
for (label, predicate) in [("whole", vec![]), ("half", lt(256)), ("1/512", lt(1))] {
let spec = ReadSpec {
predicate,
..ReadSpec::all_rows(ReadBound::None)
};
let run = || r.delta_read(TID, 1, spec.clone(), s.layout_digest()).unwrap();
black_box(run());
let (reply, instructions) = counter.measure(run);
println!(
"delta_read_keys_bench {keys} keys string={string:<5} {rounds:>2} rounds {label:<6} {:>7} rows out {:>8.1} instr/band row",
reply.len(),
instructions as f64 / band as f64
);
}
}
}
}