Skip to content

Commit e930902

Browse files
committed
Fix: Avoid x86-only hashes on Arm
1 parent f2aae99 commit e930902

4 files changed

Lines changed: 58 additions & 60 deletions

File tree

Cargo.lock

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ bench_find = [
3030
"regex", # Byteset Search
3131
]
3232
bench_hash = [
33-
"ahash", # aHash
34-
"xxhash-rust", # xxHash
35-
"gxhash", # gxHash
36-
"blake3", # Blake3
37-
"crc32fast", # CRC32
38-
"murmur3", # MurmurHash3
39-
"cityhash", # CityHash
40-
"foldhash", # FoldHash
41-
"bit-set", # for collision counting
33+
"ahash", # aHash
34+
"xxhash-rust", # xxHash
35+
"gxhash", # gxHash (x86_64 only)
36+
"blake3", # Blake3
37+
"crc32fast", # CRC32
38+
"murmurhash32", # MurmurHash32
39+
"cityhash", # CityHash (x86_64 only)
40+
"foldhash", # FoldHash
41+
"bit-set", # for collision counting
4242
]
4343
bench_sequence = [
4444
"arrow", # Sorting
@@ -121,22 +121,14 @@ optional = true
121121
version = "1.3.2"
122122
optional = true
123123

124-
[dependencies.murmur3]
125-
version = "0.5"
126-
optional = true
127-
128-
[dependencies.cityhash]
129-
version = "0.1"
124+
[dependencies.murmurhash32]
125+
version = "0.3"
130126
optional = true
131127

132128
[dependencies.foldhash]
133129
version = "0.2"
134130
optional = true
135131

136-
[dependencies.gxhash]
137-
version = "3.4.1"
138-
optional = true
139-
140132
[dependencies.ahash]
141133
version = "0.8"
142134
optional = true
@@ -194,6 +186,10 @@ optional = true
194186
version = "0.8"
195187
optional = true
196188

189+
[target.'cfg(target_arch = "x86_64")'.dependencies]
190+
gxhash = { version = "3.4.1", optional = true }
191+
cityhash = { version = "0.1", optional = true }
192+
197193
[[bench]]
198194
name = "bench_find"
199195
path = "bench_find.rs"

README.md

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,28 +40,29 @@ Many of them have similar pitfalls:
4040
StringZilla addresses those issues and seems to provide competitive performance.
4141
On Intel Sapphire Rapids CPU, on `xlsum.csv` dataset, the following numbers can be expected for hashing individual whitespace-delimited words and newline-delimited lines:
4242

43-
| Library | Bits | Ports ¹ | Short Words | Long Lines |
44-
| --------------------- | :---: | :-----: | -------------: | --------------: |
45-
| Rust 🦀 | | | | |
46-
| `std::hash` | 64 || 0.43 GiB/s | 3.74 GiB/s |
47-
| `crc32fast::hash` | 32 || 0.49 GiB/s | 8.45 GiB/s |
48-
| `xxh3::xxh3_64` | 64 || 1.08 GiB/s | 9.48 GiB/s |
49-
| `aHash::hash_one` | 64 || 1.23 GiB/s | 8.61 GiB/s |
50-
| `foldhash::hash_one` | 64 || 1.02 GiB/s | 8.24 GiB/s |
51-
| `gxhash::gxhash64` | 64 || __2.68 GiB/s__ | 9.19 GiB/s |
52-
| `stringzilla::hash` | 64 || 1.84 GiB/s | __11.38 GiB/s__ |
53-
| | | | | |
54-
| Python 🐍 | | | | |
55-
| `hash` | 32/64 || 0.13 GiB/s | 4.27 GiB/s |
56-
| `xxhash.xxh3_64` | 64 || 0.04 GiB/s | 6.38 GiB/s |
57-
| `google_crc32c.value` | 32 || 0.04 GiB/s | 5.96 GiB/s |
58-
| `mmh3.hash32` | 32 || 0.05 GiB/s | 2.65 GiB/s |
59-
| `mmh3.hash64` | 64 || 0.03 GiB/s | 4.45 GiB/s |
60-
| `cityhash.CityHash64` | 64 || 0.06 GiB/s | 4.87 GiB/s |
61-
| `stringzilla.hash` | 64 || __0.14 GiB/s__ | __9.19 GiB/s__ |
43+
| Library | Bits | Ports ¹ | Arm ² | Short Words | Long Lines |
44+
| --------------------- | :---: | :-----: | :---: | -------------: | --------------: |
45+
| Rust 🦀 | | | | | |
46+
| `std::hash` | 64 || | 0.43 GiB/s | 3.74 GiB/s |
47+
| `crc32fast::hash` | 32 || | 0.49 GiB/s | 8.45 GiB/s |
48+
| `xxh3::xxh3_64` | 64 || | 1.08 GiB/s | 9.48 GiB/s |
49+
| `aHash::hash_one` | 64 || | 1.23 GiB/s | 8.61 GiB/s |
50+
| `foldhash::hash_one` | 64 || | 1.02 GiB/s | 8.24 GiB/s |
51+
| `gxhash::gxhash64` | 64 || | 2.68 GiB/s | 9.19 GiB/s |
52+
| `stringzilla::hash` | 64 || | __1.84 GiB/s__ | __11.38 GiB/s__ |
53+
| | | | | |
54+
| Python 🐍 | | | | |
55+
| `hash` | 32/64 || | 0.13 GiB/s | 4.27 GiB/s |
56+
| `xxhash.xxh3_64` | 64 || | 0.04 GiB/s | 6.38 GiB/s |
57+
| `google_crc32c.value` | 32 || | 0.04 GiB/s | 5.96 GiB/s |
58+
| `mmh3.hash32` | 32 || | 0.05 GiB/s | 2.65 GiB/s |
59+
| `mmh3.hash64` | 64 || | 0.03 GiB/s | 4.45 GiB/s |
60+
| `cityhash.CityHash64` | 64 || | 0.06 GiB/s | 4.87 GiB/s |
61+
| `stringzilla.hash` | 64 || | __0.14 GiB/s__ | __9.19 GiB/s__ |
6262

6363

6464
> ¹ Portability means availability in multiple other programming languages, like C, C++, Python, Java, Go, JavaScript, etc.
65+
> ² Most hash functions work on both x86 and Arm, as well as many other CPU architectures, but gxHash, and many MurMurHash and CityHash implementations don't.
6566
6667
In larger systems, however, we often need the ability to incrementally hash the data.
6768
This is especially important in distributed systems, where the data is too large to fit into memory at once.

bench_hash.rs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ The benchmarks compare the performance of different hash functions including:
1212
- StringZilla (`bytesum`, `hash`, and incremental `hash` function variants)
1313
- aHash (both incremental and single-entry variants)
1414
- xxHash (xxh3) through the third-party `xxhash-rust` crate
15-
- gxhash (gxhash64)
15+
- gxhash (gxhash64, x86_64 only)
1616
- FoldHash (a fast hash with good quality)
1717
- CRC32 (IEEE) via `crc32fast`
18+
- MurmurHash32 via `murmurhash32`
19+
- CityHash64 via `cityhash` (x86_64 only)
1820
- Blake3 (the only cryptographic hash in the comparison, for reference)
1921
2022
## Usage Examples
@@ -36,26 +38,27 @@ RUSTFLAGS="-C target-cpu=native" \
3638
cargo criterion --features bench_hash bench_hash --jobs $(nproc)
3739
```
3840
39-
For `gxhash`, ensure that your CPU supports the required AES and SSE2 instructions.
41+
Note: `gxhash` and `cityhash` are only compiled on x86_64 targets as they require x86-specific instructions.
4042
"#]
4143
use std::collections::HashSet;
4244
use std::env;
4345
use std::error::Error;
4446
use std::fs;
4547
use std::hash::{BuildHasher, Hasher};
4648
use std::hint::black_box;
47-
use std::io::Cursor;
4849

4950
use bit_set::BitSet;
5051
use criterion::{Criterion, Throughput};
5152

5253
use ahash::RandomState as AHashState;
5354
use blake3;
55+
#[cfg(target_arch = "x86_64")]
5456
use cityhash;
5557
use crc32fast;
5658
use foldhash;
59+
#[cfg(target_arch = "x86_64")]
5760
use gxhash;
58-
use murmur3;
61+
use murmurhash32;
5962
use stringzilla::sz;
6063
use xxhash_rust::xxh3::xxh3_64;
6164

@@ -236,7 +239,8 @@ fn bench_stateless(
236239
print_collision_rate(&unique_tokens, |t| xxh3_64(t));
237240
}
238241

239-
// Benchmark: gxhash
242+
// Benchmark: gxhash (x86_64 only)
243+
#[cfg(target_arch = "x86_64")]
240244
if should_run("stateless/gxhash::gxhash64") {
241245
group.bench_function("gxhash::gxhash64", |b| {
242246
b.iter(|| {
@@ -276,24 +280,21 @@ fn bench_stateless(
276280
print_collision_rate(&unique_tokens, |t| crc32fast::hash(t) as u64);
277281
}
278282

279-
// Benchmark: MurmurHash3 (x64_128) via `murmur3` (stateless)
280-
if should_run("stateless/murmur3::x64_128") {
281-
group.bench_function("murmur3::x64_128", |b| {
283+
// Benchmark: MurmurHash32 via `murmurhash32` (stateless)
284+
if should_run("stateless/murmurhash32") {
285+
group.bench_function("murmurhash32", |b| {
282286
b.iter(|| {
283287
for token in tokens {
284288
let t = black_box(*token);
285-
let mut cursor = Cursor::new(t);
286-
let _ = black_box(murmur3::murmur3_x64_128(&mut cursor, 0).unwrap());
289+
let _ = black_box(murmurhash32::murmurhash3(t) as u64);
287290
}
288291
})
289292
});
290-
print_collision_rate(&unique_tokens, |t| {
291-
let mut cursor = Cursor::new(t);
292-
murmur3::murmur3_x64_128(&mut cursor, 0).unwrap() as u64
293-
});
293+
print_collision_rate(&unique_tokens, |t| murmurhash32::murmurhash3(t) as u64);
294294
}
295295

296-
// Benchmark: CityHash64 via `cityhash` (stateless)
296+
// Benchmark: CityHash64 via `cityhash` (stateless, x86_64 only)
297+
#[cfg(target_arch = "x86_64")]
297298
if should_run("stateless/cityhash::city_hash_64") {
298299
group.bench_function("cityhash::city_hash_64", |b| {
299300
b.iter(|| {

0 commit comments

Comments
 (0)