1 //! Benchmarks that compare TinyVec to SmallVec
2 //!
3 //! All the following commentary is based on the latest nightly at the time:
4 //! rustc 1.55.0 (c8dfcfe04 2021-09-06).
5 //!
6 //! Some of these benchmarks are just a few instructions, so we put our own for loop inside
7 //! the criterion::Bencher::iter call. This seems to improve the stability of measurements, and it
8 //! has the wonderful side effect of making the emitted assembly easier to follow. Some of these
9 //! benchmarks are totally inlined so that there are no calls at all in the hot path, so finding
10 //! this for loop is an easy way to find your way around the emitted assembly.
11 //!
12 //! The clear method is cheaper to call for arrays of elements without a Drop impl, so wherever
13 //! possible we reuse a single object in the benchmark loop, with a clear + black_box on each
14 //! iteration in an attempt to not make that visible to the optimizer.
15 //!
16 //! We always call black_box(&v), instead of v = black_box(v) because the latter does a move of the
17 //! inline array, which is linear in the size of the array and thus varies based on the array type
18 //! being benchmarked, and this move can be more expensive than the function we're trying to
19 //! benchmark.
20 //!
21 //! We also black_box the input to each method call. This has a significant effect on the assembly
22 //! emitted, for example if we do not black_box the range we iterate over in the ::push benchmarks,
23 //! the loop is unrolled. It's not entirely clear if it's better to black_box the iterator that
24 //! yields the items being pushed, or to black_box at a deeper level: v.push(black_box(i)) for
25 //! example. Anecdotally, it seems like the latter approach produces unreasonably bad assembly.
26 //!
27 
28 use criterion::{black_box, criterion_group, criterion_main, Criterion};
29 use smallvec::SmallVec;
30 use std::iter::FromIterator;
31 use tinyvec::TinyVec;
32 
33 const ITERS: usize = 10_000;
34 
35 macro_rules! tinyvec_benches {
36   ($c:expr, $type:ty ; $len:expr) => {{
37     let mut g = $c.benchmark_group(concat!(
38       "TinyVec_",
39       stringify!($type),
40       "_",
41       stringify!($len)
42     ));
43 
44     g.bench_function(
45       concat!(
46         "TinyVec<[",
47         stringify!($type),
48         "; ",
49         stringify!($len),
50         "]>::default"
51       ),
52       |b| {
53         b.iter(|| {
54           for _ in 0..ITERS {
55             let v: TinyVec<[$type; $len]> = TinyVec::default();
56             black_box(&v);
57           }
58         });
59       },
60     );
61 
62     g.bench_function(
63       concat!(
64         "TinyVec<[",
65         stringify!($type),
66         "; ",
67         stringify!($len),
68         "]>::clone"
69       ),
70       |b| {
71         b.iter(|| {
72           let outer: TinyVec<[$type; $len]> =
73             black_box(TinyVec::from_iter(0..=($len as usize - 1) as _));
74           for _ in 0..ITERS {
75             let v = outer.clone();
76             black_box(&v);
77           }
78         });
79       },
80     );
81 
82     g.bench_function(
83       concat!(
84         "TinyVec<[",
85         stringify!($type),
86         "; ",
87         stringify!($len),
88         "]>::clear"
89       ),
90       |b| {
91         b.iter(|| {
92           let mut v: TinyVec<[$type; $len]> = TinyVec::default();
93           for _ in 0..ITERS {
94             v.clear();
95             black_box(&v);
96           }
97         });
98       },
99     );
100 
101     g.bench_function(
102       concat!(
103         "TinyVec<[",
104         stringify!($type),
105         "; ",
106         stringify!($len),
107         "]>::push"
108       ),
109       |b| {
110         b.iter(|| {
111           let mut v: TinyVec<[$type; $len]> = TinyVec::default();
112           for _ in 0..ITERS {
113             v.clear();
114             black_box(&v);
115             for i in black_box(0..=($len as usize - 1) as _) {
116               v.push(i);
117             }
118             black_box(&v);
119           }
120         });
121       },
122     );
123 
124     g.bench_function(
125       concat!(
126         "TinyVec<[",
127         stringify!($type),
128         "; ",
129         stringify!($len),
130         "]>::from_iter"
131       ),
132       |b| {
133         b.iter(|| {
134           for _ in 0..ITERS {
135             let v: TinyVec<[$type; $len]> =
136               TinyVec::from_iter(black_box(0..=($len as usize - 1) as _));
137             black_box(&v);
138           }
139         });
140       },
141     );
142 
143     g.bench_function(
144       concat!(
145         "TinyVec<[",
146         stringify!($type),
147         "; ",
148         stringify!($len),
149         "]>::from_slice"
150       ),
151       |b| {
152         b.iter(|| {
153           let data: &[$type] = &[0, 1, 2, 3, 4, 5, 6, 7];
154           for _ in 0..ITERS {
155             let v: TinyVec<[$type; $len]> = TinyVec::from(black_box(data));
156             black_box(&v);
157           }
158         });
159       },
160     );
161 
162     g.bench_function(
163       concat!(
164         "TinyVec<[",
165         stringify!($type),
166         "; ",
167         stringify!($len),
168         "]>::extend"
169       ),
170       |b| {
171         b.iter(|| {
172           let mut v: TinyVec<[$type; $len]> = black_box(TinyVec::default());
173           for _ in 0..ITERS {
174             v.clear();
175             black_box(&v);
176             v.extend(black_box(0..=($len as usize - 1) as _));
177             black_box(&v);
178           }
179         });
180       },
181     );
182 
183     g.bench_function(
184       concat!(
185         "TinyVec<[",
186         stringify!($type),
187         "; ",
188         stringify!($len),
189         "]>::extend_from_slice"
190       ),
191       |b| {
192         b.iter(|| {
193           let data: &[$type] = black_box(&[0, 1, 2, 3, 4, 5, 6, 7]);
194           let mut v: TinyVec<[$type; $len]> = black_box(TinyVec::default());
195           for _ in 0..ITERS {
196             v.clear();
197             black_box(&v);
198             v.extend_from_slice(data);
199             black_box(&v);
200           }
201         });
202       },
203     );
204 
205     g.bench_function(
206       concat!(
207         "TinyVec<[",
208         stringify!($type),
209         "; ",
210         stringify!($len),
211         "]>::insert"
212       ),
213       |b| {
214         b.iter(|| {
215           let mut v: TinyVec<[$type; $len]> = TinyVec::default();
216           for _ in 0..ITERS {
217             v.clear();
218             black_box(&v);
219             for i in black_box(0..=($len as usize - 1) as _) {
220               v.insert(i as usize, i);
221             }
222             black_box(&v);
223           }
224         });
225       },
226     );
227 
228     g.bench_function(
229       concat!(
230         "TinyVec<[",
231         stringify!($type),
232         "; ",
233         stringify!($len),
234         "]>::remove"
235       ),
236       |b| {
237         b.iter(|| {
238           let outer: TinyVec<[$type; $len]> =
239             black_box(TinyVec::from_iter(0..=($len as usize - 1) as _));
240           for _ in 0..ITERS {
241             let mut v = outer.clone();
242             for i in black_box((0..=($len as usize - 1) as _).rev()) {
243               v.remove(i);
244             }
245             black_box(&v);
246           }
247         });
248       },
249     );
250   }};
251 }
252 
tinyvec_benches(c: &mut Criterion)253 fn tinyvec_benches(c: &mut Criterion) {
254   tinyvec_benches!(c, u8; 8);
255   tinyvec_benches!(c, u8; 16);
256   tinyvec_benches!(c, u8; 32);
257   tinyvec_benches!(c, u8; 64);
258   tinyvec_benches!(c, u8; 128);
259   tinyvec_benches!(c, u8; 256);
260   tinyvec_benches!(c, u64; 2);
261   tinyvec_benches!(c, u64; 4);
262   tinyvec_benches!(c, u64; 8);
263   tinyvec_benches!(c, u64; 16);
264   tinyvec_benches!(c, u64; 32);
265 }
266 
267 macro_rules! smallvec_benches {
268   ($c:expr, $type:ty ; $len:expr) => {{
269     let mut g = $c.benchmark_group(concat!(
270       "SmallVec_",
271       stringify!($type),
272       "_",
273       stringify!($len)
274     ));
275 
276     g.bench_function(
277       concat!(
278         "SmallVec<[",
279         stringify!($type),
280         "; ",
281         stringify!($len),
282         "]>::default"
283       ),
284       |b| {
285         b.iter(|| {
286           for _ in 0..ITERS {
287             let v: SmallVec<[$type; $len]> = SmallVec::default();
288             black_box(&v);
289           }
290         });
291       },
292     );
293 
294     g.bench_function(
295       concat!(
296         "SmallVec<[",
297         stringify!($type),
298         "; ",
299         stringify!($len),
300         "]>::clone"
301       ),
302       |b| {
303         b.iter(|| {
304           let outer: SmallVec<[$type; $len]> =
305             black_box(SmallVec::from_iter(0..=($len as usize - 1) as _));
306           for _ in 0..ITERS {
307             let v = outer.clone();
308             black_box(&v);
309           }
310         });
311       },
312     );
313 
314     g.bench_function(
315       concat!(
316         "SmallVec<[",
317         stringify!($type),
318         "; ",
319         stringify!($len),
320         "]>::clear"
321       ),
322       |b| {
323         b.iter(|| {
324           let mut v: SmallVec<[$type; $len]> = SmallVec::default();
325           for _ in 0..ITERS {
326             v.clear();
327             black_box(&v);
328           }
329         });
330       },
331     );
332 
333     g.bench_function(
334       concat!(
335         "SmallVec<[",
336         stringify!($type),
337         "; ",
338         stringify!($len),
339         "]>::push"
340       ),
341       |b| {
342         b.iter(|| {
343           let mut v: SmallVec<[$type; $len]> = SmallVec::default();
344           for _ in 0..ITERS {
345             v.clear();
346             black_box(&v);
347             for i in black_box(0..=($len as usize - 1) as _) {
348               v.push(i);
349             }
350             black_box(&v);
351           }
352         });
353       },
354     );
355 
356     g.bench_function(
357       concat!(
358         "SmallVec<[",
359         stringify!($type),
360         "; ",
361         stringify!($len),
362         "]>::from_iter"
363       ),
364       |b| {
365         b.iter(|| {
366           for _ in 0..ITERS {
367             let v: SmallVec<[$type; $len]> =
368               SmallVec::from_iter(black_box(0..=($len as usize - 1) as _));
369             black_box(&v);
370           }
371         });
372       },
373     );
374 
375     g.bench_function(
376       concat!(
377         "SmallVec<[",
378         stringify!($type),
379         "; ",
380         stringify!($len),
381         "]>::from_slice"
382       ),
383       |b| {
384         b.iter(|| {
385           let data: &[$type] = &[0, 1, 2, 3, 4, 5, 6, 7];
386           for _ in 0..ITERS {
387             let v: SmallVec<[$type; $len]> = SmallVec::from(black_box(data));
388             black_box(&v);
389           }
390         });
391       },
392     );
393 
394     g.bench_function(
395       concat!(
396         "SmallVec<[",
397         stringify!($type),
398         "; ",
399         stringify!($len),
400         "]>::extend"
401       ),
402       |b| {
403         b.iter(|| {
404           let mut v: SmallVec<[$type; $len]> = black_box(SmallVec::default());
405           for _ in 0..ITERS {
406             v.clear();
407             black_box(&v);
408             v.extend(black_box(0..=($len as usize - 1) as _));
409             black_box(&v);
410           }
411         });
412       },
413     );
414 
415     g.bench_function(
416       concat!(
417         "SmallVec<[",
418         stringify!($type),
419         "; ",
420         stringify!($len),
421         "]>::extend_from_slice"
422       ),
423       |b| {
424         b.iter(|| {
425           let data: &[$type] = black_box(&[0, 1, 2, 3, 4, 5, 6, 7]);
426           let mut v: SmallVec<[$type; $len]> = black_box(SmallVec::default());
427           for _ in 0..ITERS {
428             v.clear();
429             black_box(&v);
430             v.extend_from_slice(data);
431             black_box(&v);
432           }
433         });
434       },
435     );
436 
437     g.bench_function(
438       concat!(
439         "SmallVec<[",
440         stringify!($type),
441         "; ",
442         stringify!($len),
443         "]>::insert"
444       ),
445       |b| {
446         b.iter(|| {
447           let mut v: SmallVec<[$type; $len]> = SmallVec::default();
448           for _ in 0..ITERS {
449             v.clear();
450             black_box(&v);
451             for i in black_box(0..=($len as usize - 1) as _) {
452               v.insert(i as usize, i);
453             }
454             black_box(&v);
455           }
456         });
457       },
458     );
459 
460     g.bench_function(
461       concat!(
462         "SmallVec<[",
463         stringify!($type),
464         "; ",
465         stringify!($len),
466         "]>::remove"
467       ),
468       |b| {
469         b.iter(|| {
470           let outer: SmallVec<[$type; $len]> =
471             black_box(SmallVec::from_iter(0..=($len as usize - 1) as _));
472           for _ in 0..ITERS {
473             let mut v = outer.clone();
474             for i in black_box((0..=($len as usize - 1) as _).rev()) {
475               v.remove(i);
476             }
477             black_box(&v);
478           }
479         });
480       },
481     );
482   }};
483 }
484 
smallvec_benches(c: &mut Criterion)485 fn smallvec_benches(c: &mut Criterion) {
486   smallvec_benches!(c, u8; 8);
487   smallvec_benches!(c, u8; 16);
488   smallvec_benches!(c, u8; 32);
489   smallvec_benches!(c, u8; 64);
490   smallvec_benches!(c, u8; 128);
491   smallvec_benches!(c, u8; 256);
492   smallvec_benches!(c, u64; 2);
493   smallvec_benches!(c, u64; 4);
494   smallvec_benches!(c, u64; 8);
495   smallvec_benches!(c, u64; 16);
496   smallvec_benches!(c, u64; 32);
497 }
498 
499 criterion_group!(benches, tinyvec_benches, smallvec_benches);
500 criterion_main!(benches);
501