Skip to main content

max / quasi

3.3 KB · 93 lines History Blame Raw
1 //! The screen the bench renders.
2 //!
3 //! A copy of MNW's `library_contacts` pane, which is a real converted screen
4 //! rather than a shape invented for a benchmark. It was picked for the same
5 //! reason that batch picked it: it holds every table member the vocabulary has
6 //! grown and nothing that is still unnamed, so a cost measured here is a cost a
7 //! shipped screen pays.
8 //!
9 //! What it deliberately keeps from the original: two tables rather than one, a
10 //! value that is a link in two flavours (a route this app answers and a
11 //! `mailto:` that leaves), a destructive per-row act, and column widths and
12 //! priorities that make the narrowing pass do work. A single-column table of
13 //! plain strings would measure the loop and not the renderer.
14 //!
15 //! The row counts are the bench's axis, not the screen's: see [`SIZES`].
16
17 use quasi_router::Node;
18
19 use crate::staging;
20
21 /// Row counts, and why these three.
22 ///
23 /// Any single number hides it.
24 ///
25 /// 5 is a screen someone actually has, 25 is a full page, 200 is the size at
26 /// which a per-row cost stops being deniable.
27 pub const SIZES: [usize; 3] = [5, 25, 200];
28
29 /// One buyer row's worth of strings.
30 pub(crate) struct Buyer {
31 pub(crate) username: String,
32 pub(crate) email: String,
33 pub(crate) purchases: String,
34 pub(crate) spent: String,
35 pub(crate) last_purchase: String,
36 }
37
38 /// One shared-with row's worth of strings.
39 pub(crate) struct Shared {
40 pub(crate) seller_id: String,
41 pub(crate) username: String,
42 pub(crate) name: String,
43 }
44
45 /// Rows built once, outside the timed region.
46 ///
47 /// The bench measures rendering, so building the strings must not be inside the
48 /// loop. They are generated rather than repeated so that no two cells are the
49 /// same string, which keeps an allocator from making a repeated value look
50 /// cheaper than a real one.
51 pub struct Rows {
52 pub(crate) buyers: Vec<Buyer>,
53 pub(crate) shared: Vec<Shared>,
54 }
55
56 impl Rows {
57 /// `count` rows in each of the two tables.
58 #[must_use]
59 pub fn new(count: usize) -> Self {
60 let buyers = (0..count)
61 .map(|i| Buyer {
62 username: format!("buyer{i:04}"),
63 email: format!("buyer{i:04}@example.com"),
64 purchases: (i % 37 + 1).to_string(),
65 spent: format!("${}.{:02}", i * 7 % 900 + 3, i % 100),
66 last_purchase: format!("Aug {:02}, 2026", i % 28 + 1),
67 })
68 .collect();
69 let shared = (0..count)
70 .map(|i| Shared {
71 seller_id: format!("{:08}", i * 31 + 7),
72 username: format!("creator{i:04}"),
73 name: format!("Creator Number {i}"),
74 })
75 .collect();
76 Self { buyers, shared }
77 }
78 }
79
80 /// The pane, built the way the real screen builds it.
81 ///
82 /// Rebuilt per iteration on purpose. Building the description is part of what a
83 /// request pays, and a bench that hoisted it out would measure the renderer
84 /// against a tree the handler never actually has to make.
85 ///
86 /// The shape itself lives in [`crate::staging`], written once against the
87 /// `Data` seam so that the runtime path and the staged path cannot drift. This
88 /// is that one description evaluated over real strings.
89 #[must_use]
90 pub fn pane(rows: &Rows) -> Node {
91 staging::describe(&staging::Live(rows))
92 }
93