1
use std::any::type_name;
2
use std::collections::{BTreeMap, HashMap};
3
use std::env;
4
use std::hash::Hash;
5
use std::ops::AddAssign;
6

            
7
use criterion::{
8
    black_box, criterion_group, criterion_main, BatchSize, Bencher, BenchmarkId, Criterion,
9
};
10
use fnv::FnvBuildHasher;
11
use kempt::Map;
12
use rand::distributions::Standard;
13
use rand::prelude::Distribution;
14
use rand::rngs::StdRng;
15
use rand::seq::SliceRandom;
16
use rand::{thread_rng, Rng, SeedableRng};
17

            
18
22
fn btree_lookup<Key>(bench: &mut Bencher, keys: &[Key])
19
22
where
20
22
    Key: Clone + Ord,
21
22
{
22
22
    let set = keys
23
22
        .iter()
24
4320
        .map(|key| (key.clone(), ()))
25
22
        .collect::<BTreeMap<_, _>>();
26
22
    let mut keys = keys.iter().cycle();
27
22

            
28
22
    bench.iter(|| {
29
22
        let key = black_box(keys.next().expect("cycled"));
30
22
        assert!(set.get(key).is_some());
31
22
    });
32
22
}
33

            
34
22
fn hash_lookup<Key>(bench: &mut Bencher, keys: &[Key])
35
22
where
36
22
    Key: Eq + Hash + Clone,
37
22
{
38
22
    let set = keys
39
22
        .iter()
40
4320
        .map(|key| (key.clone(), ()))
41
22
        .collect::<HashMap<_, _, FnvBuildHasher>>();
42
22
    let mut keys = keys.iter().cycle();
43
22

            
44
22
    bench.iter(|| {
45
22
        let key = black_box(keys.next().expect("cycled"));
46
22
        assert!(set.get(key).is_some());
47
22
    });
48
22
}
49

            
50
22
fn object_lookup<Key>(bench: &mut Bencher, keys: &[Key])
51
22
where
52
22
    Key: Clone + Ord,
53
22
{
54
22
    let set = keys
55
22
        .iter()
56
4320
        .map(|key| (key.clone(), ()))
57
22
        .collect::<Map<Key, ()>>();
58
22
    let mut keys = keys.iter().cycle();
59
22

            
60
22
    bench.iter(|| {
61
22
        let key = black_box(keys.next().expect("cycled"));
62
22
        assert!(set.get(key).is_some());
63
22
    });
64
22
}
65

            
66
3
fn lookup<Key>(c: &mut Criterion, keys: &[Key], sizes: &[usize])
67
3
where
68
3
    Key: Eq + Hash + Clone + Ord + Default + From<u8> + TryFrom<usize> + AddAssign,
69
3
{
70
3
    let mut group = c.benchmark_group(format!("lookup {}", type_name::<Key>()));
71
23
    for limit in sizes.iter().copied() {
72
23
        if Key::try_from(limit).is_err() {
73
1
            break;
74
22
        }
75
22
        group.bench_with_input(BenchmarkId::new("hash", limit), &keys[..limit], hash_lookup);
76
22
        group.bench_with_input(
77
22
            BenchmarkId::new("btree", limit),
78
22
            &keys[..limit],
79
22
            btree_lookup,
80
22
        );
81
22
        group.bench_with_input(
82
22
            BenchmarkId::new("object", limit),
83
22
            &keys[..limit],
84
22
            object_lookup,
85
22
        );
86
    }
87
3
}
88

            
89
42
fn btree_fill<Key>(bench: &mut Bencher, (keys, starting_size): &(&[Key], usize))
90
42
where
91
42
    Key: Clone + Ord,
92
42
{
93
42
    bench.iter_batched(
94
42
        || &keys[..*starting_size],
95
42
        |keys: &[Key]| {
96
42
            let mut map = BTreeMap::new();
97
8182
            for key in keys {
98
8140
                map.insert(key.clone(), ());
99
8140
            }
100
42
        },
101
42
        BatchSize::LargeInput,
102
42
    );
103
42
}
104

            
105
42
fn hash_fill<Key>(bench: &mut Bencher, (keys, starting_size): &(&[Key], usize))
106
42
where
107
42
    Key: Eq + Hash + Clone,
