Skip to content

Commit 5ca1485

Browse files
committed
bench: cover random inputs and dispatch crossovers
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e7b02886-a2c5-43cd-bb97-bfa037d3b625
1 parent 4233dc2 commit 5ca1485

2 files changed

Lines changed: 156 additions & 1 deletion

File tree

benches/hamming_bench.rs

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@ use hexhamming::hex_hamming_distance_pack;
1111
const HEX_SIZES: [usize; 5] = [16, 32, 64, 128, 254];
1212
const BYTE_SIZES: [usize; 5] = [8, 16, 32, 64, 127];
1313

14+
fn pseudo_random_bytes(len: usize, seed: u64) -> Vec<u8> {
15+
let mut state = seed;
16+
(0..len)
17+
.map(|_| {
18+
state = state.wrapping_add(0x9E3779B97F4A7C15);
19+
let mut value = state;
20+
value = (value ^ (value >> 30)).wrapping_mul(0xBF58476D1CE4E5B9);
21+
value = (value ^ (value >> 27)).wrapping_mul(0x94D049BB133111EB);
22+
((value ^ (value >> 31)) >> 56) as u8
23+
})
24+
.collect()
25+
}
26+
1427
/// Benchmark hex hamming distance across all available algorithms
1528
fn bench_hex_by_algo(c: &mut Criterion) {
1629
let algos: &[&str] = if cfg!(target_arch = "x86_64") {
@@ -192,6 +205,88 @@ fn bench_array_api(c: &mut Criterion) {
192205
group.finish();
193206
}
194207

208+
fn bench_array_random_and_boundaries(c: &mut Criterion) {
209+
let small_16 = pseudo_random_bytes(16, 1);
210+
let big_512x16 = pseudo_random_bytes(512 * 16, 2);
211+
let mut group = c.benchmark_group("array_api/512x16_random_no_match");
212+
group.bench_function("first", |bencher| {
213+
bencher.iter(|| {
214+
bytes_array_first_within_dist(
215+
black_box(&big_512x16),
216+
black_box(&small_16),
217+
black_box(0),
218+
)
219+
})
220+
});
221+
group.bench_function("best", |bencher| {
222+
bencher.iter(|| {
223+
bytes_array_best_within_dist(black_box(&big_512x16), black_box(&small_16), black_box(0))
224+
})
225+
});
226+
group.bench_function("all", |bencher| {
227+
bencher.iter(|| {
228+
bytes_array_all_within_dist(black_box(&big_512x16), black_box(&small_16), black_box(0))
229+
})
230+
});
231+
group.finish();
232+
233+
let small_64 = pseudo_random_bytes(64, 3);
234+
let big_16384x64 = pseudo_random_bytes(16_384 * 64, 4);
235+
let mut group = c.benchmark_group("array_api/16384x64_random_no_match");
236+
group.sample_size(30);
237+
group.bench_function("first", |bencher| {
238+
bencher.iter(|| {
239+
bytes_array_first_within_dist(
240+
black_box(&big_16384x64),
241+
black_box(&small_64),
242+
black_box(0),
243+
)
244+
})
245+
});
246+
group.bench_function("best", |bencher| {
247+
bencher.iter(|| {
248+
bytes_array_best_within_dist(
249+
black_box(&big_16384x64),
250+
black_box(&small_64),
251+
black_box(0),
252+
)
253+
})
254+
});
255+
group.bench_function("all", |bencher| {
256+
bencher.iter(|| {
257+
bytes_array_all_within_dist(
258+
black_box(&big_16384x64),
259+
black_box(&small_64),
260+
black_box(0),
261+
)
262+
})
263+
});
264+
group.finish();
265+
266+
let mut group = c.benchmark_group("array_api/parallel_boundary_64");
267+
group.sample_size(30);
268+
for num_elements in [4095usize, 4096, 65_536, 81_920, 131_072] {
269+
let big = pseudo_random_bytes(num_elements * 64, 10 + num_elements as u64);
270+
group.bench_function(format!("{num_elements} elements/all"), |bencher| {
271+
bencher.iter(|| {
272+
bytes_array_all_within_dist(black_box(&big), black_box(&small_64), black_box(0))
273+
})
274+
});
275+
if num_elements >= 65_536 {
276+
group.bench_function(format!("{num_elements} elements/best"), |bencher| {
277+
bencher.iter(|| {
278+
bytes_array_best_within_dist(
279+
black_box(&big),
280+
black_box(&small_64),
281+
black_box(0),
282+
)
283+
})
284+
});
285+
}
286+
}
287+
group.finish();
288+
}
289+
195290
#[cfg(target_arch = "aarch64")]
196291
fn bench_hex_string_pack(c: &mut Criterion) {
197292
// AArch64-only group for the packed NEON hex-string path.
@@ -213,6 +308,7 @@ criterion_group!(
213308
bench_bytes_by_algo,
214309
bench_bytes_within_dist,
215310
bench_array_api,
311+
bench_array_random_and_boundaries,
216312
bench_hex_string_pack
217313
);
218314
#[cfg(not(target_arch = "aarch64"))]
@@ -221,6 +317,7 @@ criterion_group!(
221317
bench_hex_by_algo,
222318
bench_bytes_by_algo,
223319
bench_bytes_within_dist,
224-
bench_array_api
320+
bench_array_api,
321+
bench_array_random_and_boundaries
225322
);
226323
criterion_main!(benches);

test/test_performance_matrix.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import random
2+
3+
import pytest
4+
5+
from hexhamming import hamming_distance_bytes, hamming_distance_string
6+
7+
8+
def random_bytes(size):
9+
rng = random.Random(0xC0FFEE + size)
10+
return bytes(rng.randrange(256) for _ in range(size))
11+
12+
13+
def stdlib_bytes_distance(a, b):
14+
return (int.from_bytes(a, "big") ^ int.from_bytes(b, "big")).bit_count()
15+
16+
17+
def stdlib_hex_distance(a, b):
18+
return (int(a, 16) ^ int(b, 16)).bit_count()
19+
20+
21+
@pytest.mark.benchmark(group="random_bytes")
22+
@pytest.mark.parametrize("size", (16, 64, 1024))
23+
def test_hamming_distance_bytes_random_bench(benchmark, size):
24+
a = random_bytes(size)
25+
b = random_bytes(size + 1)[:size]
26+
benchmark(hamming_distance_bytes, a, b)
27+
28+
29+
@pytest.mark.benchmark(group="random_bytes_stdlib")
30+
@pytest.mark.parametrize("size", (16, 64, 1024))
31+
def test_stdlib_bytes_distance_random_bench(benchmark, size):
32+
a = random_bytes(size)
33+
b = random_bytes(size + 1)[:size]
34+
benchmark(stdlib_bytes_distance, a, b)
35+
36+
37+
@pytest.mark.benchmark(group="random_hex")
38+
@pytest.mark.parametrize("size", (16, 64, 1024))
39+
def test_hamming_distance_string_random_bench(benchmark, size):
40+
a = random_bytes(size // 2).hex()
41+
b = random_bytes(size // 2 + 1).hex()[:size]
42+
benchmark(hamming_distance_string, a, b)
43+
44+
45+
@pytest.mark.benchmark(group="random_hex_stdlib")
46+
@pytest.mark.parametrize("size", (16, 64, 1024))
47+
def test_stdlib_hex_distance_random_bench(benchmark, size):
48+
a = random_bytes(size // 2).hex()
49+
b = random_bytes(size // 2 + 1).hex()[:size]
50+
benchmark(stdlib_hex_distance, a, b)
51+
52+
53+
@pytest.mark.benchmark(group="gil_boundary")
54+
@pytest.mark.parametrize("size", (16376, 16384, 16392))
55+
def test_hamming_distance_bytes_gil_boundary_bench(benchmark, size):
56+
a = random_bytes(size)
57+
b = random_bytes(size + 1)[:size]
58+
benchmark(hamming_distance_bytes, a, b)

0 commit comments

Comments
 (0)