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