108
42
{
109
42
    bench.iter_batched(
110
42
        || &keys[..*starting_size],
111
42
        |keys: &[Key]| {
112
42
            let mut map =
113
42
                HashMap::with_capacity_and_hasher(*starting_size, FnvBuildHasher::default());
114
8182
            for key in keys {
115
8140
                map.insert(key.clone(), ());
116
8140
            }
117
42
        },
118
42
        BatchSize::LargeInput,
119
42
    );
120
42
}
121

            
122
42
fn object_fill<Key>(bench: &mut Bencher, (keys, starting_size): &(&[Key], usize))
123
42
where
124
42
    Key: Clone + Ord,
125
42
{
126
42
    bench.iter_batched(
127
42
        || &keys[..*starting_size],
128
42
        |keys: &[Key]| {
129
42
            let mut map = Map::with_capacity(*starting_size);
130
8182
            for key in keys {
131
8140
                map.insert(key.clone(), ());
132
8140
            }
133
42
        },
134
42
        BatchSize::LargeInput,
135
42
    );
136
42
}
137

            
138
6
fn fill<Key>(c: &mut Criterion, keys: &[Key], sizes: &[usize], name: &str)
139
6
where
140
6
    Key: Eq + Hash + Clone + Ord + TryFrom<usize>,
141
6
    Standard: Distribution<Key>,
142
6
{
143
6
    let mut group = c.benchmark_group(format!("{name} {}", type_name::<Key>()));
144
44
    for limit in sizes.iter().copied() {
145
44
        if Key::try_from(limit * 2).is_err() {
146
2
            break;
147
42
        }
148
42

            
149
42
        group.bench_with_input(BenchmarkId::new("hash", limit), &(keys, limit), hash_fill);
150
42
        group.bench_with_input(BenchmarkId::new("btree", limit), &(keys, limit), btree_fill);
151
42
        group.bench_with_input(
152
42
            BenchmarkId::new("object", limit),
153
42
            &(keys, limit),
154
42
            object_fill,
155
42
        );
156
    }
157
6
}
158

            
159
22
fn btree_remove<Key>(bench: &mut Bencher, keys: &[Key])
160
22
where
161
22
    Key: Clone + Ord,
162
22
{
163
22
    let set = keys
164
22
        .iter()
165
4320
        .map(|key| (key.clone(), ()))
166
22
        .collect::<BTreeMap<_, _>>();
167
22
    let mut keys = keys.iter().cycle();
168
22

            
169
22
    bench.iter_batched(
170
22
        || set.clone(),
171
22
        |mut set| {
172
22
            let key = black_box(keys.next().expect("cycled"));
173
22
            assert!(set.remove(key).is_some());
174
22
        },
175
22
        BatchSize::LargeInput,
176
22
    );
177
22
}
178

            
179
22
fn hash_remove<Key>(bench: &mut Bencher, keys: &[Key])
180
22
where
181
22
    Key: Eq + Hash + Clone,
182
22
{
183
22
    let set = keys
184
22
        .iter()
185
4320
        .map(|key| (key.clone(), ()))
186
22
        .collect::<HashMap<_, _, FnvBuildHasher>>();
187
22
    let mut keys = keys.iter().cycle();
188
22

            
189
22
    bench.iter_batched(
190
22
        || set.clone(),
191
22
        |mut set| {
192
22
            let key = black_box(keys.next().expect("cycled"));
193
22
            assert!(set.remove(key).is_some());
194
22
        },
195
22
        BatchSize::LargeInput,
196
22
    );
197
22
}
198

            
199
22
fn object_remove<Key>(bench: &mut Bencher, keys: &[Key])
200
22
where
201
22
    Key: Clone + Ord,
202
22
{
203
22
    let set = keys
204
22
        .iter()
205
4320
        .map(|key| (key.clone(), ()))
206
22
        .collect::<Map<Key, ()>>();
207
22
    let mut keys = keys.iter().cycle();
208
22

            
209
22
    bench.iter_batched(
210
22
        || set.clone(),
211
22
        |mut set| {
212
22
            let key = black_box(keys.next().expect("cycled"));
213
22
            assert!(set.remove(key).is_some());
214
22
        },
215
22
        BatchSize::LargeInput,
216
22
    );
217
22
}
218

            
219
3
fn remove<Key>(c: &mut Criterion, keys: &[Key], sizes: &[usize])
220
3
where
221
3
    Key: Eq + Hash + Clone + Ord + Default + From<u8> + TryFrom<usize> + AddAssign,
