Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

54 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rapidhash v3 for compile-time evaluation

A portable, accurate, header-only C++20 implementation of rapidhash v3 designed for compile-time (constexpr) evaluation.

Works across most platforms :

  • No compiler intrinsics
  • Forces little-endian

The trade-off for compile-time support is that a few optimizations had to be sacrificed. In practice, however, the performance impact is small and depends on your workload. See the Benchmarks section for details.

Note

This implementation contains only the classic version of rapidhash v3. The Micro/Nano versions and macro modifiers are not yet supported.

README Summary

  • API
    • Available functions
    • Examples
  • Benchmarks
    • Results
    • Building the benchmarks
  • Tests
    • Types
    • Building the tests

API

Available functions

The following functions are available in the rapidhash::cexpr namespace :

  • For raw buffers :
    • rapidhash_with_seed
    • rapidhash
  • For strings :
    • rapidhash_string_with_seed
    • rapidhash_string
  • For integrals :
    • rapidhash_integral_with_seed
    • rapidhash_integral

The default seed (when using functions that do not specify it) is set to 0.

Examples

#include "rapidhash_cexpr.h"

#include <cstddef>
#include <string>
#include <cstdint>

constexpr std::array<const std::byte, 100> kArray{};
constexpr std::string kMessage = "Hello World!";
constexpr std::uint64_t kInt = 28;

constexpr std::uint64_t kSeed = 42;

/*-------------------------*/

using rapidhash::cexpr;

// Takes a std::span<const std::byte>.
// The array stores the same type so we can take this shortcut here.
//
// Tip : You can use std::as_bytes to convert easily to a compatible span.
constexpr std::uint64_t kRawHash = rapidhash(std::span{ kArray });
constexpr std::uint64_t kSaltedRawHash = rapidhash_with_seed(std::span{ kArray }, kSeed);

// Takes a std::string_view, so you can take advantage of implicit construction.
constexpr std::uint64_t kStringHash = rapidhash_string(kMessage);
constexpr std::uint64_t kSaltedStringHash = rapidhash_string_with_seed(kMessage, kSeed);

// Works with any integral type.
constexpr std::uint64_t kIntHash = rapidhash_integral(kInt);
constexpr std::uint64_t kSaltedIntHash = rapidhash_integral_with_seed(kInt, kSeed);

// rapidhash is a one-shot hash function so, if you want to simulate a state to mix multiple
// elements together, you can do it by using the last hash as a seed for the next one :
constexpr std::uint64_t kStringIntMix = rapidhash_integral_with_seed(kMessage, kIntHash);

Results

Benchmarking is done in runtime to allow comparison with the original algorithm. However, the constexpr algorithm remains identical, so you should hit similar results during compilation.

Environment :

  • CPU : Intel Core Ultra 5 125U (Meteor Lake-M/P)
  • RAM : SK hynix 16Go DDR5 SO-DIMM PC5-5600
  • OS : Windows 11 24H2

Compiler : clang version 22.1.8

Flags :

  • bench framework : -std=c99 -Wall -Wextra -Wstrict-aliasing=1 -O2
  • hash (compatibility layer) : -std=c++20 -Wall -Wextra -O2
Test / Metric Unit Constexpr vs Original (summary) Key result
Large-input throughput (256 B → 128 MiB) MB/s 40,714 vs 40,464 (+0.6%) Essentially identical; size-dependent variation from −10.1% to +14.0%
Small fixed-size throughput (1–127 B) M hashes/s 233.3 vs 299.6 (−22.1%) Major slowdown; worst per-size loss −38.3%
Small random-size throughput ([1–N] B) M hashes/s 273.2 vs 349.0 (−21.7%) Major slowdown; worst per-size loss −35.0%
Small fixed-size latency chain M hashes/s (9.56 vs 9.61 ns/hash) 104.6 vs 104.1 (+0.5%) Same latency performance; mostly unchanged
Small random-size latency chain M hashes/s (8.07 vs 8.01 ns/hash) 123.9 vs 124.8 (−0.7%) Same latency performance; negligible difference
Small fixed throughput by size trend M hashes/s Loss ranges −12.5% → −38.3% Largest regressions around 1–16 B and 97–112 B
Small random throughput by size trend M hashes/s Loss ranges −7.0% → −35.0% Consistent slowdown across all sizes
Latency by size trend M hashes/s Usually within ±2% No meaningful regression
Run reproducibility % variation < 2% between runs Results are stable

As you can observe, there is a noticeable performance degradation for small fixed-size throughput workloads, with a measured loss of approximately 35–40%.

However this constexpr version remains a high-quality algorithm, maintaining almost identical latency and only a very small slowdown for large key sizes.

Building the benchmarks

Simply run :

make bench

This builds the bench executable in the project's root build directory. If the build directory does not already exist, it will be created automatically.

Tests

Types

We carefully test in Github Actions multiple aspects of this rewrite :

  • Matching test : verifies that the outputs for all possible algorithm branches matches the official implementation's. This way, we ensure that we distribute a faithful rapidhash v3 rewrite.

  • Endianness test : verifies that our implementation works correctly on little/big-endian.

  • Documentation test : checks if the documentation examples in this README actually compile.

  • Compiler matrix : ensures that our implementation can build correctly on supported compilers.

Building the tests

Simply run :

make tests

This builds the test executable in the project's root build directory. If the build directory does not already exist, it will be created automatically.

About

A simple and modern C++20 implementation of rapidhash v3 meant for compile-time (constexpr) execution.

Topics

Resources

Stars

Watchers

Forks

Releases

Contributors

Languages