Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/edda

crates/gunnar-delta/tests/latency.rs

//! A small `deltify` must not sleep.
//!
//! `in_parallel_with_slice` runs the caller's periodic closure on a scoped
//! thread that sleeps for whatever that closure returns, and `std::thread::scope`
//! waits for it. So the sleep is not a progress refresh rate; it is a floor on
//! the entire call. gitoxide's own convention is 50 ms, and with it a `deltify`
//! over four small objects took 50 ms of which roughly 49.99 were sleeping.
//!
//! # Why this is a loop, and where the budget comes from
//!
//! A single-call timing assertion tight enough to catch a 50 ms floor ("under
//! 20 ms") goes red whenever the machine is busy, and a flaky test gets
//! deleted. Twenty calls against the floor cannot finish under a second no
//! matter what; twenty calls without it are dominated by thread spawn.
//!
//! The budget is measured, not guessed. On a 32-core Threadripper PRO 3975WX,
//! debug build:
//!
//! | load average | with the 50 ms tick | with a 1 ms tick |
//! |---|---|---|
//! | ~90  | 2190 ms | — |
//! | ~195 | — | 244 / 313 / 390 ms |
//!
//! 700 ms sits below the floor by 3x and above the worst observed non-floor run
//! by nearly 2x, on a box carrying six times its core count. The gap is that
//! wide because the two cases differ in kind, not in degree: one sleeps and one
//! does not.

use std::sync::atomic::AtomicBool;
use std::time::Instant;

use gix_object::Kind;
use gunnar_delta::{deltify, Object, Options};

const CALLS: usize = 20;

#[test]
fn twenty_small_searches_do_not_add_up_to_a_sleep() {
    let contents: Vec<Vec<u8>> = (0..4u8)
        .map(|k| {
            let mut v = vec![0u8; 1_024];
            for (i, b) in v.iter_mut().enumerate() {
                *b = (i as u8).wrapping_add(k);
            }
            v
        })
        .collect();
    let objects: Vec<Object<'_>> = contents
        .iter()
        .enumerate()
        .map(|(i, c)| {
            let mut id = [0u8; 20];
            id[..8].copy_from_slice(&(i as u64).to_be_bytes());
            Object {
                id: gix_hash::ObjectId::from(id),
                kind: Kind::Blob,
                name_hash: 0,
                epoch: 0,
                data: c.as_slice(),
            }
        })
        .collect();

    let started = Instant::now();
    let mut deltified = 0usize;
    for _ in 0..CALLS {
        let out = deltify(
            &objects,
            &Options::default(),
            &mut gix_utils::progress::Discard,
            &AtomicBool::default(),
        )
        .expect("deltify");
        deltified += out.iter().filter(|d| d.is_delta()).count();
    }
    let elapsed = started.elapsed();

    // The calls must have done real work, or "it was fast" means "it did
    // nothing".
    assert!(
        deltified >= CALLS,
        "{deltified} deltas across {CALLS} calls. These searches found nothing, so their \
         speed says nothing"
    );
    assert!(
        elapsed.as_millis() < 700,
        "{CALLS} searches over four 1 KiB objects took {elapsed:?}. At gitoxide's stock 50 ms \
         watcher tick that is exactly {} ms of sleeping, which is what this test exists to \
         catch. The work itself is single-digit milliseconds",
        CALLS * 50
    );
}