// This file is part of ICU4X. For terms of use, please see the file // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). use criterion::{black_box, criterion_group, criterion_main, Criterion}; use std::fmt; use writeable::LengthHint; use writeable::Writeable; /// A sample type implementing Writeable struct WriteableMessage<'s> { message: &'s str, } impl Writeable for WriteableMessage<'_> { fn write_to(&self, sink: &mut W) -> fmt::Result { sink.write_str(self.message) } fn writeable_length_hint(&self) -> LengthHint { LengthHint::exact(self.message.len()) } } writeable::impl_display_with_writeable!(WriteableMessage<'_>); /// A sample type implementing Display #[cfg(feature = "bench")] struct DisplayMessage<'s> { message: &'s str, } #[cfg(feature = "bench")] impl fmt::Display for DisplayMessage<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(self.message) } } /// A sample type that contains multiple fields #[cfg(feature = "bench")] struct ComplexWriteable<'a> { prefix: &'a str, n0: usize, infix: &'a str, n1: usize, suffix: &'a str, } #[cfg(feature = "bench")] impl Writeable for ComplexWriteable<'_> { fn write_to(&self, sink: &mut W) -> fmt::Result { self.prefix.write_to(sink)?; self.n0.write_to(sink)?; self.infix.write_to(sink)?; self.n1.write_to(sink)?; self.suffix.write_to(sink)?; Ok(()) } fn writeable_length_hint(&self) -> LengthHint { self.prefix.writeable_length_hint() + self.n0.writeable_length_hint() + self.infix.writeable_length_hint() + self.n1.writeable_length_hint() + self.suffix.writeable_length_hint() } } #[cfg(feature = "bench")] writeable::impl_display_with_writeable!(ComplexWriteable<'_>); const SHORT_STR: &str = "short"; const MEDIUM_STR: &str = "this is a medium-length string"; const LONG_STR: &str = "this string is very very very very very very very very very very very very very very very very very very very very very very very very long"; #[cfg(feature = "bench")] const LONG_OVERLAP_STR: &str = "this string is very very very very very very very long but different"; fn overview_bench(c: &mut Criterion) { c.bench_function("writeable/overview", |b| { b.iter(|| { // This benchmark runs to_string on short, medium, and long strings in one batch. WriteableMessage { message: black_box(SHORT_STR), } .write_to_string(); WriteableMessage { message: black_box(MEDIUM_STR), } .write_to_string(); WriteableMessage { message: black_box(LONG_STR), } .write_to_string(); }); }); #[cfg(feature = "bench")] { writeable_benches(c); writeable_dyn_benches(c); display_benches(c); complex_benches(c); } } #[cfg(feature = "bench")] fn writeable_benches(c: &mut Criterion) { c.bench_function("writeable/to_string/short", |b| { b.iter(|| { WriteableMessage { message: black_box(SHORT_STR), } .write_to_string() .into_owned() }); }); c.bench_function("writeable/to_string/medium", |b| { b.iter(|| { WriteableMessage { message: black_box(MEDIUM_STR), } .write_to_string() .into_owned() }); }); c.bench_function("writeable/to_string/long", |b| { b.iter(|| { WriteableMessage { message: black_box(LONG_STR), } .write_to_string() .into_owned() }); }); c.bench_function("writeable/cmp_str", |b| { b.iter(|| { let short = black_box(SHORT_STR); let medium = black_box(MEDIUM_STR); let long = black_box(LONG_STR); let long_overlap = black_box(LONG_OVERLAP_STR); [short, medium, long, long_overlap].map(|s1| { [short, medium, long, long_overlap].map(|s2| { let message = WriteableMessage { message: s1 }; writeable::cmp_str(&message, s2) }) }) }); }); } #[cfg(feature = "bench")] fn writeable_dyn_benches(c: &mut Criterion) { // Same as `write_to_string`, but casts to a `dyn fmt::Write` fn writeable_dyn_to_string(w: &impl Writeable) -> String { let mut output = String::with_capacity(w.writeable_length_hint().capacity()); w.write_to(&mut output as &mut dyn fmt::Write) .expect("impl Write for String is infallible"); output } c.bench_function("writeable_dyn/to_string/short", |b| { b.iter(|| { writeable_dyn_to_string(&WriteableMessage { message: black_box(SHORT_STR), }) }); }); c.bench_function("writeable_dyn/to_string/medium", |b| { b.iter(|| { writeable_dyn_to_string(&WriteableMessage { message: black_box(MEDIUM_STR), }) }); }); c.bench_function("writeable_dyn/to_string/long", |b| { b.iter(|| { writeable_dyn_to_string(&WriteableMessage { message: black_box(LONG_STR), }) }); }); } #[cfg(feature = "bench")] fn display_benches(c: &mut Criterion) { c.bench_function("display/to_string/short", |b| { b.iter(|| { DisplayMessage { message: black_box(SHORT_STR), } .to_string() }); }); c.bench_function("display/to_string/medium", |b| { b.iter(|| { DisplayMessage { message: black_box(MEDIUM_STR), } .to_string() }); }); c.bench_function("display/to_string/long", |b| { b.iter(|| { DisplayMessage { message: black_box(LONG_STR), } .to_string() }); }); } #[cfg(feature = "bench")] fn complex_benches(c: &mut Criterion) { const COMPLEX_WRITEABLE_MEDIUM: ComplexWriteable = ComplexWriteable { prefix: "There are ", n0: 55, infix: " apples and ", n1: 8124, suffix: " oranges", }; c.bench_function("complex/write_to_string/medium", |b| { b.iter(|| { black_box(COMPLEX_WRITEABLE_MEDIUM) .write_to_string() .into_owned() }); }); c.bench_function("complex/display_to_string/medium", |b| { b.iter(|| black_box(COMPLEX_WRITEABLE_MEDIUM).to_string()); }); const REFERENCE_STRS: [&str; 6] = [ "There are 55 apples and 8124 oranges", "There are 55 apples and 0 oranges", "There are no apples", SHORT_STR, MEDIUM_STR, LONG_STR, ]; c.bench_function("complex/cmp_str", |b| { b.iter(|| { black_box(REFERENCE_STRS) .map(|s| writeable::cmp_str(black_box(&COMPLEX_WRITEABLE_MEDIUM), s)) }); }); } criterion_group!(benches, overview_bench,); criterion_main!(benches);