rapidhash

repository·master·Indexed 42 days ago

https://github.com/hoxxep/rapidhash

A high-performance, platform-independent hashing algorithm implemented in Rust, designed as the successor to wyhash. Version 4.5.1 provides optimized implementations for AMD64 and AArch64, offering both in-memory hashing via std::hash traits (rapidhash::fast and rapidhash::quality) and stable, cross-platform portable hashing via the rapidhash::v1, v2, and v3 modules. It includes a CLI tool for hashing files and stdin, and provides C bindings via the rapidhash-c package.

Tokens
12.6K
Snippets
46
Records
61
Agent score
54%

What's inside rapidhash

  1. Performance benchmarks for rapidhash

    master

    rapidhash is a high-performance, non-cryptographic hash function. In benchmarks, it is one of the fastest general-purpose options, often ranking second only to gxhash (which requires AES instructions and is not portable).

    Benchmarking Methodology

    • Raw Throughput: Measured via charts to evaluate performance across various input sizes.
    • Real-world Workloads: Measured using the foldhash benchmark suite, which includes metrics for raw throughput, hashmap lookup (miss/hit), and hashmap insertion performance across common types.

    Key Comparisons

    • vs. foldhash: rapidhash uses the same integer buffer construction as foldhash but is faster for strings due to its specific algorithm. It also provides portable and streaming hash flavors.
    • vs. gxhash: gxhash is faster but requires AES instructions and is not portable. rapidhash is preferred for applications requiring portability or when hashing tuples and structs rather than just strings.
    • vs. SipHash: The default Rust hasher (SipHash) is included in benchmarks for comparison, though rapidhash generally provides higher throughput.

    Best Practices for Benchmarking

    To ensure consistent inlining and accurate performance results when benchmarking your own use cases, it is recommended to use the following profile settings in your Cargo.toml:

    [profile.release]
    opt-level = 3
    lto = "fat"
    codegen-units = 1
  2. Choose the right module for in-memory hashing

    master

    For use within a single execution (e.g., for HashMap or HashSet), use the Rust hashing traits. Note that in-memory hash outputs are not guaranteed to be stable across platforms, compiler versions, or crate versions.

    Select a module based on your priority:

    • rapidhash::fast: Optimized for speed. Best for standard data structures like HashMap and HashSet.
    • rapidhash::quality: Optimized for statistical hash quality. Best for algorithms like HyperLogLog or MinHash.
    • rapidhash::inner: Used to configure advanced parameters for specific use cases.
    // Example: Using the fast module for a HashMap
    use std::collections::HashMap;
    use rapidhash::fast::RandomState;
    
    let mut map: HashMap<_, _, RandomState> = HashMap::with_capacity_and_hasher(10, RandomState::new());
  3. Understand rapidhash DoS resistance

    master

    Rapidhash is a keyed hash function designed to be minimally resistant to Denial of Service (DoS) attacks. It aims to be immune to length extension and re-ordering attacks by randomizing the secrets array (a deviation from the original C++ implementation).

    To achieve DoS resistance, you must instantiate the hasher through RandomState or RapidSecrets using a randomized secret seed.

    Note: Rapidhash is not a cryptographic hash function. If the seed or secrets are known, collisions can be constructed, and an interactive attacker might attempt to learn the seed through observation.

  4. Achieve portable hashing with rapidhash::v* modules

    master

    If you need hash outputs that are stable across different platforms, compiler versions, or crate versions, use the rapidhash::v1, rapidhash::v2, or rapidhash::v3 modules. These modules provide C++ compatibility and guarantee stable outputs.

    Recommendation: Use rapidhash::v3. It is the fastest, most recent version, and is the only version that supports streaming.

    // Use v3 for the best performance and streaming support
    // Outputs are guaranteed to be stable across versions
    let hash = rapidhash::v3::hash(data);
  5. Generate benchmark reports and charts

    master

    The project provides Python scripts to generate visual charts and tables from benchmark data. These scripts use uv to manage dependencies automatically.

    # generate the --bench bench charts
    uv run generate_charts.py
    
    # generate the --bench realworld table
    uv run generate_table.py
  6. Fuzz rapidhash

    master

    Fuzzing can be performed on the raw rapidhash method or the RapidHasher struct (testing std::hash::Hasher trait implementations). This requires cargo-fuzz or cargo-afl to be installed.

    # fuzz the raw rapidhash method (requires cargo-fuzz)
    cargo +nightly fuzz run rapidhash
    
    # fuzz the RapidHasher struct (requires cargo-fuzz)
    cargo +nightly fuzz run rapidhasher
    
    # use AFL fuzzing (requires cargo-afl)
    cargo afl fuzz -i in -o out target/debug/afl_rapidhash
  7. Install dependencies for rapidhash

    master

    To build, test, and benchmark rapidhash (including the C implementations used for correctness verification), you need a C++ compiler, Rust, uv, and cargo-criterion for benchmarking.

    Note: The benchmarks require the rapidhash-c code to be built, which necessitates a C++ compiler.

    # install git and a C++ compiler
    sudo yum install -y git gcc gcc-c++
    
    # install rust
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    . "$HOME/.cargo/env"
    
    # install uv
    curl -LsSf https://astral.sh/uv/install.sh | sh
    
    # install cargo-criterion for benchmarks
    cargo install cargo-criterion
    
    # clone rapidhash
    git clone https://github.com/hoxxep/rapidhash.git
    cd rapidhash
  8. Run rapidhash benchmarks

    master

    Benchmarks are located in the rapidhash-bench crate. It is recommended to set RUSTFLAGS="-C target-cpu=native" to ensure the benchmarks reflect performance on your specific hardware. Most benchmark commands require the bench feature to be enabled.

    # Navigate to the bench crate
    cd rapidhash-bench
    
    # Run all benchmarks
    RUSTFLAGS="-C target-cpu=native" cargo criterion --plotting-backend disabled --bench bench --features bench
    
    # Run the realworld benchmark
    RUSTFLAGS="-C target-cpu=native" cargo criterion --plotting-backend disabled --bench realworld --features bench
    
    # Run quality tests across various hash functions
    RUSTFLAGS="-C target-cpu=native" cargo bench --bench quality --features bench
    
    # Benchmark WASM targets
    RUSTFLAGS="-C target-cpu=native" cargo criterion --plotting-backend disabled --bench wasm --features bench
  9. Run rapidhash tests

    master

    You can run tests using standard cargo commands. Use the --all-features flag to test all capabilities, or specific flags to test no_std environments without std or unsafe features.

    # Run tests
    cargo +nightly test --all-features
    
    # Run tests, for no_std with std = off and unsafe = off
    cargo test --no-default-features --lib
    
    # Check MSRV (Minimum Supported Rust Version)
    cargo +1.77.0 test --features=std,rand,rng
  10. Fuzzing rapidhash

    master

    To perform fuzz testing on rapidhash, you can use cargo fuzz or AFL. It is recommended to read the cargo fuzzing guide for general setup.

    Depending on your target, you can fuzz the raw hashing method or the RapidHasher struct (which implements std::hash::Hasher). Note that these commands require the unsafe feature to be enabled.

    # fuzz the raw rapidhash method.
    cargo +nightly fuzz run --features unsafe rapidhash
    
    # fuzz the RapidHasher struct with std::hash::Hasher write and finish calls.
    cargo +nightly fuzz run --features unsafe rapidhasher
    
    # use AFL fuzzing.
    cargo afl fuzz -i in -o out target/debug/afl_rapidhash
  11. Install and use the rapidhash CLI

    master

    The rapidhash CLI tool allows you to hash files or stdin quickly. It outputs the hash as a decimal u64. The CLI is fully compatible with C++ rapidhash V1, V2, and V3 algorithms.

    To install via cargo:

    cargo install rapidhash

    To hash a file using the V3 algorithm:

    rapidhash --v3 example.txt

    To hash from stdin:

    echo "example" | rapidhash --v3
    # install
    cargo install rapidhash
    
    # hash a file (output: 8543579700415218186)
    rapidhash --v3 example.txt
    
    # hash stdin (output: 8543579700415218186)
    echo "example" | rapidhash --v3
  12. Manage hashing seeds and secrets with RapidSecrets

    master

    RapidSecrets is a struct used to hold the seed and a set of seven internal secrets required for the rapidhash hashing process. Using custom secrets is highly recommended to provide minimal protection against trivial collision attacks (HashDoS).

    Key Behaviors

    • Pre-mixing: When you provide a seed, RapidSecrets pre-mixes it and generates a sequence of seven derived secrets. This ensures a good distribution of entropy.
    • Performance: Constructing RapidSecrets is cheap, but for performance, you should instantiate it once and reuse it for persistent hashing tasks rather than recreating it in a critical loop.
    • Compatibility: If you need to match the behavior of the C++ implementation of rapidhash, use the seed_cpp method. Note that seed_cpp uses fixed default secrets, which makes it vulnerable to collision attacks if the seed is known.

    To protect against collision attacks in persistent hashing, generate your own secrets at compile time using a randomized seed:

    use rapidhash::v3::RapidSecrets;
    
    // Change this value to a random u64 to avoid trivial collision attacks
    const MY_CUSTOM_SECRETS: RapidSecrets = RapidSecrets::seed(0x123456789ABCDEF0);
    
    pub fn my_secure_hash(data: &[u8]) -> u64 {
        // Use the seeded version of the hash function with your custom secrets
        rapidhash::v3::rapidhash_v3_seeded(data, &MY_CUSTOM_SECRETS)
    }
    use rapidhash::v3::RapidSecrets;
    const MY_CUSTOM_SECRETS: RapidSecrets = RapidSecrets::seed(0x123456789ABCDEF0);
    
    pub fn my_secure_hash(data: &[u8]) -> u64 {
        rapidhash::v3::rapidhash_v3_seeded(data, &MY_CUSTOM_SECRETS)
    }