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

nordisk/edda

crates/nornir-docbook/examples/common/mod.rs

#![allow(dead_code)]

//! The corpus and the kernel probes both measurement examples run on.
//!
//! `bench.rs` asks what a render COSTS; `mem.rs` asks WHERE the memory goes.
//! They must ask it of the same documents or the two tables cannot be read
//! against each other, so the generator lives here and neither owns it.

use nornir_docbook::{ExportMeta, ImageSet};
use std::time::Duration;

/// A one-pixel PNG, so the corpus can carry real images rather than paths that
/// resolve to nothing.
pub const PNG_1PX: &[u8] = &[
    0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0x15, 0xc4,
    0x89, 0x00, 0x00, 0x00, 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0x00, 0x01, 0x00, 0x00,
    0x05, 0x00, 0x01, 0x0d, 0x0a, 0x2d, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae,
    0x42, 0x60, 0x82,
];

/// One block of the corpus: everything a real document has, once.
///
/// `n` makes each block textually distinct, which matters more than it looks:
/// typst memoizes on content, so a corpus of a thousand identical paragraphs
/// would measure the memo rather than the layout.
pub fn block(n: usize) -> String {
    format!(
        "## Section {n}\n\n\
         Prose with `snake_case_ident_{n}`, a [link](https://example.test/a_{n}), \
         **strong**, *emphasis* and a MAX_BLOB_BYTES that must keep its underscores.\n\n\
         ### Detail {n}\n\n\
         - first item {n}\n- second item with `code_{n}`\n- third\n\n\
         1. step one\n2. step two\n\n\
         | column | value | note |\n| --- | ---: | :-: |\n\
         | alpha {n} | {n} | ok |\n| beta | {} | ok |\n\n\
         ```rust\nfn item_{n}() -> usize {{ let x = {n}; x * 2 }}\n```\n\n\
         > A quoted line about {n}.\n\n",
        n * 7
    )
}

/// A document of about `target` bytes, with twenty images spread through it.
pub fn document(target: usize) -> String {
    document_with(target, true)
}

/// The same document with no image references at all.
///
/// `mem.rs` needs it: the public [`md_to_typst`](nornir_docbook::export::md_to_typst)
/// door emits a root-anchored `#image("/…")` for a local path, which is the
/// right answer for a caller compiling against a root of its own and a
/// "file not found" for the rootless in-memory pipeline that example rebuilds.
/// A corpus with no pictures is the same document in every way the memory
/// question cares about — the twenty images are one pixel each — and it lets
/// the rebuild be compared to the shipped function byte for byte.
pub fn document_with(target: usize, with_images: bool) -> String {
    let mut out = String::with_capacity(target + 4096);
    out.push_str("# The Corpus\n\nA generated document.\n\n");
    let mut n = 0;
    while out.len() < target {
        out.push_str(&block(n));
        if with_images && n < 20 {
            out.push_str(&format!("![figure {n}](docs/fig{n}.png)\n\n"));
        }
        n += 1;
    }
    out
}

pub fn images() -> ImageSet {
    let mut set = ImageSet::new();
    for i in 0..20 {
        set.insert(&format!("docs/fig{i}.png"), PNG_1PX.to_vec());
    }
    set
}

pub fn meta() -> ExportMeta {
    ExportMeta {
        title: "corpus".into(),
        version: String::new(),
        generated: String::new(),
        cover_image: String::new(),
    }
}

/// p50 and p99 of a run of durations.
pub fn stats(mut v: Vec<Duration>) -> (Duration, Duration) {
    v.sort();
    let p = |q: f64| v[(((v.len() - 1) as f64) * q).round() as usize];
    (p(0.50), p(0.99))
}

/// Peak resident set, in kB, from `/proc/self/status`.
///
/// `VmHWM` and not `VmRSS`: the question is what the process ever needed, and
/// the allocator hands pages back before this is read.
pub fn peak_rss_kb() -> u64 {
    proc_status_kb("VmHWM:")
}

/// Resident set *right now*, in kB. The companion to [`peak_rss_kb`]: a stage
/// table needs the level as well as the high-water, because a stage that frees
/// what it took shows a flat peak and a falling level, and those are different
/// facts.
pub fn rss_kb() -> u64 {
    proc_status_kb("VmRSS:")
}

fn proc_status_kb(field: &str) -> u64 {
    std::fs::read_to_string("/proc/self/status")
        .ok()
        .and_then(|s| {
            s.lines()
                .find(|l| l.starts_with(field))?
                .split_whitespace()
                .nth(1)?
                .parse()
                .ok()
        })
        .unwrap_or(0)
}

pub fn ms(d: Duration) -> f64 {
    d.as_secs_f64() * 1000.0
}

/// Bytes as MB, one decimal — the unit every memory row in these examples is
/// printed in.
pub fn mb(bytes: u64) -> String {
    format!("{:.1}", bytes as f64 / 1_048_576.0)
}