Files
ladybird/Tests/AK/TestHashFunctions.cpp
Jelle Raaijmakers 1745926fc6 AK+Everywhere: Use MurmurHash3 for int/u64 hashing
Rework our hash functions a bit for significant better performance:

* Rename int_hash to u32_hash to mirror u64_hash.
* Make pair_int_hash call u64_hash instead of multiple u32_hash()es.
* Implement MurmurHash3's fmix32 and fmix64 for u32_hash and u64_hash.

On my machine, this speeds up u32_hash by 20%, u64_hash by ~290%, and
pair_int_hash by ~260%.

We lose the property that an input of 0 results in something that is not
0. I've experimented with an offset to both hash functions, but it
resulted in a measurable performance degradation for u64_hash. If
there's a good use case for 0 not to result in 0, we can always add in
that offset as a countermeasure in the future.
2026-02-20 22:47:24 +01:00

74 lines
1.9 KiB
C++

/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibTest/TestCase.h>
#include <AK/HashFunctions.h>
#include <AK/Types.h>
TEST_CASE(u32_hash)
{
static_assert(u32_hash(42) == 142593372u);
static_assert(u32_hash(0) == 0u);
}
TEST_CASE(pair_int_hash)
{
static_assert(pair_int_hash(42, 17) == 1110885963u);
static_assert(pair_int_hash(0, 0) == 0u);
}
TEST_CASE(u64_hash)
{
static_assert(u64_hash(42) == 2386713036u);
static_assert(u64_hash(0) == 0u);
}
TEST_CASE(ptr_hash)
{
// These tests are not static_asserts because the values are
// different and the goal is to bind the behavior.
if constexpr (sizeof(FlatPtr) == 8) {
EXPECT_EQ(ptr_hash(FlatPtr(42)), 2386713036u);
EXPECT_EQ(ptr_hash(FlatPtr(0)), 0u);
EXPECT_EQ(ptr_hash(reinterpret_cast<void const*>(42)), 2386713036u);
EXPECT_EQ(ptr_hash(reinterpret_cast<void const*>(0)), 0u);
} else {
EXPECT_EQ(ptr_hash(FlatPtr(42)), 142593372u);
EXPECT_EQ(ptr_hash(FlatPtr(0)), 0u);
EXPECT_EQ(ptr_hash(reinterpret_cast<void const*>(42)), 142593372u);
EXPECT_EQ(ptr_hash(reinterpret_cast<void const*>(0)), 0u);
}
}
TEST_CASE(constexpr_ptr_hash)
{
// This test does not check the result because the goal is just to
// ensure the function can be executed in a constexpr context. The
// "ptr_hash" test binds the result.
static_assert(ptr_hash(FlatPtr(42)));
}
template<typename HashFunction>
requires(IsCallableWithArguments<HashFunction, unsigned, u64>)
static void run_benchmark(HashFunction hash_function)
{
for (size_t i = 0; i < 1'000'000; ++i) {
auto a = hash_function(i);
AK::taint_for_optimizer(a);
auto b = hash_function(i);
AK::taint_for_optimizer(b);
EXPECT_EQ(a, b);
}
}
BENCHMARK_CASE(deterministic_hash)
{
run_benchmark(u64_hash);
}