Files
logaritmiskandClaude Opus 5 b553c630f5 refactor!: K comes first in History, HistoryBuilder and Joint
`K` is the one type parameter people change, and it was last. Naming a
history in a struct field meant writing all four to say one thing:

    struct Ladder { history: History<i64, ConstantDrift, NullObserver, String> }
    struct Analysis<'h> { joint: Joint<'h, i64, ConstantDrift, NullObserver, &'static str> }

Now:

    struct Ladder { history: History<String> }
    struct Analysis<'h> { joint: Joint<'h> }

`History<K, T, D, O>`, all four defaulted. Bounds may reference later
parameters, so `D: Drift<T> = ConstantDrift` is legal in third position.
`Joint` gains the same defaults, so `Joint<'h, String>` spells it.

72 call sites swapped, and the reorder makes most of them shorter: 18
now read `History<String>` and the `&'static str` ones read `History`.
The two turbofished builders shrink from
`HistoryBuilder::<Untimed, _, _, String>::new()` to
`HistoryBuilder::<String, Untimed>::new()`.

`Joint` keeps `O` structurally, defaulted rather than removed. #72 notes
it never touches the observer, which is true — but it borrows the whole
`&'h History<K, T, D, O>` and calls `History::resolve_terms`, so dropping
the parameter means either a view type or moving that method off
`History`. The default already buys the entire user-visible benefit,
which was the spelling.

Refs #72.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011hcFjNDmHXZF8URGLku5zZ
2026-09-10 06:49:22 +02:00

63 lines
2.1 KiB
Rust

//! Ingestion cost: one event per call versus one batched call.
//!
//! The rest of the suite only measures batched construction, which is why a
//! quadratic in the incremental path went unnoticed — `record_winner` and
//! `event(..).commit()` each ingest a single event, so a caller looping over a
//! match feed takes that path.
use std::hint::black_box;
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use smallvec::smallvec;
use trueskill_tt::{Event, History, Member, Outcome, Team};
fn events(n: usize, time: i64) -> Vec<Event<i64, String>> {
(0..n)
.map(|i| Event {
time,
teams: smallvec![
Team::with_members([Member::new(format!("p{}", 2 * i))]),
Team::with_members([Member::new(format!("p{}", 2 * i + 1))]),
],
outcome: Outcome::winner(0, 2),
})
.collect()
}
fn bench_ingest(c: &mut Criterion) {
let mut group = c.benchmark_group("ingest");
for n in [250usize, 500, 1000] {
group.bench_with_input(BenchmarkId::new("one-at-a-time", n), &n, |b, &n| {
b.iter_batched(
|| events(n, 0),
|evs| {
let mut h: History<String> = History::builder().key_type::<String>().build();
for ev in evs {
h.add_events(std::iter::once(ev)).unwrap();
}
black_box(h.time_slices_len())
},
criterion::BatchSize::SmallInput,
);
});
group.bench_with_input(BenchmarkId::new("single-batch", n), &n, |b, &n| {
b.iter_batched(
|| events(n, 0),
|evs| {
let mut h: History<String> = History::builder().key_type::<String>().build();
h.add_events(evs).unwrap();
black_box(h.time_slices_len())
},
criterion::BatchSize::SmallInput,
);
});
}
group.finish();
}
criterion_group!(benches, bench_ingest);
criterion_main!(benches);