| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
use std::collections::HashMap; |
| 11 |
use std::hint::black_box; |
| 12 |
use std::path::PathBuf; |
| 13 |
|
| 14 |
use criterion::{Criterion, criterion_group, criterion_main}; |
| 15 |
|
| 16 |
use docengine::{ |
| 17 |
DocLoader, DocLoaderConfig, QuoteAuthor, Renderer, post_process_directives, post_process_quotes, |
| 18 |
}; |
| 19 |
|
| 20 |
|
| 21 |
|
| 22 |
fn description_2kb() -> String { |
| 23 |
let block = "\ |
| 24 |
## What this is |
| 25 |
|
| 26 |
A small, focused tool for **doing one thing well**. It stays out of your way |
| 27 |
and does not phone home. See the [guide](/docs/guide/overview) for the full |
| 28 |
tour, or jump straight to `quickstart` below. |
| 29 |
|
| 30 |
- Fast, native builds — no runtime to install |
| 31 |
- Full export, no lock-in |
| 32 |
- Works offline first, syncs when you want it to |
| 33 |
|
| 34 |
| Tier | Price | Files | |
| 35 |
|------|-------|-------| |
| 36 |
| Basic | $16 | text | |
| 37 |
| Big Files | $36 | large | |
| 38 |
|
| 39 |
> A short aside about why this exists and who it is for. |
| 40 |
"; |
| 41 |
|
| 42 |
let mut s = String::with_capacity(2200); |
| 43 |
for _ in 0..4 { |
| 44 |
s.push_str(block); |
| 45 |
} |
| 46 |
s |
| 47 |
} |
| 48 |
|
| 49 |
|
| 50 |
|
| 51 |
fn doc_page_20kb() -> String { |
| 52 |
let section = "\ |
| 53 |
## Section heading |
| 54 |
|
| 55 |
Longer explanatory prose that runs a few sentences so the parser has real text |
| 56 |
to walk, not just markup. It references [another page](/docs/guide/other) and |
| 57 |
uses `inline code` plus **bold** and *italic* spans throughout the paragraph. |
| 58 |
|
| 59 |
 |
| 60 |
|
| 61 |
> [!NOTE] |
| 62 |
> A callout that the directives post-processor will rewrite into a styled alert |
| 63 |
> block. These are common in the developer docs. |
| 64 |
|
| 65 |
> [!WARNING] |
| 66 |
> A second callout of a different type, to exercise the type-matching arm. |
| 67 |
|
| 68 |
1. First ordered step with some detail |
| 69 |
2. Second step that continues the thought |
| 70 |
3. Third step to close it out |
| 71 |
"; |
| 72 |
|
| 73 |
let mut s = String::with_capacity(21_000); |
| 74 |
for _ in 0..32 { |
| 75 |
s.push_str(section); |
| 76 |
} |
| 77 |
s |
| 78 |
} |
| 79 |
|
| 80 |
|
| 81 |
|
| 82 |
|
| 83 |
fn site_docs_path() -> Option<PathBuf> { |
| 84 |
let p = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../server/site-docs/public"); |
| 85 |
p.is_dir().then_some(p) |
| 86 |
} |
| 87 |
|
| 88 |
fn server_doc_config() -> DocLoaderConfig { |
| 89 |
|
| 90 |
|
| 91 |
DocLoaderConfig { |
| 92 |
sections: vec![ |
| 93 |
("about".to_string(), "About".to_string()), |
| 94 |
("guide".to_string(), "Guide".to_string()), |
| 95 |
("developer".to_string(), "Developer".to_string()), |
| 96 |
("legal".to_string(), "Legal".to_string()), |
| 97 |
("support".to_string(), "Support".to_string()), |
| 98 |
("tech".to_string(), "Tech".to_string()), |
| 99 |
], |
| 100 |
link_prefix: "/docs".to_string(), |
| 101 |
unpublished_pattern: Some("unpublished/".to_string()), |
| 102 |
examples_path: None, |
| 103 |
pre_process: None, |
| 104 |
} |
| 105 |
} |
| 106 |
|
| 107 |
|
| 108 |
fn bench_presets(c: &mut Criterion) { |
| 109 |
let input = description_2kb(); |
| 110 |
let mut group = c.benchmark_group("render_presets_2kb"); |
| 111 |
for (name, renderer) in [ |
| 112 |
("permissive", Renderer::permissive()), |
| 113 |
("standard", Renderer::standard()), |
| 114 |
("strict", Renderer::strict()), |
| 115 |
("sanitize_only", Renderer::sanitize_only()), |
| 116 |
] { |
| 117 |
group.bench_function(name, |b| { |
| 118 |
b.iter(|| renderer.render(black_box(&input))); |
| 119 |
}); |
| 120 |
} |
| 121 |
group.finish(); |
| 122 |
} |
| 123 |
|
| 124 |
|
| 125 |
|
| 126 |
fn bench_large_page(c: &mut Criterion) { |
| 127 |
let input = doc_page_20kb(); |
| 128 |
c.bench_function("render_permissive_20kb_page", |b| { |
| 129 |
let renderer = Renderer::permissive(); |
| 130 |
b.iter(|| renderer.render(black_box(&input))); |
| 131 |
}); |
| 132 |
} |
| 133 |
|
| 134 |
|
| 135 |
fn bench_doc_loader(c: &mut Criterion) { |
| 136 |
let Some(path) = site_docs_path() else { |
| 137 |
eprintln!("skipping doc_loader_load: site-docs corpus not found alongside crate"); |
| 138 |
return; |
| 139 |
}; |
| 140 |
c.bench_function("doc_loader_load_site_docs", |b| { |
| 141 |
b.iter(|| DocLoader::load(black_box(&path), &server_doc_config())); |
| 142 |
}); |
| 143 |
} |
| 144 |
|
| 145 |
|
| 146 |
|
| 147 |
fn bench_post_process(c: &mut Criterion) { |
| 148 |
|
| 149 |
let directive_html = Renderer::permissive().render(&doc_page_20kb()); |
| 150 |
c.bench_function("post_process_directives_20kb", |b| { |
| 151 |
b.iter(|| post_process_directives(black_box(&directive_html))); |
| 152 |
}); |
| 153 |
|
| 154 |
|
| 155 |
|
| 156 |
let id = uuid::Uuid::from_u128(0x0123_4567_89ab_cdef_0123_4567_89ab_cdef); |
| 157 |
let marker = format!("[quote:{id}:0123abcd]"); |
| 158 |
let mut quote_html = String::with_capacity(4096); |
| 159 |
for _ in 0..16 { |
| 160 |
quote_html.push_str("<p>Some preceding paragraph text for context.</p>\n"); |
| 161 |
quote_html.push_str("<blockquote><p>"); |
| 162 |
quote_html.push_str(&marker); |
| 163 |
quote_html.push_str("</p></blockquote>\n"); |
| 164 |
} |
| 165 |
let mut authors = HashMap::new(); |
| 166 |
authors.insert( |
| 167 |
id, |
| 168 |
QuoteAuthor { |
| 169 |
username: "creator".to_string(), |
| 170 |
display_name: "A Creator".to_string(), |
| 171 |
is_removed: false, |
| 172 |
}, |
| 173 |
); |
| 174 |
c.bench_function("post_process_quotes_16_markers", |b| { |
| 175 |
b.iter(|| post_process_quotes(black_box("e_html), black_box(&authors))); |
| 176 |
}); |
| 177 |
} |
| 178 |
|
| 179 |
criterion_group!( |
| 180 |
benches, |
| 181 |
bench_presets, |
| 182 |
bench_large_page, |
| 183 |
bench_doc_loader, |
| 184 |
bench_post_process |
| 185 |
); |
| 186 |
criterion_main!(benches); |
| 187 |
|