From bf13ce9618c324962301e8d266a20a49e912b2ed Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Thu, 24 Apr 2025 19:38:02 +0200 Subject: [PATCH 1/4] Add button benchmark I also realized that the label benchmark wasn't testing the painting, since the buttons at some point will be placed outside the screen_rect, meaning it won't be painted. This fixes it by benching the label in a child ui. The label &str benchmark went from 483 ns to 535 ns with these changes. --- crates/egui_demo_lib/README.md | 13 ++++++ crates/egui_demo_lib/benches/benchmark.rs | 57 ++++++++++++++++++++--- 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/crates/egui_demo_lib/README.md b/crates/egui_demo_lib/README.md index 43ca0a0f309..5a742eec052 100644 --- a/crates/egui_demo_lib/README.md +++ b/crates/egui_demo_lib/README.md @@ -14,3 +14,16 @@ The demo library is a separate crate for three reasons: * To remove the amount of code in `egui` proper. * To make it easy for 3rd party egui integrations to use it for tests. - See for instance https://github.com/not-fl3/egui-miniquad/blob/master/examples/demo.rs + +This crate also contains benchmarks for egui. +Run them with +```bash +# Run all benchmarks +cargo bench -p egui_demo_lib + +# Run a single benchmark +cargo bench -p egui_demo_lib "benchmark name" + +# Profile benchmarks with cargo-flamegraph (--root flag is necessary for MacOS) +CARGO_PROFILE_BENCH_DEBUG=true cargo flamegraph --bench benchmark --root -p egui_demo_lib -- --bench "benchmark name" +``` diff --git a/crates/egui_demo_lib/benches/benchmark.rs b/crates/egui_demo_lib/benches/benchmark.rs index dab6bdd7b12..40fc93be8b4 100644 --- a/crates/egui_demo_lib/benches/benchmark.rs +++ b/crates/egui_demo_lib/benches/benchmark.rs @@ -1,8 +1,9 @@ use std::fmt::Write as _; -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; use egui::epaint::TextShape; +use egui::{Button, UiBuilder}; use egui_demo_lib::LOREM_IPSUM_LONG; use rand::Rng as _; @@ -57,14 +58,56 @@ pub fn criterion_benchmark(c: &mut Criterion) { let _ = ctx.run(RawInput::default(), |ctx| { egui::CentralPanel::default().show(ctx, |ui| { c.bench_function("label &str", |b| { - b.iter(|| { - ui.label("the quick brown fox jumps over the lazy dog"); - }); + b.iter_batched_ref( + || ui.new_child(UiBuilder::new()), + |ui| { + ui.label("the quick brown fox jumps over the lazy dog"); + }, + BatchSize::LargeInput, + ) }); c.bench_function("label format!", |b| { - b.iter(|| { - ui.label("the quick brown fox jumps over the lazy dog".to_owned()); - }); + b.iter_batched_ref( + || ui.new_child(UiBuilder::new()), + |ui| { + ui.label("the quick brown fox jumps over the lazy dog".to_owned()); + }, + BatchSize::LargeInput, + ) + }); + }); + }); + } + + { + let ctx = egui::Context::default(); + let _ = ctx.run(RawInput::default(), |ctx| { + egui::CentralPanel::default().show(ctx, |ui| { + let mut group = c.benchmark_group("button"); + group.bench_function("button", |b| { + b.iter_batched_ref( + || ui.new_child(UiBuilder::new()), + |ui| { + ui.add(Button::new("Hello World")); + }, + BatchSize::LargeInput, + ); + }); + group.bench_function("button image", |b| { + b.iter_batched_ref( + || ui.new_child(UiBuilder::new()), + |ui| { + ui.add(Button::image_and_text("some-image.png", "Hello World")); + }, + BatchSize::LargeInput, + ) + }); + group.bench_function("button image right text", |b| { + b.iter_batched_ref( + || ui.new_child(UiBuilder::new()), + |ui| ui.add(Button::image_and_text("some-image.png", "Hello World")), + BatchSize::LargeInput, + ) }); }); }); From 39eb37908123d3d98e09f34219f02e94a382a47e Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Thu, 24 Apr 2025 19:52:59 +0200 Subject: [PATCH 2/4] Fix image failing to load --- crates/egui_demo_lib/benches/benchmark.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/egui_demo_lib/benches/benchmark.rs b/crates/egui_demo_lib/benches/benchmark.rs index 40fc93be8b4..5b44c8ef6c2 100644 --- a/crates/egui_demo_lib/benches/benchmark.rs +++ b/crates/egui_demo_lib/benches/benchmark.rs @@ -3,7 +3,8 @@ use std::fmt::Write as _; use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; use egui::epaint::TextShape; -use egui::{Button, UiBuilder}; +use egui::load::SizedTexture; +use egui::{Button, TextureId, UiBuilder, Vec2}; use egui_demo_lib::LOREM_IPSUM_LONG; use rand::Rng as _; @@ -84,6 +85,11 @@ pub fn criterion_benchmark(c: &mut Criterion) { let _ = ctx.run(RawInput::default(), |ctx| { egui::CentralPanel::default().show(ctx, |ui| { let mut group = c.benchmark_group("button"); + + // To ensure we have a valid image, let's use the font texture. The size + // shouldn't be important for this benchmark. + let image = SizedTexture::new(TextureId::default(), Vec2::splat(16.0)); + group.bench_function("button", |b| { b.iter_batched_ref( || ui.new_child(UiBuilder::new()), @@ -97,7 +103,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { b.iter_batched_ref( || ui.new_child(UiBuilder::new()), |ui| { - ui.add(Button::image_and_text("some-image.png", "Hello World")); + ui.add(Button::image_and_text(image, "Hello World")); }, BatchSize::LargeInput, ) @@ -105,7 +111,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { group.bench_function("button image right text", |b| { b.iter_batched_ref( || ui.new_child(UiBuilder::new()), - |ui| ui.add(Button::image_and_text("some-image.png", "Hello World")), + |ui| ui.add(Button::image_and_text(image, "Hello World")), BatchSize::LargeInput, ) }); From a56788451a96ad4eccca86a51bccc827be02aa10 Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Thu, 24 Apr 2025 20:23:21 +0200 Subject: [PATCH 3/4] Fix missing right text --- crates/egui_demo_lib/benches/benchmark.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/egui_demo_lib/benches/benchmark.rs b/crates/egui_demo_lib/benches/benchmark.rs index 5b44c8ef6c2..b91ca4c96a1 100644 --- a/crates/egui_demo_lib/benches/benchmark.rs +++ b/crates/egui_demo_lib/benches/benchmark.rs @@ -111,7 +111,9 @@ pub fn criterion_benchmark(c: &mut Criterion) { group.bench_function("button image right text", |b| { b.iter_batched_ref( || ui.new_child(UiBuilder::new()), - |ui| ui.add(Button::image_and_text(image, "Hello World")), + |ui| { + ui.add(Button::image_and_text(image, "Hello World").right_text("⏵")); + }, BatchSize::LargeInput, ) }); From 7d37bc2a7fae105cef8116f17370f17621a32a19 Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Fri, 25 Apr 2025 15:55:15 +0200 Subject: [PATCH 4/4] Ensure widgets have an id clash for more reliable benchmark results --- crates/egui_demo_lib/README.md | 3 + crates/egui_demo_lib/benches/benchmark.rs | 111 +++++++++++----------- 2 files changed, 60 insertions(+), 54 deletions(-) diff --git a/crates/egui_demo_lib/README.md b/crates/egui_demo_lib/README.md index 5a742eec052..58a5d6305fa 100644 --- a/crates/egui_demo_lib/README.md +++ b/crates/egui_demo_lib/README.md @@ -26,4 +26,7 @@ cargo bench -p egui_demo_lib "benchmark name" # Profile benchmarks with cargo-flamegraph (--root flag is necessary for MacOS) CARGO_PROFILE_BENCH_DEBUG=true cargo flamegraph --bench benchmark --root -p egui_demo_lib -- --bench "benchmark name" + +# Profile with cargo-instruments +CARGO_PROFILE_BENCH_DEBUG=true cargo instruments --profile bench --bench benchmark -p egui_demo_lib -t time -- --bench "benchmark name" ``` diff --git a/crates/egui_demo_lib/benches/benchmark.rs b/crates/egui_demo_lib/benches/benchmark.rs index b91ca4c96a1..331788c9b29 100644 --- a/crates/egui_demo_lib/benches/benchmark.rs +++ b/crates/egui_demo_lib/benches/benchmark.rs @@ -4,10 +4,17 @@ use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; use egui::epaint::TextShape; use egui::load::SizedTexture; -use egui::{Button, TextureId, UiBuilder, Vec2}; +use egui::{Button, Id, TextureId, Ui, UiBuilder, Vec2}; use egui_demo_lib::LOREM_IPSUM_LONG; use rand::Rng as _; +/// Each iteration should be called in their own `Ui` with an intentional id clash, +/// to prevent the Context from building a massive map of `WidgetRects` (which would slow the test, +/// causing unreliable results). +fn create_benchmark_ui(ctx: &egui::Context) -> Ui { + Ui::new(ctx.clone(), Id::new("clashing_id"), UiBuilder::new()) +} + pub fn criterion_benchmark(c: &mut Criterion) { use egui::RawInput; @@ -57,25 +64,23 @@ pub fn criterion_benchmark(c: &mut Criterion) { { let ctx = egui::Context::default(); let _ = ctx.run(RawInput::default(), |ctx| { - egui::CentralPanel::default().show(ctx, |ui| { - c.bench_function("label &str", |b| { - b.iter_batched_ref( - || ui.new_child(UiBuilder::new()), - |ui| { - ui.label("the quick brown fox jumps over the lazy dog"); - }, - BatchSize::LargeInput, - ) - }); - c.bench_function("label format!", |b| { - b.iter_batched_ref( - || ui.new_child(UiBuilder::new()), - |ui| { - ui.label("the quick brown fox jumps over the lazy dog".to_owned()); - }, - BatchSize::LargeInput, - ) - }); + c.bench_function("label &str", |b| { + b.iter_batched_ref( + || create_benchmark_ui(ctx), + |ui| { + ui.label("the quick brown fox jumps over the lazy dog"); + }, + BatchSize::LargeInput, + ); + }); + c.bench_function("label format!", |b| { + b.iter_batched_ref( + || create_benchmark_ui(ctx), + |ui| { + ui.label("the quick brown fox jumps over the lazy dog".to_owned()); + }, + BatchSize::LargeInput, + ); }); }); } @@ -83,40 +88,38 @@ pub fn criterion_benchmark(c: &mut Criterion) { { let ctx = egui::Context::default(); let _ = ctx.run(RawInput::default(), |ctx| { - egui::CentralPanel::default().show(ctx, |ui| { - let mut group = c.benchmark_group("button"); - - // To ensure we have a valid image, let's use the font texture. The size - // shouldn't be important for this benchmark. - let image = SizedTexture::new(TextureId::default(), Vec2::splat(16.0)); - - group.bench_function("button", |b| { - b.iter_batched_ref( - || ui.new_child(UiBuilder::new()), - |ui| { - ui.add(Button::new("Hello World")); - }, - BatchSize::LargeInput, - ); - }); - group.bench_function("button image", |b| { - b.iter_batched_ref( - || ui.new_child(UiBuilder::new()), - |ui| { - ui.add(Button::image_and_text(image, "Hello World")); - }, - BatchSize::LargeInput, - ) - }); - group.bench_function("button image right text", |b| { - b.iter_batched_ref( - || ui.new_child(UiBuilder::new()), - |ui| { - ui.add(Button::image_and_text(image, "Hello World").right_text("⏵")); - }, - BatchSize::LargeInput, - ) - }); + let mut group = c.benchmark_group("button"); + + // To ensure we have a valid image, let's use the font texture. The size + // shouldn't be important for this benchmark. + let image = SizedTexture::new(TextureId::default(), Vec2::splat(16.0)); + + group.bench_function("1_button_text", |b| { + b.iter_batched_ref( + || create_benchmark_ui(ctx), + |ui| { + ui.add(Button::new("Hello World")); + }, + BatchSize::LargeInput, + ); + }); + group.bench_function("2_button_text_image", |b| { + b.iter_batched_ref( + || create_benchmark_ui(ctx), + |ui| { + ui.add(Button::image_and_text(image, "Hello World")); + }, + BatchSize::LargeInput, + ); + }); + group.bench_function("3_button_text_image_right_text", |b| { + b.iter_batched_ref( + || create_benchmark_ui(ctx), + |ui| { + ui.add(Button::image_and_text(image, "Hello World").right_text("⏵")); + }, + BatchSize::LargeInput, + ); }); }); }