From 3f91eb0b5eb1af69f0ef7609d1db7dfaba36c83b Mon Sep 17 00:00:00 2001 From: Colin Rofls Date: Mon, 14 Aug 2023 17:14:43 -0400 Subject: [PATCH] Add some simple benchmarks for glyph parsing I wrote these up just to get numbers, but I think it is worth commiting them, since they will be useful if we are ever iterating on this code again. --- Cargo.toml | 5 + benches/glif_parse.rs | 69 +++++ src/glyph/mod.rs | 9 + testdata/cid61855.glif | 558 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 641 insertions(+) create mode 100644 benches/glif_parse.rs create mode 100644 testdata/cid61855.glif diff --git a/Cargo.toml b/Cargo.toml index 6545dbe6..ae62c305 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,3 +43,8 @@ tempdir = "0.3.7" maplit = "1.0.2" pretty_assertions = "1.0" expect-test = "1.4.1" +criterion = "0.3" + +[[bench]] +name = "glif_parse" +harness = false diff --git a/benches/glif_parse.rs b/benches/glif_parse.rs new file mode 100644 index 00000000..0781c221 --- /dev/null +++ b/benches/glif_parse.rs @@ -0,0 +1,69 @@ +//! Simple benchmarks of glyph parsing. +//! +//! This should be run when making any changes to glyph parsing. + +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use norad::Glyph; + +static S_GLYPH: &str = "testdata/MutatorSansLightWide.ufo/glyphs/S_.glif"; +static DOT: &str = "testdata/MutatorSansLightWide.ufo/glyphs/dot.glif"; +static A_ACUTE_GLYPH: &str = "testdata/MutatorSansLightWide.ufo/glyphs/A_acute.glif"; +// largest glyph in noto cjk +static CID61855: &str = "testdata/cid61855.glif"; + +fn load_bytes(path: &str) -> Vec { + std::fs::read(path).unwrap() +} + +pub fn criterion_benchmark(c: &mut Criterion) { + // a normal glyph + c.bench_function("parse S", |b| { + let bytes = load_bytes(S_GLYPH); + b.iter(|| { + Glyph::parse_raw(black_box(&bytes)).unwrap(); + }) + }); + // a very small glyph + c.bench_function("parse dot", |b| { + let bytes = load_bytes(DOT); + b.iter(|| { + Glyph::parse_raw(black_box(&bytes)).unwrap(); + }) + }); + // a very large glyph + c.bench_function("parse large CJK glyph", |b| { + let bytes = load_bytes(CID61855); + b.iter(|| { + Glyph::parse_raw(black_box(&bytes)).unwrap(); + }) + }); + // a component glyph + c.bench_function("parse A_acute", |b| { + let bytes = load_bytes(A_ACUTE_GLYPH); + b.iter(|| { + Glyph::parse_raw(black_box(&bytes)).unwrap(); + }) + }); + // Note to somebody using this: + // + // It might be nice if we also had some other examples, like a glyph with + // a large 'lib' section? + c.bench_function("load S glyph", |b| { + b.iter(|| { + let data = std::fs::read(black_box(S_GLYPH)).unwrap(); + // just make sure we can't be optimized away? + assert!(data.len() != 42); + }) + }); + + c.bench_function("load large CJK glyph", |b| { + b.iter(|| { + let data = std::fs::read(black_box(CID61855)).unwrap(); + // just make sure we can't be optimized away? + assert!(data.len() != 42); + }) + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/src/glyph/mod.rs b/src/glyph/mod.rs index ccb14539..6d344c58 100644 --- a/src/glyph/mod.rs +++ b/src/glyph/mod.rs @@ -68,6 +68,15 @@ impl Glyph { Glyph::load_with_names(path, &names) } + /// THIS IS NOT STABLE API! + /// + /// (exposed for benchmarking only) + #[doc(hidden)] + pub fn parse_raw(xml: &[u8]) -> Result { + let names = NameList::default(); + parse::GlifParser::from_xml(xml, Some(&names)) + } + /// Attempt to load the glyph at `path`, reusing names from the `NameList`. /// /// This uses string interning to reuse allocations when a glyph name diff --git a/testdata/cid61855.glif b/testdata/cid61855.glif new file mode 100644 index 00000000..96e48b59 --- /dev/null +++ b/testdata/cid61855.glif @@ -0,0 +1,558 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + public.verticalOrigin + 880 + + +