mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
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.
This commit is contained in:
committed by
Andreas Kling
parent
ebda8fcf11
commit
1745926fc6
Notes:
github-actions[bot]
2026-02-20 21:48:50 +00:00
Author: https://github.com/gmta Commit: https://github.com/LadybirdBrowser/ladybird/commit/1745926fc64 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/8048
@@ -8,27 +8,31 @@
|
|||||||
|
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
|
||||||
constexpr unsigned int_hash(u32 key)
|
// MurmurHash3 32-bit finalizer (fmix32).
|
||||||
|
constexpr unsigned u32_hash(u32 key)
|
||||||
{
|
{
|
||||||
key += ~(key << 15);
|
key ^= key >> 16;
|
||||||
key ^= (key >> 10);
|
key *= 0x85ebca6bU;
|
||||||
key += (key << 3);
|
key ^= key >> 13;
|
||||||
key ^= (key >> 6);
|
key *= 0xc2b2ae35U;
|
||||||
key += ~(key << 11);
|
key ^= key >> 16;
|
||||||
key ^= (key >> 16);
|
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MurmurHash3 64-bit finalizer (fmix64).
|
||||||
|
constexpr unsigned u64_hash(u64 key)
|
||||||
|
{
|
||||||
|
key ^= key >> 33;
|
||||||
|
key *= 0xff51afd7ed558ccdULL;
|
||||||
|
key ^= key >> 33;
|
||||||
|
key *= 0xc4ceb9fe1a85ec53ULL;
|
||||||
|
key ^= key >> 33;
|
||||||
|
return static_cast<unsigned>(key);
|
||||||
|
}
|
||||||
|
|
||||||
constexpr unsigned pair_int_hash(u32 key1, u32 key2)
|
constexpr unsigned pair_int_hash(u32 key1, u32 key2)
|
||||||
{
|
{
|
||||||
return int_hash((int_hash(key1) * 209) ^ (int_hash(key2 * 413)));
|
return u64_hash((static_cast<u64>(key1) << 32) | key2);
|
||||||
}
|
|
||||||
|
|
||||||
constexpr unsigned u64_hash(u64 key)
|
|
||||||
{
|
|
||||||
u32 first = key & 0xFFFFFFFF;
|
|
||||||
u32 last = key >> 32;
|
|
||||||
return pair_int_hash(first, last);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr unsigned ptr_hash(FlatPtr ptr)
|
constexpr unsigned ptr_hash(FlatPtr ptr)
|
||||||
@@ -36,7 +40,7 @@ constexpr unsigned ptr_hash(FlatPtr ptr)
|
|||||||
if constexpr (sizeof(ptr) == 8)
|
if constexpr (sizeof(ptr) == 8)
|
||||||
return u64_hash(ptr);
|
return u64_hash(ptr);
|
||||||
else
|
else
|
||||||
return int_hash(ptr);
|
return u32_hash(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline unsigned ptr_hash(void const* ptr)
|
inline unsigned ptr_hash(void const* ptr)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ struct Traits<T> : public DefaultTraits<T> {
|
|||||||
static unsigned hash(T value)
|
static unsigned hash(T value)
|
||||||
{
|
{
|
||||||
if constexpr (sizeof(T) < 8)
|
if constexpr (sizeof(T) < 8)
|
||||||
return int_hash(value);
|
return u32_hash(value);
|
||||||
else
|
else
|
||||||
return u64_hash(value);
|
return u64_hash(value);
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ struct Traits<T> : public DefaultTraits<T> {
|
|||||||
static unsigned hash(T value)
|
static unsigned hash(T value)
|
||||||
{
|
{
|
||||||
if constexpr (sizeof(T) < 8)
|
if constexpr (sizeof(T) < 8)
|
||||||
return int_hash(bit_cast<u32>(value));
|
return u32_hash(bit_cast<u32>(value));
|
||||||
else
|
else
|
||||||
return u64_hash(bit_cast<u64>(value));
|
return u64_hash(bit_cast<u64>(value));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -749,7 +749,7 @@ class Traits<Color> : public DefaultTraits<Color> {
|
|||||||
public:
|
public:
|
||||||
static unsigned hash(Color const& color)
|
static unsigned hash(Color const& color)
|
||||||
{
|
{
|
||||||
return int_hash(color.value());
|
return u32_hash(color.value());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ struct FontCacheKey {
|
|||||||
|
|
||||||
unsigned hash() const
|
unsigned hash() const
|
||||||
{
|
{
|
||||||
auto h = pair_int_hash(int_hash(bit_cast<u32>(point_size)), axes.size());
|
auto h = pair_int_hash(u32_hash(bit_cast<u32>(point_size)), axes.size());
|
||||||
for (auto const& axis : axes)
|
for (auto const& axis : axes)
|
||||||
h = pair_int_hash(h, pair_int_hash(axis.tag.to_u32(), int_hash(bit_cast<u32>(axis.value))));
|
h = pair_int_hash(h, pair_int_hash(axis.tag.to_u32(), u32_hash(bit_cast<u32>(axis.value))));
|
||||||
h = pair_int_hash(h, Traits<Gfx::ShapeFeatures>::hash(shape_features));
|
h = pair_int_hash(h, Traits<Gfx::ShapeFeatures>::hash(shape_features));
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ struct Traits<JS::PropertyKey> : public DefaultTraits<JS::PropertyKey> {
|
|||||||
if (name.is_symbol())
|
if (name.is_symbol())
|
||||||
return ptr_hash(name.as_symbol());
|
return ptr_hash(name.as_symbol());
|
||||||
if (name.is_number())
|
if (name.is_number())
|
||||||
return int_hash(name.as_number());
|
return u32_hash(name.as_number());
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -721,6 +721,6 @@ template<typename Parser>
|
|||||||
struct AK::Traits<regex::CacheKey<Parser>> : public AK::DefaultTraits<regex::CacheKey<Parser>> {
|
struct AK::Traits<regex::CacheKey<Parser>> : public AK::DefaultTraits<regex::CacheKey<Parser>> {
|
||||||
static unsigned hash(regex::CacheKey<Parser> const& key)
|
static unsigned hash(regex::CacheKey<Parser> const& key)
|
||||||
{
|
{
|
||||||
return pair_int_hash(key.pattern.hash(), int_hash(to_underlying(key.options.value())));
|
return pair_int_hash(key.pattern.hash(), u32_hash(to_underlying(key.options.value())));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ private:
|
|||||||
for (int i = 0; i < 128; i++) {
|
for (int i = 0; i < 128; i++) {
|
||||||
hash ^= ptr_hash(node->m_children[i].ptr());
|
hash ^= ptr_hash(node->m_children[i].ptr());
|
||||||
}
|
}
|
||||||
hash ^= int_hash(static_cast<u32>(node->m_is_terminal));
|
hash ^= u32_hash(static_cast<u32>(node->m_is_terminal));
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
static bool equals(NonnullRefPtr<Node> const& a, NonnullRefPtr<Node> const& b)
|
static bool equals(NonnullRefPtr<Node> const& a, NonnullRefPtr<Node> const& b)
|
||||||
|
|||||||
@@ -9,22 +9,22 @@
|
|||||||
#include <AK/HashFunctions.h>
|
#include <AK/HashFunctions.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
|
||||||
TEST_CASE(int_hash)
|
TEST_CASE(u32_hash)
|
||||||
{
|
{
|
||||||
static_assert(int_hash(42) == 3564735745u);
|
static_assert(u32_hash(42) == 142593372u);
|
||||||
static_assert(int_hash(0) == 1177991625u);
|
static_assert(u32_hash(0) == 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE(pair_int_hash)
|
TEST_CASE(pair_int_hash)
|
||||||
{
|
{
|
||||||
static_assert(pair_int_hash(42, 17) == 339337046u);
|
static_assert(pair_int_hash(42, 17) == 1110885963u);
|
||||||
static_assert(pair_int_hash(0, 0) == 954888656u);
|
static_assert(pair_int_hash(0, 0) == 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE(u64_hash)
|
TEST_CASE(u64_hash)
|
||||||
{
|
{
|
||||||
static_assert(u64_hash(42) == 2824066580u);
|
static_assert(u64_hash(42) == 2386713036u);
|
||||||
static_assert(u64_hash(0) == 954888656u);
|
static_assert(u64_hash(0) == 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE(ptr_hash)
|
TEST_CASE(ptr_hash)
|
||||||
@@ -32,17 +32,17 @@ TEST_CASE(ptr_hash)
|
|||||||
// These tests are not static_asserts because the values are
|
// These tests are not static_asserts because the values are
|
||||||
// different and the goal is to bind the behavior.
|
// different and the goal is to bind the behavior.
|
||||||
if constexpr (sizeof(FlatPtr) == 8) {
|
if constexpr (sizeof(FlatPtr) == 8) {
|
||||||
EXPECT_EQ(ptr_hash(FlatPtr(42)), 2824066580u);
|
EXPECT_EQ(ptr_hash(FlatPtr(42)), 2386713036u);
|
||||||
EXPECT_EQ(ptr_hash(FlatPtr(0)), 954888656u);
|
EXPECT_EQ(ptr_hash(FlatPtr(0)), 0u);
|
||||||
|
|
||||||
EXPECT_EQ(ptr_hash(reinterpret_cast<void const*>(42)), 2824066580u);
|
EXPECT_EQ(ptr_hash(reinterpret_cast<void const*>(42)), 2386713036u);
|
||||||
EXPECT_EQ(ptr_hash(reinterpret_cast<void const*>(0)), 954888656u);
|
EXPECT_EQ(ptr_hash(reinterpret_cast<void const*>(0)), 0u);
|
||||||
} else {
|
} else {
|
||||||
EXPECT_EQ(ptr_hash(FlatPtr(42)), 3564735745u);
|
EXPECT_EQ(ptr_hash(FlatPtr(42)), 142593372u);
|
||||||
EXPECT_EQ(ptr_hash(FlatPtr(0)), 1177991625u);
|
EXPECT_EQ(ptr_hash(FlatPtr(0)), 0u);
|
||||||
|
|
||||||
EXPECT_EQ(ptr_hash(reinterpret_cast<void const*>(42)), 3564735745u);
|
EXPECT_EQ(ptr_hash(reinterpret_cast<void const*>(42)), 142593372u);
|
||||||
EXPECT_EQ(ptr_hash(reinterpret_cast<void const*>(0)), 1177991625u);
|
EXPECT_EQ(ptr_hash(reinterpret_cast<void const*>(0)), 0u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,10 +87,10 @@ Program (script) @4:1
|
|||||||
│ │ ├─ BindingPattern (array)
|
│ │ ├─ BindingPattern (array)
|
||||||
│ │ │ ├─ entry
|
│ │ │ ├─ entry
|
||||||
│ │ │ │ └─ alias
|
│ │ │ │ └─ alias
|
||||||
│ │ │ │ └─ Identifier "a" [variable:2] @28:9
|
│ │ │ │ └─ Identifier "a" [variable:1] @28:9
|
||||||
│ │ │ └─ entry
|
│ │ │ └─ entry
|
||||||
│ │ │ └─ alias
|
│ │ │ └─ alias
|
||||||
│ │ │ └─ Identifier "b" [variable:3] @28:9
|
│ │ │ └─ Identifier "b" [variable:4] @28:9
|
||||||
│ │ └─ ArrayExpression @28:18
|
│ │ └─ ArrayExpression @28:18
|
||||||
│ │ ├─ NumericLiteral 1 @28:19
|
│ │ ├─ NumericLiteral 1 @28:19
|
||||||
│ │ └─ NumericLiteral 2 @28:22
|
│ │ └─ NumericLiteral 2 @28:22
|
||||||
@@ -99,10 +99,10 @@ Program (script) @4:1
|
|||||||
│ │ ├─ BindingPattern (object)
|
│ │ ├─ BindingPattern (object)
|
||||||
│ │ │ ├─ entry
|
│ │ │ ├─ entry
|
||||||
│ │ │ │ └─ name
|
│ │ │ │ └─ name
|
||||||
│ │ │ │ └─ Identifier "c" [variable:1] @29:11
|
│ │ │ │ └─ Identifier "c" [variable:3] @29:11
|
||||||
│ │ │ └─ entry
|
│ │ │ └─ entry
|
||||||
│ │ │ └─ name
|
│ │ │ └─ name
|
||||||
│ │ │ └─ Identifier "d" [variable:4] @29:11
|
│ │ │ └─ Identifier "d" [variable:2] @29:11
|
||||||
│ │ └─ ObjectExpression @29:22
|
│ │ └─ ObjectExpression @29:22
|
||||||
│ │ ├─ ObjectProperty @29:22
|
│ │ ├─ ObjectProperty @29:22
|
||||||
│ │ │ ├─ StringLiteral "c" @29:22
|
│ │ │ ├─ StringLiteral "c" @29:22
|
||||||
@@ -129,10 +129,10 @@ Program (script) @4:1
|
|||||||
│ │ ├─ BinaryExpression (+) @31:22
|
│ │ ├─ BinaryExpression (+) @31:22
|
||||||
│ │ │ ├─ BinaryExpression (+) @31:18
|
│ │ │ ├─ BinaryExpression (+) @31:18
|
||||||
│ │ │ │ ├─ BinaryExpression (+) @31:14
|
│ │ │ │ ├─ BinaryExpression (+) @31:14
|
||||||
│ │ │ │ │ ├─ Identifier "a" [variable:2] @31:12
|
│ │ │ │ │ ├─ Identifier "a" [variable:1] @31:12
|
||||||
│ │ │ │ │ └─ Identifier "b" [variable:3] @31:16
|
│ │ │ │ │ └─ Identifier "b" [variable:4] @31:16
|
||||||
│ │ │ │ └─ Identifier "c" [variable:1] @31:20
|
│ │ │ │ └─ Identifier "c" [variable:3] @31:20
|
||||||
│ │ │ └─ Identifier "d" [variable:4] @31:24
|
│ │ │ └─ Identifier "d" [variable:2] @31:24
|
||||||
│ │ └─ Identifier "e" [variable:0] @31:28
|
│ │ └─ Identifier "e" [variable:0] @31:28
|
||||||
│ └─ MemberExpression [computed] @31:33
|
│ └─ MemberExpression [computed] @31:33
|
||||||
│ ├─ Identifier "f" [variable:5] @31:32
|
│ ├─ Identifier "f" [variable:5] @31:32
|
||||||
|
|||||||
Reference in New Issue
Block a user