Skip to content

Commit

Permalink
Add some simple benchmarks for glyph parsing
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
cmyr committed Aug 15, 2023
1 parent b04d9ab commit 3f91eb0
Show file tree
Hide file tree
Showing 4 changed files with 641 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
69 changes: 69 additions & 0 deletions benches/glif_parse.rs
Original file line number Diff line number Diff line change
@@ -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<u8> {
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);
9 changes: 9 additions & 0 deletions src/glyph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self, GlifLoadError> {
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
Expand Down
Loading

0 comments on commit 3f91eb0

Please sign in to comment.