• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // This file is part of ICU4X. For terms of use, please see the file
2 // called LICENSE at the top level of the ICU4X source tree
3 // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4 
5 use criterion::{black_box, criterion_group, criterion_main, Criterion};
6 use std::fmt;
7 use writeable::LengthHint;
8 use writeable::Writeable;
9 
10 /// A sample type implementing Writeable
11 struct WriteableMessage<'s> {
12     message: &'s str,
13 }
14 
15 impl Writeable for WriteableMessage<'_> {
write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result16     fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
17         sink.write_str(self.message)
18     }
19 
writeable_length_hint(&self) -> LengthHint20     fn writeable_length_hint(&self) -> LengthHint {
21         LengthHint::exact(self.message.len())
22     }
23 }
24 
25 writeable::impl_display_with_writeable!(WriteableMessage<'_>);
26 
27 /// A sample type implementing Display
28 struct DisplayMessage<'s> {
29     message: &'s str,
30 }
31 
32 impl fmt::Display for DisplayMessage<'_> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result33     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34         f.write_str(self.message)
35     }
36 }
37 
38 /// A sample type that contains multiple fields
39 struct ComplexWriteable<'a> {
40     prefix: &'a str,
41     n0: usize,
42     infix: &'a str,
43     n1: usize,
44     suffix: &'a str,
45 }
46 
47 impl Writeable for ComplexWriteable<'_> {
write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result48     fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
49         self.prefix.write_to(sink)?;
50         self.n0.write_to(sink)?;
51         self.infix.write_to(sink)?;
52         self.n1.write_to(sink)?;
53         self.suffix.write_to(sink)?;
54         Ok(())
55     }
56 
writeable_length_hint(&self) -> LengthHint57     fn writeable_length_hint(&self) -> LengthHint {
58         self.prefix.writeable_length_hint()
59             + self.n0.writeable_length_hint()
60             + self.infix.writeable_length_hint()
61             + self.n1.writeable_length_hint()
62             + self.suffix.writeable_length_hint()
63     }
64 }
65 
66 writeable::impl_display_with_writeable!(ComplexWriteable<'_>);
67 
68 const SHORT_STR: &str = "short";
69 const MEDIUM_STR: &str = "this is a medium-length string";
70 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";
71 const LONG_OVERLAP_STR: &str =
72     "this string is very very very very very very very long but different";
73 
overview_bench(c: &mut Criterion)74 fn overview_bench(c: &mut Criterion) {
75     c.bench_function("writeable/overview", |b| {
76         b.iter(|| {
77             // This benchmark runs to_string on short, medium, and long strings in one batch.
78             WriteableMessage {
79                 message: black_box(SHORT_STR),
80             }
81             .write_to_string();
82             WriteableMessage {
83                 message: black_box(MEDIUM_STR),
84             }
85             .write_to_string();
86             WriteableMessage {
87                 message: black_box(LONG_STR),
88             }
89             .write_to_string();
90         });
91     });
92 
93     {
94         writeable_benches(c);
95         writeable_dyn_benches(c);
96         display_benches(c);
97         complex_benches(c);
98     }
99 }
100 
writeable_benches(c: &mut Criterion)101 fn writeable_benches(c: &mut Criterion) {
102     c.bench_function("writeable/to_string/short", |b| {
103         b.iter(|| {
104             WriteableMessage {
105                 message: black_box(SHORT_STR),
106             }
107             .write_to_string()
108             .into_owned()
109         });
110     });
111     c.bench_function("writeable/to_string/medium", |b| {
112         b.iter(|| {
113             WriteableMessage {
114                 message: black_box(MEDIUM_STR),
115             }
116             .write_to_string()
117             .into_owned()
118         });
119     });
120     c.bench_function("writeable/to_string/long", |b| {
121         b.iter(|| {
122             WriteableMessage {
123                 message: black_box(LONG_STR),
124             }
125             .write_to_string()
126             .into_owned()
127         });
128     });
129     c.bench_function("writeable/cmp_str", |b| {
130         b.iter(|| {
131             let short = black_box(SHORT_STR);
132             let medium = black_box(MEDIUM_STR);
133             let long = black_box(LONG_STR);
134             let long_overlap = black_box(LONG_OVERLAP_STR);
135             [short, medium, long, long_overlap].map(|s1| {
136                 [short, medium, long, long_overlap].map(|s2| {
137                     let message = WriteableMessage { message: s1 };
138                     writeable::cmp_str(&message, s2)
139                 })
140             })
141         });
142     });
143 }
144 
writeable_dyn_benches(c: &mut Criterion)145 fn writeable_dyn_benches(c: &mut Criterion) {
146     // Same as `write_to_string`, but casts to a `dyn fmt::Write`
147     fn writeable_dyn_to_string(w: &impl Writeable) -> String {
148         let mut output = String::with_capacity(w.writeable_length_hint().capacity());
149         w.write_to(&mut output as &mut dyn fmt::Write)
150             .expect("impl Write for String is infallible");
151         output
152     }
153 
154     c.bench_function("writeable_dyn/to_string/short", |b| {
155         b.iter(|| {
156             writeable_dyn_to_string(&WriteableMessage {
157                 message: black_box(SHORT_STR),
158             })
159         });
160     });
161     c.bench_function("writeable_dyn/to_string/medium", |b| {
162         b.iter(|| {
163             writeable_dyn_to_string(&WriteableMessage {
164                 message: black_box(MEDIUM_STR),
165             })
166         });
167     });
168     c.bench_function("writeable_dyn/to_string/long", |b| {
169         b.iter(|| {
170             writeable_dyn_to_string(&WriteableMessage {
171                 message: black_box(LONG_STR),
172             })
173         });
174     });
175 }
176 
display_benches(c: &mut Criterion)177 fn display_benches(c: &mut Criterion) {
178     c.bench_function("display/to_string/short", |b| {
179         b.iter(|| {
180             DisplayMessage {
181                 message: black_box(SHORT_STR),
182             }
183             .to_string()
184         });
185     });
186     c.bench_function("display/to_string/medium", |b| {
187         b.iter(|| {
188             DisplayMessage {
189                 message: black_box(MEDIUM_STR),
190             }
191             .to_string()
192         });
193     });
194     c.bench_function("display/to_string/long", |b| {
195         b.iter(|| {
196             DisplayMessage {
197                 message: black_box(LONG_STR),
198             }
199             .to_string()
200         });
201     });
202 }
203 
complex_benches(c: &mut Criterion)204 fn complex_benches(c: &mut Criterion) {
205     const COMPLEX_WRITEABLE_MEDIUM: ComplexWriteable = ComplexWriteable {
206         prefix: "There are ",
207         n0: 55,
208         infix: " apples and ",
209         n1: 8124,
210         suffix: " oranges",
211     };
212     c.bench_function("complex/write_to_string/medium", |b| {
213         b.iter(|| {
214             black_box(COMPLEX_WRITEABLE_MEDIUM)
215                 .write_to_string()
216                 .into_owned()
217         });
218     });
219     c.bench_function("complex/display_to_string/medium", |b| {
220         b.iter(|| black_box(COMPLEX_WRITEABLE_MEDIUM).to_string());
221     });
222     const REFERENCE_STRS: [&str; 6] = [
223         "There are 55 apples and 8124 oranges",
224         "There are 55 apples and 0 oranges",
225         "There are no apples",
226         SHORT_STR,
227         MEDIUM_STR,
228         LONG_STR,
229     ];
230     c.bench_function("complex/cmp_str", |b| {
231         b.iter(|| {
232             black_box(REFERENCE_STRS)
233                 .map(|s| writeable::cmp_str(black_box(&COMPLEX_WRITEABLE_MEDIUM), s))
234         });
235     });
236 }
237 
238 criterion_group!(benches, overview_bench,);
239 criterion_main!(benches);
240