smallvec.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  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. use criterion::{black_box, criterion_group, criterion_main, Criterion};
  28. use smallvec::SmallVec;
  29. use std::iter::FromIterator;
  30. use tinyvec::TinyVec;
  31. const ITERS: usize = 10_000;
  32. macro_rules! tinyvec_benches {
  33. ($c:expr, $type:ty ; $len:expr) => {{
  34. let mut g = $c.benchmark_group(concat!(
  35. "TinyVec_",
  36. stringify!($type),
  37. "_",
  38. stringify!($len)
  39. ));
  40. g.bench_function(
  41. concat!(
  42. "TinyVec<[",
  43. stringify!($type),
  44. "; ",
  45. stringify!($len),
  46. "]>::default"
  47. ),
  48. |b| {
  49. b.iter(|| {
  50. for _ in 0..ITERS {
  51. let v: TinyVec<[$type; $len]> = TinyVec::default();
  52. black_box(&v);
  53. }
  54. });
  55. },
  56. );
  57. g.bench_function(
  58. concat!(
  59. "TinyVec<[",
  60. stringify!($type),
  61. "; ",
  62. stringify!($len),
  63. "]>::clone"
  64. ),
  65. |b| {
  66. b.iter(|| {
  67. let outer: TinyVec<[$type; $len]> =
  68. black_box(TinyVec::from_iter(0..=($len as usize - 1) as _));
  69. for _ in 0..ITERS {
  70. let v = outer.clone();
  71. black_box(&v);
  72. }
  73. });
  74. },
  75. );
  76. g.bench_function(
  77. concat!(
  78. "TinyVec<[",
  79. stringify!($type),
  80. "; ",
  81. stringify!($len),
  82. "]>::clear"
  83. ),
  84. |b| {
  85. b.iter(|| {
  86. let mut v: TinyVec<[$type; $len]> = TinyVec::default();
  87. for _ in 0..ITERS {
  88. v.clear();
  89. black_box(&v);
  90. }
  91. });
  92. },
  93. );
  94. g.bench_function(
  95. concat!(
  96. "TinyVec<[",
  97. stringify!($type),
  98. "; ",
  99. stringify!($len),
  100. "]>::push"
  101. ),
  102. |b| {
  103. b.iter(|| {
  104. let mut v: TinyVec<[$type; $len]> = TinyVec::default();
  105. for _ in 0..ITERS {
  106. v.clear();
  107. black_box(&v);
  108. for i in black_box(0..=($len as usize - 1) as _) {
  109. v.push(i);
  110. }
  111. black_box(&v);
  112. }
  113. });
  114. },
  115. );
  116. g.bench_function(
  117. concat!(
  118. "TinyVec<[",
  119. stringify!($type),
  120. "; ",
  121. stringify!($len),
  122. "]>::from_iter"
  123. ),
  124. |b| {
  125. b.iter(|| {
  126. for _ in 0..ITERS {
  127. let v: TinyVec<[$type; $len]> =
  128. TinyVec::from_iter(black_box(0..=($len as usize - 1) as _));
  129. black_box(&v);
  130. }
  131. });
  132. },
  133. );
  134. g.bench_function(
  135. concat!(
  136. "TinyVec<[",
  137. stringify!($type),
  138. "; ",
  139. stringify!($len),
  140. "]>::from_slice"
  141. ),
  142. |b| {
  143. b.iter(|| {
  144. let data: &[$type] = &[0, 1, 2, 3, 4, 5, 6, 7];
  145. for _ in 0..ITERS {
  146. let v: TinyVec<[$type; $len]> = TinyVec::from(black_box(data));
  147. black_box(&v);
  148. }
  149. });
  150. },
  151. );
  152. g.bench_function(
  153. concat!(
  154. "TinyVec<[",
  155. stringify!($type),
  156. "; ",
  157. stringify!($len),
  158. "]>::extend"
  159. ),
  160. |b| {
  161. b.iter(|| {
  162. let mut v: TinyVec<[$type; $len]> = black_box(TinyVec::default());
  163. for _ in 0..ITERS {
  164. v.clear();
  165. black_box(&v);
  166. v.extend(black_box(0..=($len as usize - 1) as _));
  167. black_box(&v);
  168. }
  169. });
  170. },
  171. );
  172. g.bench_function(
  173. concat!(
  174. "TinyVec<[",
  175. stringify!($type),
  176. "; ",
  177. stringify!($len),
  178. "]>::extend_from_slice"
  179. ),
  180. |b| {
  181. b.iter(|| {
  182. let data: &[$type] = black_box(&[0, 1, 2, 3, 4, 5, 6, 7]);
  183. let mut v: TinyVec<[$type; $len]> = black_box(TinyVec::default());
  184. for _ in 0..ITERS {
  185. v.clear();
  186. black_box(&v);
  187. v.extend_from_slice(data);
  188. black_box(&v);
  189. }
  190. });
  191. },
  192. );
  193. g.bench_function(
  194. concat!(
  195. "TinyVec<[",
  196. stringify!($type),
  197. "; ",
  198. stringify!($len),
  199. "]>::insert"
  200. ),
  201. |b| {
  202. b.iter(|| {
  203. let mut v: TinyVec<[$type; $len]> = TinyVec::default();
  204. for _ in 0..ITERS {
  205. v.clear();
  206. black_box(&v);
  207. for i in black_box(0..=($len as usize - 1) as _) {
  208. v.insert(i as usize, i);
  209. }
  210. black_box(&v);
  211. }
  212. });
  213. },
  214. );
  215. g.bench_function(
  216. concat!(
  217. "TinyVec<[",
  218. stringify!($type),
  219. "; ",
  220. stringify!($len),
  221. "]>::remove"
  222. ),
  223. |b| {
  224. b.iter(|| {
  225. let outer: TinyVec<[$type; $len]> =
  226. black_box(TinyVec::from_iter(0..=($len as usize - 1) as _));
  227. for _ in 0..ITERS {
  228. let mut v = outer.clone();
  229. for i in black_box((0..=($len as usize - 1) as _).rev()) {
  230. v.remove(i);
  231. }
  232. black_box(&v);
  233. }
  234. });
  235. },
  236. );
  237. }};
  238. }
  239. fn tinyvec_benches(c: &mut Criterion) {
  240. tinyvec_benches!(c, u8; 8);
  241. tinyvec_benches!(c, u8; 16);
  242. tinyvec_benches!(c, u8; 32);
  243. tinyvec_benches!(c, u8; 64);
  244. tinyvec_benches!(c, u8; 128);
  245. tinyvec_benches!(c, u8; 256);
  246. tinyvec_benches!(c, u64; 2);
  247. tinyvec_benches!(c, u64; 4);
  248. tinyvec_benches!(c, u64; 8);
  249. tinyvec_benches!(c, u64; 16);
  250. tinyvec_benches!(c, u64; 32);
  251. }
  252. macro_rules! smallvec_benches {
  253. ($c:expr, $type:ty ; $len:expr) => {{
  254. let mut g = $c.benchmark_group(concat!(
  255. "SmallVec_",
  256. stringify!($type),
  257. "_",
  258. stringify!($len)
  259. ));
  260. g.bench_function(
  261. concat!(
  262. "SmallVec<[",
  263. stringify!($type),
  264. "; ",
  265. stringify!($len),
  266. "]>::default"
  267. ),
  268. |b| {
  269. b.iter(|| {
  270. for _ in 0..ITERS {
  271. let v: SmallVec<[$type; $len]> = SmallVec::default();
  272. black_box(&v);
  273. }
  274. });
  275. },
  276. );
  277. g.bench_function(
  278. concat!(
  279. "SmallVec<[",
  280. stringify!($type),
  281. "; ",
  282. stringify!($len),
  283. "]>::clone"
  284. ),
  285. |b| {
  286. b.iter(|| {
  287. let outer: SmallVec<[$type; $len]> =
  288. black_box(SmallVec::from_iter(0..=($len as usize - 1) as _));
  289. for _ in 0..ITERS {
  290. let v = outer.clone();
  291. black_box(&v);
  292. }
  293. });
  294. },
  295. );
  296. g.bench_function(
  297. concat!(
  298. "SmallVec<[",
  299. stringify!($type),
  300. "; ",
  301. stringify!($len),
  302. "]>::clear"
  303. ),
  304. |b| {
  305. b.iter(|| {
  306. let mut v: SmallVec<[$type; $len]> = SmallVec::default();
  307. for _ in 0..ITERS {
  308. v.clear();
  309. black_box(&v);
  310. }
  311. });
  312. },
  313. );
  314. g.bench_function(
  315. concat!(
  316. "SmallVec<[",
  317. stringify!($type),
  318. "; ",
  319. stringify!($len),
  320. "]>::push"
  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. for i in black_box(0..=($len as usize - 1) as _) {
  329. v.push(i);
  330. }
  331. black_box(&v);
  332. }
  333. });
  334. },
  335. );
  336. g.bench_function(
  337. concat!(
  338. "SmallVec<[",
  339. stringify!($type),
  340. "; ",
  341. stringify!($len),
  342. "]>::from_iter"
  343. ),
  344. |b| {
  345. b.iter(|| {
  346. for _ in 0..ITERS {
  347. let v: SmallVec<[$type; $len]> =
  348. SmallVec::from_iter(black_box(0..=($len as usize - 1) as _));
  349. black_box(&v);
  350. }
  351. });
  352. },
  353. );
  354. g.bench_function(
  355. concat!(
  356. "SmallVec<[",
  357. stringify!($type),
  358. "; ",
  359. stringify!($len),
  360. "]>::from_slice"
  361. ),
  362. |b| {
  363. b.iter(|| {
  364. let data: &[$type] = &[0, 1, 2, 3, 4, 5, 6, 7];
  365. for _ in 0..ITERS {
  366. let v: SmallVec<[$type; $len]> = SmallVec::from(black_box(data));
  367. black_box(&v);
  368. }
  369. });
  370. },
  371. );
  372. g.bench_function(
  373. concat!(
  374. "SmallVec<[",
  375. stringify!($type),
  376. "; ",
  377. stringify!($len),
  378. "]>::extend"
  379. ),
  380. |b| {
  381. b.iter(|| {
  382. let mut v: SmallVec<[$type; $len]> = black_box(SmallVec::default());
  383. for _ in 0..ITERS {
  384. v.clear();
  385. black_box(&v);
  386. v.extend(black_box(0..=($len as usize - 1) as _));
  387. black_box(&v);
  388. }
  389. });
  390. },
  391. );
  392. g.bench_function(
  393. concat!(
  394. "SmallVec<[",
  395. stringify!($type),
  396. "; ",
  397. stringify!($len),
  398. "]>::extend_from_slice"
  399. ),
  400. |b| {
  401. b.iter(|| {
  402. let data: &[$type] = black_box(&[0, 1, 2, 3, 4, 5, 6, 7]);
  403. let mut v: SmallVec<[$type; $len]> = black_box(SmallVec::default());
  404. for _ in 0..ITERS {
  405. v.clear();
  406. black_box(&v);
  407. v.extend_from_slice(data);
  408. black_box(&v);
  409. }
  410. });
  411. },
  412. );
  413. g.bench_function(
  414. concat!(
  415. "SmallVec<[",
  416. stringify!($type),
  417. "; ",
  418. stringify!($len),
  419. "]>::insert"
  420. ),
  421. |b| {
  422. b.iter(|| {
  423. let mut v: SmallVec<[$type; $len]> = SmallVec::default();
  424. for _ in 0..ITERS {
  425. v.clear();
  426. black_box(&v);
  427. for i in black_box(0..=($len as usize - 1) as _) {
  428. v.insert(i as usize, i);
  429. }
  430. black_box(&v);
  431. }
  432. });
  433. },
  434. );
  435. g.bench_function(
  436. concat!(
  437. "SmallVec<[",
  438. stringify!($type),
  439. "; ",
  440. stringify!($len),
  441. "]>::remove"
  442. ),
  443. |b| {
  444. b.iter(|| {
  445. let outer: SmallVec<[$type; $len]> =
  446. black_box(SmallVec::from_iter(0..=($len as usize - 1) as _));
  447. for _ in 0..ITERS {
  448. let mut v = outer.clone();
  449. for i in black_box((0..=($len as usize - 1) as _).rev()) {
  450. v.remove(i);
  451. }
  452. black_box(&v);
  453. }
  454. });
  455. },
  456. );
  457. }};
  458. }
  459. fn smallvec_benches(c: &mut Criterion) {
  460. smallvec_benches!(c, u8; 8);
  461. smallvec_benches!(c, u8; 16);
  462. smallvec_benches!(c, u8; 32);
  463. smallvec_benches!(c, u8; 64);
  464. smallvec_benches!(c, u8; 128);
  465. smallvec_benches!(c, u8; 256);
  466. smallvec_benches!(c, u64; 2);
  467. smallvec_benches!(c, u64; 4);
  468. smallvec_benches!(c, u64; 8);
  469. smallvec_benches!(c, u64; 16);
  470. smallvec_benches!(c, u64; 32);
  471. }
  472. criterion_group!(benches, tinyvec_benches, smallvec_benches);
  473. criterion_main!(benches);