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

nordisk/facett

facett-grid/examples/grid_bench.rs

//! CPU frame/interaction benchmark (no window or GPU):
//! `cargo run --release -p facett-grid --example grid_bench -- 1000000`
//! For process peak memory, run the built executable under the platform's memory
//! profiler (e.g. `/usr/bin/time -l target/release/examples/grid_bench 1000000` on macOS).
//! Frame timings include layout, painting and tessellation, excluding GPU submission.
use std::{hint::black_box, sync::Arc, time::Instant};

use arrow_array::{Float64Array, Int64Array, RecordBatch, StringArray};
use arrow_schema::{DataType, Field, Schema};
use facett_core::Facet;
use facett_grid::{Grid, Msg};

fn percentiles(mut samples: Vec<f64>) -> serde_json::Value {
    samples.sort_by(f64::total_cmp);
    serde_json::json!({
        "p50_ms": samples[samples.len() / 2],
        "p95_ms": samples[(samples.len() * 95 / 100).min(samples.len() - 1)],
        "max_ms": samples.last(),
    })
}

fn frame(ctx: &egui::Context, grid: &mut Grid, scroll: bool) -> (f64, usize) {
    let start = Instant::now();
    let mut input = egui::RawInput {
        screen_rect: Some(egui::Rect::from_min_size(
            egui::Pos2::ZERO,
            egui::vec2(1000.0, 700.0),
        )),
        ..Default::default()
    };
    if scroll {
        input.events = vec![
            egui::Event::PointerMoved(egui::pos2(400.0, 300.0)),
            egui::Event::MouseWheel {
                unit: egui::MouseWheelUnit::Point,
                delta: egui::vec2(0.0, -48.0),
                modifiers: egui::Modifiers::NONE,
                phase: egui::TouchPhase::Move,
            },
        ];
    }
    let output = ctx.run_ui(input, |ui| {
        egui::CentralPanel::default().show(ui, |ui| grid.ui(ui));
    });
    let primitives = ctx.tessellate(output.shapes, output.pixels_per_point);
    let vertices = primitives
        .iter()
        .map(|p| match &p.primitive {
            egui::epaint::Primitive::Mesh(m) => m.vertices.len(),
            _ => 0,
        })
        .sum();
    black_box(&primitives);
    (start.elapsed().as_secs_f64() * 1000.0, vertices)
}

fn main() {
    let n = std::env::args()
        .nth(1)
        .map(|s| s.parse::<usize>().expect("row count"))
        .unwrap_or(100_000)
        .max(1);
    let batch = RecordBatch::try_new(
        Arc::new(Schema::new(vec![
            Field::new("id", DataType::Int64, false),
            Field::new("amount", DataType::Float64, false),
            Field::new("region", DataType::Utf8, false),
            Field::new("description", DataType::Utf8, false),
        ])),
        vec![
            Arc::new(Int64Array::from_iter_values(0..n as i64)),
            Arc::new(Float64Array::from_iter_values(
                (0..n).map(|i| ((i * 37) % 10000) as f64 / 10.0),
            )),
            Arc::new(StringArray::from_iter_values(
                (0..n).map(|i| ["north", "south", "east", "west"][i % 4]),
            )),
            Arc::new(StringArray::from_iter_values(
                (0..n).map(|i| format!("Order {i}: goods and services")),
            )),
        ],
    )
    .unwrap();
    let source_bytes = batch.get_array_memory_size();
    let start = Instant::now();
    let mut grid = Grid::from_record_batch("benchmark", &batch).with_totals(true);
    let ingest_ms = start.elapsed().as_secs_f64() * 1000.0;
    let ctx = egui::Context::default();
    let (first_frame_ms, _) = frame(&ctx, &mut grid, false);
    for _ in 0..10 {
        frame(&ctx, &mut grid, false);
    }
    let mut vertices = 0;
    let scrolling: Vec<_> = (0..120)
        .map(|_| {
            let (ms, count) = frame(&ctx, &mut grid, true);
            vertices = count;
            ms
        })
        .collect();
    assert!(vertices > 0);
    let start = Instant::now();
    grid.click_header(1, false);
    let sort_ms = start.elapsed().as_secs_f64() * 1000.0;
    let start = Instant::now();
    grid.update(Msg::SetFilter("north".into()));
    let filter_submit_ms = start.elapsed().as_secs_f64() * 1000.0;
    let mut searching = Vec::new();
    while grid.filter_pending() {
        searching.push(frame(&ctx, &mut grid, false).0);
    }
    let filter_complete_ms = start.elapsed().as_secs_f64() * 1000.0;
    searching.push(frame(&ctx, &mut grid, false).0); // includes the new filtered sums
    assert_eq!(grid.visible_rows(), n.div_ceil(4));
    println!(
        "{}",
        serde_json::json!({
            "rows": n, "columns": 4, "totals": true,
            "source_array_bytes": source_bytes,
            "ingest_ms": ingest_ms, "first_frame_ms": first_frame_ms,
            "scroll_cpu": percentiles(scrolling), "last_frame_vertices": vertices,
            "sort_ms": sort_ms, "filter_submit_ms": filter_submit_ms,
            "filter_complete_ms": filter_complete_ms, "search_cpu": percentiles(searching),
            "method": "Headless egui layout + paint + tessellation; no GPU or vsync. source_array_bytes is source buffers, not process memory."
        })
    );
}