diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 5a4a3270..a713348a 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -1,45 +1,34 @@ -name: Benchmark +name: Continuous Benchmarking with Bencher on: - push: - paths: - - 'src/**' - - 'benches/**' - - 'ogg_pager/**' - branches: - - main - workflow_dispatch: - + push: + paths: + - 'src/**' + - 'benches/**' + - 'ogg_pager/**' + branches: + - main + workflow_dispatch: jobs: - benchmark: - name: Benchmark - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@v1 - with: - toolchain: nightly - - - name: Run benchmark + benchmark_with_bencher: + name: Benchmark with Bencher + runs-on: ubuntu-latest env: - RUSTFLAGS: '--cfg bench' - run: | - cargo bench --all-features -- --output-format bencher | tee output.txt + BENCHER_PROJECT: lofty + BENCHER_BRANCH: main + BENCHER_API_TOKEN: ${{ secrets.BENCHER_API_TOKEN }} + steps: + - uses: actions/checkout@v4 - - name: Store benchmark result - uses: benchmark-action/github-action-benchmark@v1 - with: - # What benchmark tool the output.txt came from - tool: 'cargo' - # Where the output from the benchmark tool is stored - output-file-path: output.txt - # Show alert with commit comment on detecting possible performance regression - alert-threshold: '200%' - # Workflow will fail when an alert happens - fail-on-alert: true - # GitHub API token to make a commit comment - github-token: ${{ secrets.GITHUB_TOKEN }} - # Enable alert commit comment - comment-on-alert: true - auto-push: true + - name: Install callgrind + run: sudo apt-get install -y valgrind + - name: Install iai-callgrind-runner + run: | + version=$(cargo metadata --format-version=1 |\ + jq '.packages[] | select(.name == "iai-callgrind").version' |\ + tr -d '"' + ) + cargo install iai-callgrind-runner --version $version + - uses: bencherdev/bencher@main + - name: Run Bencher + run: | + bencher run --adapter rust_iai_callgrind --err "cargo bench" diff --git a/Cargo.toml b/Cargo.toml index bc04b052..dd4616b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,14 +36,14 @@ hound = { git = "https://github.com/ruuda/hound.git", rev = "02e66effb33683d # tag_writer example structopt = { version = "0.3.26", default-features = false } tempfile = "3.9.0" - -# Pretty heavy dependency, we don't want this compiling for test/doc runs -[target.'cfg(bench)'.dev-dependencies] -criterion = { version = "0.5.1", features = ["html_reports"] } +iai-callgrind = "0.10.2" [lib] bench = false +[profile.bench] +debug = true + [[bench]] name = "read_file" path = "benches/read_file.rs" diff --git a/benches/create_tag.rs b/benches/create_tag.rs index d873fe82..86b9aebf 100644 --- a/benches/create_tag.rs +++ b/benches/create_tag.rs @@ -7,126 +7,120 @@ use lofty::mp4::Ilst; use lofty::ogg::VorbisComments; use lofty::{Accessor, MimeType, Picture, PictureType, TagExt, WriteOptions}; -use criterion::{criterion_group, criterion_main, Criterion}; +use iai_callgrind::{library_benchmark, library_benchmark_group, main}; const ENCODER: &str = "Lavf57.56.101"; macro_rules! bench_tag_write { - ($c:ident, [$(($NAME:literal, $tag:ty, |$tag_:ident| $extra_block:block)),+ $(,)?]) => { - let mut g = $c.benchmark_group("Tag writing"); - + ([$(($NAME:ident, $tag:ty, |$tag_:ident| $extra_block:block)),+ $(,)?]) => { $( - g.bench_function( - $NAME, - |b| b.iter(|| { - let mut v = Vec::new(); - let mut $tag_ = <$tag>::default(); + #[library_benchmark] + fn $NAME() { + let mut v = Vec::new(); + let mut $tag_ = <$tag>::default(); - $tag_.set_artist(String::from("Dave Eddy")); - $tag_.set_title(String::from("TempleOS Hymn Risen (Remix)")); - $tag_.set_album(String::from("Summer")); - $tag_.set_year(2017); - $tag_.set_track(1); - $tag_.set_genre(String::from("Electronic")); - $extra_block - $tag_.dump_to(&mut v, WriteOptions::default()).unwrap(); - }) - ); + $tag_.set_artist(String::from("Dave Eddy")); + $tag_.set_title(String::from("TempleOS Hymn Risen (Remix)")); + $tag_.set_album(String::from("Summer")); + $tag_.set_year(2017); + $tag_.set_track(1); + $tag_.set_genre(String::from("Electronic")); + $extra_block; + $tag_.dump_to(&mut v, WriteOptions::default()).unwrap(); + } )+ } } -fn bench_write(c: &mut Criterion) { - bench_tag_write!( - c, - [ - ("AIFF Text Chunks", AIFFTextChunks, |tag| {}), - ("APEv2", ApeTag, |tag| { - use lofty::ape::ApeItem; - use lofty::ItemValue; +bench_tag_write!([ + (aiff_text_chunks, AIFFTextChunks, |tag| {}), + (apev2, ApeTag, |tag| { + use lofty::ape::ApeItem; + use lofty::ItemValue; - let picture = Picture::new_unchecked( - PictureType::CoverFront, - Some(MimeType::Jpeg), - None, - include_bytes!("../benches_assets/cover.jpg").to_vec(), - ); + let picture = Picture::new_unchecked( + PictureType::CoverFront, + Some(MimeType::Jpeg), + None, + include_bytes!("../benches_assets/cover.jpg").to_vec(), + ); - tag.insert( - ApeItem::new( - String::from("Cover (Front)"), - ItemValue::Binary(picture.as_ape_bytes()), - ) - .unwrap(), - ); - tag.insert( - ApeItem::new( - String::from("Encoder"), - ItemValue::Text(String::from(ENCODER)), - ) - .unwrap(), - ); - }), - ("ID3v2", Id3v2Tag, |tag| { - use lofty::id3::v2::{Frame, FrameFlags, TextInformationFrame}; - use lofty::TextEncoding; + tag.insert( + ApeItem::new( + String::from("Cover (Front)"), + ItemValue::Binary(picture.as_ape_bytes()), + ) + .unwrap(), + ); + tag.insert( + ApeItem::new( + String::from("Encoder"), + ItemValue::Text(String::from(ENCODER)), + ) + .unwrap(), + ); + }), + (id3v2, Id3v2Tag, |tag| { + use lofty::id3::v2::{Frame, FrameFlags, TextInformationFrame}; + use lofty::TextEncoding; - let picture = Picture::new_unchecked( - PictureType::CoverFront, - Some(MimeType::Jpeg), - None, - include_bytes!("../benches_assets/cover.jpg").to_vec(), - ); + let picture = Picture::new_unchecked( + PictureType::CoverFront, + Some(MimeType::Jpeg), + None, + include_bytes!("../benches_assets/cover.jpg").to_vec(), + ); - tag.insert_picture(picture); - tag.insert( - Frame::new( - "TSSE", - TextInformationFrame { - encoding: TextEncoding::Latin1, - value: String::from(ENCODER), - }, - FrameFlags::default(), - ) - .unwrap(), - ); - }), - ("ID3v1", Id3v1Tag, |tag| {}), - ("MP4 Ilst", Ilst, |tag| { - use lofty::mp4::{Atom, AtomData, AtomIdent}; + tag.insert_picture(picture); + tag.insert( + Frame::new( + "TSSE", + TextInformationFrame { + encoding: TextEncoding::Latin1, + value: String::from(ENCODER), + }, + FrameFlags::default(), + ) + .unwrap(), + ); + }), + (id3v1, Id3v1Tag, |tag| {}), + (ilst, Ilst, |tag| { + use lofty::mp4::{Atom, AtomData, AtomIdent}; - let picture = Picture::new_unchecked( - PictureType::CoverFront, - Some(MimeType::Jpeg), - None, - include_bytes!("../benches_assets/cover.jpg").to_vec(), - ); + let picture = Picture::new_unchecked( + PictureType::CoverFront, + Some(MimeType::Jpeg), + None, + include_bytes!("../benches_assets/cover.jpg").to_vec(), + ); - tag.insert_picture(picture); - tag.insert(Atom::new( - AtomIdent::Fourcc(*b"\xa9too"), - AtomData::UTF8(String::from(ENCODER)), - )); - }), - ("RIFF INFO", RIFFInfoList, |tag| { - tag.insert(String::from("ISFT"), String::from(ENCODER)); - }), - ("Vorbis Comments", VorbisComments, |tag| { - use lofty::ogg::OggPictureStorage; + tag.insert_picture(picture); + tag.insert(Atom::new( + AtomIdent::Fourcc(*b"\xa9too"), + AtomData::UTF8(String::from(ENCODER)), + )); + }), + (riff_info, RIFFInfoList, |tag| { + tag.insert(String::from("ISFT"), String::from(ENCODER)); + }), + (vorbis_comments, VorbisComments, |tag| { + use lofty::ogg::OggPictureStorage; - let picture = Picture::new_unchecked( - PictureType::CoverFront, - Some(MimeType::Jpeg), - None, - include_bytes!("../benches_assets/cover.jpg").to_vec(), - ); + let picture = Picture::new_unchecked( + PictureType::CoverFront, + Some(MimeType::Jpeg), + None, + include_bytes!("../benches_assets/cover.jpg").to_vec(), + ); - let _ = tag.insert_picture(picture, None).unwrap(); - tag.push(String::from("ENCODER"), String::from(ENCODER)); - }), - ] - ); -} + let _ = tag.insert_picture(picture, None).unwrap(); + tag.push(String::from("ENCODER"), String::from(ENCODER)); + }), +]); -criterion_group!(benches, bench_write); -criterion_main!(benches); +library_benchmark_group!( + name = tag_writing; + benchmarks = aiff_text_chunks, apev2, id3v2, id3v1, ilst, riff_info, vorbis_comments +); +main!(library_benchmark_groups = tag_writing); diff --git a/benches/read_file.rs b/benches/read_file.rs index bc32651b..d567cda1 100644 --- a/benches/read_file.rs +++ b/benches/read_file.rs @@ -1,68 +1,65 @@ use lofty::{ParseOptions, Probe}; -use criterion::{criterion_group, criterion_main, Criterion}; +use iai_callgrind::{library_benchmark, library_benchmark_group, main}; +use std::hint::black_box; use std::io::Cursor; macro_rules! test_read_file { - ($c:ident, [$(($NAME:ident, $path:expr)),+ $(,)?]) => { - let mut g = $c.benchmark_group("File reading (Inferred from Content)"); - + ([$(($NAME:ident, $path:expr)),+ $(,)?]) => { $( - const $NAME: &[u8] = include_bytes!($path); + paste::paste! { + #[library_benchmark] + fn [<$NAME:lower>]() { + const $NAME: &[u8] = include_bytes!($path); - g.bench_function( - stringify!($NAME), - |b| b.iter(|| { - Probe::new(Cursor::new($NAME)) - .options(ParseOptions::new()) - .guess_file_type() - .unwrap() - .read() - .unwrap() - }) - ); + black_box(Probe::new(Cursor::new($NAME)) + .options(ParseOptions::new()) + .guess_file_type() + .unwrap() + .read() + .unwrap()); + } + } )+ } } -fn content_infer_read(c: &mut Criterion) { - test_read_file!( - c, - [ - (AAC, "../benches_assets/01 TempleOS Hymn Risen (Remix).aac"), - ( - AIFF, - "../benches_assets/01 TempleOS Hymn Risen (Remix).aiff" - ), - (APE, "../benches_assets/01 TempleOS Hymn Risen (Remix).ape"), - ( - FLAC, - "../benches_assets/01 TempleOS Hymn Risen (Remix).flac" - ), - (MP4, "../benches_assets/01 TempleOS Hymn Risen (Remix).m4a"), - (MP3, "../benches_assets/01 TempleOS Hymn Risen (Remix).mp3"), - (MPC, "../benches_assets/01 TempleOS Hymn Risen (Remix).mpc"), - ( - OPUS, - "../benches_assets/01 TempleOS Hymn Risen (Remix).opus" - ), - (RIFF, "../benches_assets/01 TempleOS Hymn Risen (Remix).wav"), - ( - SPEEX, - "../benches_assets/01 TempleOS Hymn Risen (Remix).spx" - ), - ( - VORBIS, - "../benches_assets/01 TempleOS Hymn Risen (Remix).ogg" - ), - ( - WAVPACK, - "../benches_assets/01 TempleOS Hymn Risen (Remix).wv" - ), - ] - ); -} +test_read_file!([ + (AAC, "../benches_assets/01 TempleOS Hymn Risen (Remix).aac"), + ( + AIFF, + "../benches_assets/01 TempleOS Hymn Risen (Remix).aiff" + ), + (APE, "../benches_assets/01 TempleOS Hymn Risen (Remix).ape"), + ( + FLAC, + "../benches_assets/01 TempleOS Hymn Risen (Remix).flac" + ), + (MP4, "../benches_assets/01 TempleOS Hymn Risen (Remix).m4a"), + (MP3, "../benches_assets/01 TempleOS Hymn Risen (Remix).mp3"), + (MPC, "../benches_assets/01 TempleOS Hymn Risen (Remix).mpc"), + ( + OPUS, + "../benches_assets/01 TempleOS Hymn Risen (Remix).opus" + ), + (RIFF, "../benches_assets/01 TempleOS Hymn Risen (Remix).wav"), + ( + SPEEX, + "../benches_assets/01 TempleOS Hymn Risen (Remix).spx" + ), + ( + VORBIS, + "../benches_assets/01 TempleOS Hymn Risen (Remix).ogg" + ), + ( + WAVPACK, + "../benches_assets/01 TempleOS Hymn Risen (Remix).wv" + ), +]); -criterion_group!(benches, content_infer_read); -criterion_main!(benches); +library_benchmark_group!( + name = file_reading; + benchmarks = aac, aiff, ape, flac, mp4, mp3, mpc, opus, riff, speex, vorbis, wavpack +); +main!(library_benchmark_groups = file_reading);