Skip to content

Commit 840098f

Browse files
committed
Add SmolStr vs String benchmarks
1 parent 5ffc900 commit 840098f

File tree

3 files changed

+172
-0
lines changed

3 files changed

+172
-0
lines changed

Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,16 @@ arbitrary = { version = "1.3", optional = true }
2020
proptest = "1.5"
2121
serde_json = "1.0"
2222
serde = { version = "1.0", features = ["derive"] }
23+
criterion = "0.7"
24+
rand = "0.9.2"
2325

2426
[features]
2527
default = ["std"]
2628
std = ["serde?/std", "borsh?/std"]
29+
30+
[[bench]]
31+
name = "bench"
32+
harness = false
33+
34+
[profile.bench]
35+
lto = "fat"

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ languages. Strings consisting of a series of newlines, followed by a series of
2222
whitespace are a typical pattern in computer programs because of indentation.
2323
Note that a specialized interner might be a better solution for some use cases.
2424

25+
## Benchmarks
26+
Run criterion benches with
27+
```sh
28+
cargo bench --bench \* -- --quick
29+
```
30+
2531
## MSRV Policy
2632

2733
Minimal Supported Rust Version: latest stable.

benches/bench.rs

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
//! SmolStr vs String benchmarks.
2+
use criterion::{Criterion, criterion_group, criterion_main};
3+
use rand::distr::{Alphanumeric, SampleString};
4+
use smol_str::{SmolStr, StrExt, ToSmolStr, format_smolstr};
5+
use std::hint::black_box;
6+
7+
/// 12: small (inline)
8+
/// 50: medium (heap)
9+
/// 1000: large (heap)
10+
const TEST_LENS: [usize; 3] = [12, 50, 1000];
11+
12+
fn format_bench(c: &mut Criterion) {
13+
for len in TEST_LENS {
14+
let n = rand::random_range(10000..99999);
15+
let str_len = len.checked_sub(n.to_smolstr().len()).unwrap();
16+
let str = Alphanumeric.sample_string(&mut rand::rng(), str_len);
17+
18+
c.bench_function(&format!("SmolStr format_smolstr! len={len}"), |b| {
19+
let mut v = <_>::default();
20+
b.iter(|| v = format_smolstr!("{str}-{n}"));
21+
assert_eq!(v, format!("{str}-{n}"));
22+
});
23+
c.bench_function(&format!("std format! len={len}"), |b| {
24+
let mut v = <_>::default();
25+
b.iter(|| v = format!("{str}-{n}"));
26+
assert_eq!(v, format!("{str}-{n}"));
27+
});
28+
}
29+
}
30+
31+
fn from_str_bench(c: &mut Criterion) {
32+
for len in TEST_LENS {
33+
let str = Alphanumeric.sample_string(&mut rand::rng(), len);
34+
35+
c.bench_function(&format!("SmolStr::from len={len}"), |b| {
36+
let mut v = <_>::default();
37+
b.iter(|| v = SmolStr::from(black_box(&str)));
38+
assert_eq!(v, str);
39+
});
40+
c.bench_function(&format!("std String::from len={len}"), |b| {
41+
let mut v = <_>::default();
42+
b.iter(|| v = String::from(black_box(&str)));
43+
assert_eq!(v, str);
44+
});
45+
}
46+
}
47+
48+
fn clone_bench(c: &mut Criterion) {
49+
for len in TEST_LENS {
50+
let str = Alphanumeric.sample_string(&mut rand::rng(), len);
51+
let smolstr = SmolStr::new(&str);
52+
53+
c.bench_function(&format!("SmolStr::clone len={len}"), |b| {
54+
let mut v = <_>::default();
55+
b.iter(|| v = smolstr.clone());
56+
assert_eq!(v, str);
57+
});
58+
c.bench_function(&format!("std String::clone len={len}"), |b| {
59+
let mut v = <_>::default();
60+
b.iter(|| v = str.clone());
61+
assert_eq!(v, str);
62+
});
63+
}
64+
}
65+
66+
fn eq_bench(c: &mut Criterion) {
67+
for len in TEST_LENS {
68+
let str = Alphanumeric.sample_string(&mut rand::rng(), len);
69+
let smolstr = SmolStr::new(&str);
70+
71+
c.bench_function(&format!("SmolStr::eq len={len}"), |b| {
72+
let mut v = false;
73+
b.iter(|| v = smolstr == black_box(&str));
74+
assert!(v);
75+
});
76+
c.bench_function(&format!("std String::eq len={len}"), |b| {
77+
let mut v = false;
78+
b.iter(|| v = &str == black_box(&str));
79+
assert!(v);
80+
});
81+
}
82+
}
83+
84+
fn to_lowercase_bench(c: &mut Criterion) {
85+
const END_CHAR: char = 'İ';
86+
87+
for len in TEST_LENS {
88+
// mostly ascii seq with some non-ascii at the end
89+
let mut str = Alphanumeric.sample_string(&mut rand::rng(), len - END_CHAR.len_utf8());
90+
str.push(END_CHAR);
91+
let str = str.as_str();
92+
93+
c.bench_function(&format!("SmolStr to_lowercase_smolstr len={len}"), |b| {
94+
let mut v = <_>::default();
95+
b.iter(|| v = str.to_lowercase_smolstr());
96+
assert_eq!(v, str.to_lowercase());
97+
});
98+
c.bench_function(&format!("std to_lowercase len={len}"), |b| {
99+
let mut v = <_>::default();
100+
b.iter(|| v = str.to_lowercase());
101+
assert_eq!(v, str.to_lowercase());
102+
});
103+
}
104+
}
105+
106+
fn to_ascii_lowercase_bench(c: &mut Criterion) {
107+
for len in TEST_LENS {
108+
let str = Alphanumeric.sample_string(&mut rand::rng(), len);
109+
let str = str.as_str();
110+
111+
c.bench_function(
112+
&format!("SmolStr to_ascii_lowercase_smolstr len={len}"),
113+
|b| {
114+
let mut v = <_>::default();
115+
b.iter(|| v = str.to_ascii_lowercase_smolstr());
116+
assert_eq!(v, str.to_ascii_lowercase());
117+
},
118+
);
119+
c.bench_function(&format!("std to_ascii_lowercase len={len}"), |b| {
120+
let mut v = <_>::default();
121+
b.iter(|| v = str.to_ascii_lowercase());
122+
assert_eq!(v, str.to_ascii_lowercase());
123+
});
124+
}
125+
}
126+
127+
fn replace_bench(c: &mut Criterion) {
128+
for len in TEST_LENS {
129+
let s_dash_s = Alphanumeric.sample_string(&mut rand::rng(), len / 2)
130+
+ "-"
131+
+ &Alphanumeric.sample_string(&mut rand::rng(), len - 1 - len / 2);
132+
let str = s_dash_s.as_str();
133+
134+
c.bench_function(&format!("SmolStr replace_smolstr len={len}"), |b| {
135+
let mut v = <_>::default();
136+
b.iter(|| v = str.replace_smolstr("-", "_"));
137+
assert_eq!(v, str.replace("-", "_"));
138+
});
139+
c.bench_function(&format!("std replace len={len}"), |b| {
140+
let mut v = <_>::default();
141+
b.iter(|| v = str.replace("-", "_"));
142+
assert_eq!(v, str.replace("-", "_"));
143+
});
144+
}
145+
}
146+
147+
criterion_group!(
148+
benches,
149+
format_bench,
150+
from_str_bench,
151+
clone_bench,
152+
eq_bench,
153+
to_lowercase_bench,
154+
to_ascii_lowercase_bench,
155+
replace_bench,
156+
);
157+
criterion_main!(benches);

0 commit comments

Comments
 (0)