222
3
{
223
3
    let mut group = c.benchmark_group(format!("remove {}", type_name::<Key>()));
224
23
    for limit in sizes.iter().copied() {
225
23
        if Key::try_from(limit).is_err() {
226
1
            break;
227
22
        }
228
22
        group.bench_with_input(BenchmarkId::new("hash", limit), &keys[..limit], hash_remove);
229
22
        group.bench_with_input(
230
22
            BenchmarkId::new("btree", limit),
231
22
            &keys[..limit],
232
22
            btree_remove,
233
22
        );
234
22
        group.bench_with_input(
235
22
            BenchmarkId::new("object", limit),
236
22
            &keys[..limit],
237
22
            object_remove,
238
22
        );
239
    }
240
3
}
241

            
242
6
fn generate_keys<Key>(max: Key, shuffle: bool, random_seed: &[u8; 32]) -> Vec<Key>
243
6
where
244
6
    Key: Eq + Hash + Copy + Ord + Default + From<u8> + AddAssign,
245
6
{
246
6
    let mut keys = Vec::new();
247
6
    let mut key = Key::default();
248
40516
    while keys.len() < 10000 && key < max {
249
40510
        keys.push(key);
250
40510
        key += Key::from(1);
251
40510
    }
252
6
    if shuffle {
253
3
        let mut rng = StdRng::from_seed(*random_seed);
254
3
        keys.shuffle(&mut rng);
255
3
    }
256
6
    keys
257
6
}
258

            
259
3
fn suite_for_key<Key>(c: &mut Criterion, max: Key, sizes: &[usize], random_seed: &[u8; 32])
260
3
where
261
3
    Key: Eq + Hash + Copy + Ord + Default + From<u8> + TryFrom<usize> + AddAssign,
262
3
    Standard: Distribution<Key>,
263
3
{
264
3
    let keys = generate_keys::<Key>(max, true, random_seed);
265
3
    fill::<Key>(c, &keys, sizes, "fill-rdm");
266
3
    lookup::<Key>(c, &keys, sizes);
267
3
    remove::<Key>(c, &keys, sizes);
268
3
    let keys = generate_keys::<Key>(max, false, random_seed);
269
3
    fill::<Key>(c, &keys, sizes, "fill-seq");
270
3
}
271

            
272
1
fn criterion_benchmark(c: &mut Criterion) {
273
2
    let random_seed = env::args().find(|arg| arg.starts_with("-s")).map_or_else(
274
1
        || thread_rng().gen(),
275
1
        |seed| {
276
            let (_, seed) = seed.split_at(2);
277
            let (upper, lower) = if seed.len() > 32 {
278
                let (upper, lower) = seed.split_at(seed.len() - 32);
279
                (
280
                    u128::from_str_radix(upper, 16).expect("invalid hexadecimal seed"),
281
                    u128::from_str_radix(lower, 16).expect("invalid hexadecimal seed"),
282
                )
283
            } else {
284
                (
285
                    0,
286
                    u128::from_str_radix(seed, 16).expect("invalid hexadecimal seed"),
287
                )
288
            };
289
            let mut seed = [0; 32];
290
            seed[..16].copy_from_slice(&upper.to_be_bytes());
291
            seed[16..].copy_from_slice(&lower.to_be_bytes());
292
            seed
293
1
        },
294
1
    );
295
1
    print!("Using random seed -s");
296
33
    for b in random_seed {
297
32
        print!("{b:02x}");
298
32
    }
299
1
    println!();
300
1

            
301
1
    let sizes = [5, 10, 25, 50, 100, 250, 500, 1000];
302
1
    suite_for_key::<u8>(c, u8::MAX, &sizes, &random_seed);
303
1
    suite_for_key::<usize>(c, usize::MAX, &sizes, &random_seed);
304
1
    suite_for_key::<u128>(c, u128::MAX, &sizes, &random_seed);
305
1
}
306

            
307
criterion_group!(benches, criterion_benchmark);
308
criterion_main!(benches);