|
1 | | -use criterion::{black_box, criterion_group, criterion_main, Criterion}; |
| 1 | +use criterion::{ |
| 2 | + black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, |
| 3 | +}; |
2 | 4 | use std::collections::hash_map::DefaultHasher; |
3 | 5 | use std::hash::{Hash, Hasher}; |
| 6 | +use std::str::FromStr; |
4 | 7 |
|
| 8 | +use bench::*; |
5 | 9 | use cold_string::ColdString; |
6 | 10 |
|
7 | 11 | const SHORT: &str = "qwerty"; |
8 | 12 | const LONG: &str = "this_is_a_longer_string_that_will_allocate"; |
9 | 13 |
|
10 | | -fn bench_construction(c: &mut Criterion) { |
11 | | - let mut group = c.benchmark_group("construction"); |
12 | | - |
13 | | - group.bench_function("ColdString short", |b| { |
14 | | - b.iter(|| black_box(ColdString::from(black_box(SHORT)))) |
15 | | - }); |
16 | | - |
17 | | - group.bench_function("String short", |b| { |
18 | | - b.iter(|| black_box(String::from(black_box(SHORT)))) |
19 | | - }); |
20 | | - |
21 | | - group.bench_function("ColdString long", |b| { |
22 | | - b.iter(|| black_box(ColdString::from(black_box(LONG)))) |
23 | | - }); |
24 | | - |
25 | | - group.bench_function("String long", |b| { |
26 | | - b.iter(|| black_box(String::from(black_box(LONG)))) |
| 14 | +const LENGTHS: &[usize] = &[4, 8, 16, 32, 64]; |
| 15 | + |
| 16 | +fn bench_construction_inner<T: FromStr>( |
| 17 | + g: &mut BenchmarkGroup<'_, WallTime>, |
| 18 | + name: &'static str, |
| 19 | + min: usize, |
| 20 | + max: usize, |
| 21 | + strings: &[String], |
| 22 | +) { |
| 23 | + let label = format!("{}-len={}-{}", name, min, max); |
| 24 | + g.bench_function(&label, |b| { |
| 25 | + b.iter(|| { |
| 26 | + for x in strings.iter() { |
| 27 | + let _ = black_box(T::from_str(black_box(x.as_str()))); |
| 28 | + } |
| 29 | + }) |
27 | 30 | }); |
| 31 | +} |
28 | 32 |
|
| 33 | +#[rustfmt::skip] |
| 34 | +fn bench_construction(c: &mut Criterion) { |
| 35 | + let mut group = c.benchmark_group("construction"); |
| 36 | + for len in LENGTHS { |
| 37 | + for min in [0, *len] { |
| 38 | + let mut strings = Vec::with_capacity(1000); |
| 39 | + for _ in 0..1000 { |
| 40 | + strings.push(random_string(min, *len)); |
| 41 | + } |
| 42 | + bench_construction_inner::<String>(&mut group, "std", min, *len, &strings); |
| 43 | + bench_construction_inner::<smol_str::SmolStr>(&mut group, "smol_str", min, *len, &strings); |
| 44 | + bench_construction_inner::<compact_str::CompactString>(&mut group, "compact_str", min, *len, &strings); |
| 45 | + bench_construction_inner::<smartstring::alias::String>(&mut group, "smartstring", min, *len, &strings); |
| 46 | + bench_construction_inner::<smallstr::SmallString<[u8; 8]>>(&mut group, "smallstr", min, *len, &strings); |
| 47 | + bench_construction_inner::<compact_string::CompactString>(&mut group, "compact_string", min, *len, &strings); |
| 48 | + bench_construction_inner::<cold_string::ColdString>(&mut group, "cold-string", min, *len, &strings); |
| 49 | + } |
| 50 | + } |
29 | 51 | group.finish(); |
30 | 52 | } |
31 | 53 |
|
32 | 54 | fn bench_len(c: &mut Criterion) { |
33 | 55 | let cold = ColdString::from(LONG); |
34 | 56 | let string = String::from(LONG); |
35 | | - |
36 | 57 | let mut group = c.benchmark_group("len"); |
37 | | - |
38 | 58 | group.bench_function("ColdString len", |b| b.iter(|| black_box(cold.len()))); |
39 | | - |
40 | 59 | group.bench_function("String len", |b| b.iter(|| black_box(string.len()))); |
41 | | - |
42 | 60 | group.finish(); |
43 | 61 | } |
44 | 62 |
|
45 | | -fn bench_as_str(c: &mut Criterion) { |
46 | | - let cold = ColdString::from(LONG); |
47 | | - let string = String::from(LONG); |
| 63 | +fn bench_as_str_inner<T: FromStr + AsRef<str>>( |
| 64 | + g: &mut BenchmarkGroup<'_, WallTime>, |
| 65 | + name: &'static str, |
| 66 | + min: usize, |
| 67 | + max: usize, |
| 68 | + strings: &[String], |
| 69 | + indices: &[usize], // Pass pre-shuffled indices |
| 70 | +) { |
| 71 | + // Pre-convert to the target type |
| 72 | + let strings: Vec<_> = strings.iter() |
| 73 | + .map(|s| T::from_str(s).map_err(|_| ()).unwrap()) |
| 74 | + .collect(); |
| 75 | + |
| 76 | + let strings = black_box(strings); |
| 77 | + let label = format!("{}-len={}-{}", name, min, max); |
| 78 | + |
| 79 | + g.bench_function(&label, |b| { |
| 80 | + b.iter(|| { |
| 81 | + let mut sum: u8 = 0; |
| 82 | + // Iterate using the shuffled indices to force cache misses |
| 83 | + for &i in indices.iter() { |
| 84 | + let s = &strings[i]; |
| 85 | + let x: &str = black_box(s).as_ref(); |
| 86 | + // Accessing the data is crucial to force the dereference |
| 87 | + sum ^= x.as_bytes().first().unwrap_or(&0); |
| 88 | + } |
| 89 | + black_box(sum) |
| 90 | + }) |
| 91 | + }); |
| 92 | +} |
48 | 93 |
|
| 94 | +#[rustfmt::skip] |
| 95 | +fn bench_as_str(c: &mut Criterion) { |
49 | 96 | let mut group = c.benchmark_group("as_str"); |
50 | | - |
51 | | - group.bench_function("ColdString as_str", |b| b.iter(|| black_box(cold.as_str()))); |
52 | | - |
53 | | - group.bench_function("String as_str", |b| b.iter(|| black_box(string.as_str()))); |
54 | | - |
| 97 | + let count = 1_000_000; |
| 98 | + |
| 99 | + // Pre-calculate random indices once to keep the comparison fair across crates |
| 100 | + let mut indices: Vec<usize> = (0..count).collect(); |
| 101 | + fastrand::shuffle(&mut indices); |
| 102 | + // Limit to a subset if 1M iterations inside b.iter is too slow |
| 103 | + let indices_subset = &indices[..1000]; |
| 104 | + |
| 105 | + for len in LENGTHS { |
| 106 | + for min in [0, *len] { |
| 107 | + let mut strings = Vec::with_capacity(count); |
| 108 | + for _ in 0..count { |
| 109 | + strings.push(random_string(min, *len)); |
| 110 | + } |
| 111 | + |
| 112 | + bench_as_str_inner::<String>(&mut group, "std", min, *len, &strings, indices_subset); |
| 113 | + bench_as_str_inner::<smol_str::SmolStr>(&mut group, "smol_str", min, *len, &strings, indices_subset); |
| 114 | + bench_as_str_inner::<compact_str::CompactString>(&mut group, "compact_str", min, *len, &strings, indices_subset); |
| 115 | + bench_as_str_inner::<smartstring::alias::String>(&mut group, "smartstring", min, *len, &strings, indices_subset); |
| 116 | + bench_as_str_inner::<smallstr::SmallString<[u8; 8]>>(&mut group, "smallstr", min, *len, &strings, indices_subset); |
| 117 | + bench_as_str_inner::<compact_string::CompactString>(&mut group, "compact_string", min, *len, &strings, indices_subset); |
| 118 | + bench_as_str_inner::<cold_string::ColdString>(&mut group, "cold-string", min, *len, &strings, indices_subset); |
| 119 | + } |
| 120 | + } |
55 | 121 | group.finish(); |
56 | 122 | } |
57 | 123 |
|
|
0 commit comments