From a4d64340fcce61f650bf80628e1533382688ef08 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Sun, 10 Dec 2023 19:41:00 -0800 Subject: [PATCH 1/2] Add benchmarks Signed-off-by: John Nunley --- Cargo.toml | 7 ++++ benches/render.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 benches/render.rs diff --git a/Cargo.toml b/Cargo.toml index 17d6fc9..4977f52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,15 @@ readme = "README.md" [dependencies] libm = { version = "0.2.7", default-features = false, optional = true } +[dev-dependencies] +criterion = { version = "0.5.1", default-features = false, features = ["cargo_bench_support"] } +fastrand = { version = "2.0.1", default-features = false } + [features] default = ["eval", "std"] eval = [] std = [] +[[bench]] +name = "render" +harness = false diff --git a/benches/render.rs b/benches/render.rs new file mode 100644 index 0000000..d9a41d7 --- /dev/null +++ b/benches/render.rs @@ -0,0 +1,81 @@ +//! Rendering benchmarks. + +use criterion::{criterion_group, criterion_main, Criterion, black_box}; +use fastrand::Rng; +use zeno::{Scratch, PathBuilder, Command, Mask, Style}; + +fn drawing(c: &mut Criterion) { + // Set up buffers for rendering. + let mut buffer = Box::new([0u8; 1024 * 1024]); + let mut scratch = Scratch::new(); + let mut rng = Rng::with_seed(0x12345678); + + c.bench_function("fill_square", |b| { + let path = { + let mut path = Vec::::new(); + path.add_rect( + (5.0, 5.0), + 1000.0, + 1000.0 + ); + path + }; + + b.iter(|| { + Mask::with_scratch(&path, &mut scratch) + .style(Style::Fill(zeno::Fill::EvenOdd)) + .render_into(&mut *buffer, None); + black_box((&mut scratch, &mut buffer)); + }); + }); + + c.bench_function("complicated_shape", |b| { + // Create a weird, jagged circle. + let path = { + let (center_x, center_y) = (500.0, 500.0); + let radius = 450.0; + let mut path = Vec::::new(); + + path.move_to((center_x, center_y)); + + for i in 0..500 { + let angle = core::f32::consts::PI * 2.0 * (i as f32) / 500.0; + let pt_x = center_x + (angle.cos() * radius) + rng.f32(); + let pt_y = center_y + (angle.sin() * radius) + rng.f32(); + path.line_to((pt_x, pt_y)); + } + + path.close(); + path + }; + + b.iter(|| { + Mask::with_scratch(&path, &mut scratch) + .style(Style::Fill(zeno::Fill::EvenOdd)) + .render_into(&mut *buffer, None); + black_box((&mut scratch, &mut buffer)); + }) + }); + + c.bench_function("circle", |b| { + let path = { + let mut path = Vec::::new(); + path.add_circle((500.0, 500.0), 450.0); + path + }; + + b.iter(|| { + Mask::with_scratch(&path, &mut scratch) + .style(Style::Fill(zeno::Fill::EvenOdd)) + .render_into(&mut *buffer, None); + black_box((&mut scratch, &mut buffer)); + }); + }); +} + +criterion_group!( + benches, + drawing +); + +criterion_main!(benches); From e6e48811184cdba867a06e0b90b217bb60a5b5c3 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Sun, 10 Dec 2023 19:43:44 -0800 Subject: [PATCH 2/2] Use default arrays instead of uninit arrays In the rasterizer, arrays of data are gradually initialized by creating an uninitialized array and writing into those arrays. However, this is technically unsound in Rust, and causes MIRI to fail when I try to run on it. Out of curiousity, I tried switching it to zeroing the array first before writing to it. However, by some benchmarks I wrote it seems to have no effect on the speed of the system. So I think it's a viable solution to reduce undefined behavior. Signed-off-by: John Nunley --- src/raster.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/raster.rs b/src/raster.rs index 24ffe97..71681d3 100644 --- a/src/raster.rs +++ b/src/raster.rs @@ -378,8 +378,7 @@ impl<'a, S: RasterStorage> Rasterizer<'a, S> { } fn quad_to(&mut self, control: FixedPoint, to: FixedPoint) { - let mut arc: [FixedPoint; 16 * 2 + 1] = - unsafe { core::mem::MaybeUninit::uninit().assume_init() }; + let mut arc = [FixedPoint::default(); 16 * 2 + 1]; arc[0].x = to.x; arc[0].y = to.y; arc[1].x = control.x; @@ -429,8 +428,7 @@ impl<'a, S: RasterStorage> Rasterizer<'a, S> { } fn curve_to(&mut self, control1: FixedPoint, control2: FixedPoint, to: FixedPoint) { - let mut arc: [FixedPoint; 16 * 8 + 1] = - unsafe { core::mem::MaybeUninit::uninit().assume_init() }; + let mut arc = [FixedPoint::default(); 16 * 8 + 1]; arc[0].x = to.x; arc[0].y = to.y; arc[1].x = control2.x; @@ -548,7 +546,7 @@ impl<'a, S: RasterStorage> PathBuilder for Rasterizer<'a, S> { } } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] pub struct Cell { x: i32, cover: i32, @@ -642,9 +640,9 @@ impl AdaptiveStorage { max: FixedPoint::default(), height: 0, cell_count: 0, - cells: unsafe { core::mem::MaybeUninit::uninit().assume_init() }, + cells: [Default::default(); MAX_CELLS], heap_cells: Vec::new(), - indices: unsafe { core::mem::MaybeUninit::uninit().assume_init() }, + indices: [Default::default(); MAX_BAND], heap_indices: Vec::new(), } }