Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120//! Times the engine's stages -- HTML parsing, parsing plus styling (`Document::parse`),//! box-tree construction, and layout -- over the 227-case recipe corpus and, with//! `--page <path>`, over one real page. The first measurement `roadmap.md` -> "Substrate//! rework" item 4 asks for before any incremental restyle or layout cache is built.//!//! cargo run --release -p webengine --example bench -- [--page <path>] [--rounds <n>]//!//! Each document is run `rounds` times (default 20) and its fastest round kept, which//! is the least noisy estimate of what the code costs; the corpus figures are the sum of//! those minima over every case. Styling is `Document::parse` minus parsing alone, since//! the two run as one call.
use std::time::{Duration, Instant};
use anyhow::{bail, Context, Result};use dom::Document;use layout::boxtree::BoxTree;use layout::fragment;
const VIEWPORT: (f64, f64) = (800.0, 600.0);
#[derive(Default, Clone, Copy)]struct Stages { parse: Duration, parse_and_style: Duration, box_tree: Duration, layout: Duration,}
impl Stages { fn min(self, other: Stages) -> Stages { Stages { parse: self.parse.min(other.parse), parse_and_style: self.parse_and_style.min(other.parse_and_style), box_tree: self.box_tree.min(other.box_tree), layout: self.layout.min(other.layout), } }
fn add(self, other: Stages) -> Stages { Stages { parse: self.parse + other.parse, parse_and_style: self.parse_and_style + other.parse_and_style, box_tree: self.box_tree + other.box_tree, layout: self.layout + other.layout, } }}
fn once(html: &str) -> Stages { let start = Instant::now(); let nodes = dom::parse::parse_html(html); let parse = start.elapsed(); drop(nodes);
let start = Instant::now(); let document = Document::parse_with_viewport(html, VIEWPORT); let parse_and_style = start.elapsed();
let start = Instant::now(); let tree = BoxTree::build(&document); let box_tree = start.elapsed();
let start = Instant::now(); let root = fragment::layout(&document, &tree, VIEWPORT); let layout = start.elapsed(); drop(root);
Stages { parse, parse_and_style, box_tree, layout }}
fn fastest(html: &str, rounds: usize) -> Stages { let max = Duration::MAX; (0..rounds).fold(Stages { parse: max, parse_and_style: max, box_tree: max, layout: max }, |best, _| { best.min(once(html)) })}
fn report(label: &str, documents: usize, s: Stages) { let style = s.parse_and_style.saturating_sub(s.parse); let total = s.parse_and_style + s.box_tree + s.layout; let ms = |d: Duration| d.as_secs_f64() * 1e3; println!( "{label:<22} {documents:>4} doc(s) parse {:>8.3} ms style {:>8.3} ms box tree {:>8.3} ms layout {:>8.3} ms total {:>8.3} ms", ms(s.parse), ms(style), ms(s.box_tree), ms(s.layout), ms(total), );}
fn main() -> Result<()> { let mut page = None; let mut rounds = 20usize; let mut args = std::env::args().skip(1); while let Some(arg) = args.next() { match arg.as_str() { "--page" => page = Some(args.next().context("--page needs a path")?), "--rounds" => rounds = args.next().context("--rounds needs a value")?.parse()?, other => bail!("unknown argument: {other}"), } }
let mut cases = harness::cases::enumerated(); cases.extend(harness::cases::random(0xC0FFEE, 200)); let corpus = cases.iter().fold(Stages::default(), |sum, case| sum.add(fastest(&case.html, rounds))); report("recipe corpus", cases.len(), corpus);
if let Some(path) = page { let html = std::fs::read_to_string(&path).with_context(|| format!("reading {path}"))?; let document = Document::parse_with_viewport(&html, VIEWPORT); let elements = document.elements_in_document_order().len(); drop(document); println!("page: {path} ({} bytes, {elements} elements)", html.len()); report("page", 1, fastest(&html, rounds)); } Ok(())}