Further improve hashing of pointers and integers.

Also make sure that the appropriate functions are inlined properly
(using V8_INLINE instead of inline to enforce it even with GCC),
and improve the HashIsOkish unittest.

TEST=unittests
R=svenpanne@chromium.org

Review URL: https://codereview.chromium.org/635733002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24427 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
bmeurer@chromium.org 2014-10-07 07:39:19 +00:00
parent e3294b1f09
commit 0a6e406927
3 changed files with 67 additions and 52 deletions

View File

@ -19,18 +19,49 @@ namespace base {
namespace {
// Thomas Wang, Integer Hash Functions.
// https://gist.github.com/badboy/6267743
template <typename T>
inline size_t hash_value_unsigned(T value) {
const unsigned size_t_bits = std::numeric_limits<size_t>::digits;
// ceiling(std::numeric_limits<T>::digits / size_t_bits) - 1
const unsigned length = (std::numeric_limits<T>::digits - 1) / size_t_bits;
size_t seed = 0;
// Hopefully, this loop can be unrolled.
for (unsigned i = length * size_t_bits; i > 0; i -= size_t_bits) {
seed ^= static_cast<size_t>(value >> i) + (seed << 6) + (seed >> 2);
V8_INLINE size_t hash_value_unsigned(T v) {
switch (sizeof(T)) {
case 4: {
// "32 bit Mix Functions"
v = ~v + (v << 15); // v = (v << 15) - v - 1;
v = v ^ (v >> 12);
v = v + (v << 2);
v = v ^ (v >> 4);
v = v * 2057; // v = (v + (v << 3)) + (v << 11);
v = v ^ (v >> 16);
return static_cast<size_t>(v);
}
case 8: {
switch (sizeof(size_t)) {
case 4: {
// "64 bit to 32 bit Hash Functions"
v = ~v + (v << 18); // v = (v << 18) - v - 1;
v = v ^ (v >> 31);
v = v * 21; // v = (v + (v << 2)) + (v << 4);
v = v ^ (v >> 11);
v = v + (v << 6);
v = v ^ (v >> 22);
return static_cast<size_t>(v);
}
case 8: {
// "64 bit Mix Functions"
v = ~v + (v << 21); // v = (v << 21) - v - 1;
v = v ^ (v >> 24);
v = (v + (v << 3)) + (v << 8); // v * 265
v = v ^ (v >> 14);
v = (v + (v << 2)) + (v << 4); // v * 21
v = v ^ (v >> 28);
v = v + (v << 31);
return static_cast<size_t>(v);
}
}
}
}
seed ^= static_cast<size_t>(value) + (seed << 6) + (seed >> 2);
return seed;
UNREACHABLE();
return static_cast<size_t>(v);
}
} // namespace
@ -64,17 +95,7 @@ size_t hash_combine(size_t seed, size_t value) {
}
// Thomas Wang, Integer Hash Functions.
// http://www.concentric.net/~Ttwang/tech/inthash.htm
size_t hash_value(unsigned int v) {
v = ~v + (v << 15); // v = (v << 15) - v - 1;
v = v ^ (v >> 12);
v = v + (v << 2);
v = v ^ (v >> 4);
v = v * 2057; // v = (v + (v << 3)) + (v << 11);
v = v ^ (v >> 16);
return v;
}
size_t hash_value(unsigned int v) { return hash_value_unsigned(v); }
size_t hash_value(unsigned long v) { // NOLINT(runtime/int)
@ -86,17 +107,5 @@ size_t hash_value(unsigned long long v) { // NOLINT(runtime/int)
return hash_value_unsigned(v);
}
size_t hash_value(float v) {
// 0 and -0 both hash to zero.
return v != 0.0f ? hash_value_unsigned(bit_cast<uint32_t>(v)) : 0;
}
size_t hash_value(double v) {
// 0 and -0 both hash to zero.
return v != 0.0 ? hash_value_unsigned(bit_cast<uint64_t>(v)) : 0;
}
} // namespace base
} // namespace v8

View File

@ -62,17 +62,17 @@ template <typename>
struct hash;
inline size_t hash_combine() { return 0u; }
inline size_t hash_combine(size_t seed) { return seed; }
V8_INLINE size_t hash_combine() { return 0u; }
V8_INLINE size_t hash_combine(size_t seed) { return seed; }
size_t hash_combine(size_t seed, size_t value);
template <typename T, typename... Ts>
inline size_t hash_combine(T const& v, Ts const&... vs) {
V8_INLINE size_t hash_combine(T const& v, Ts const&... vs) {
return hash_combine(hash<T>()(v), hash_combine(vs...));
}
#define V8_BASE_HASH_VALUE_TRIVIAL(type) \
inline size_t hash_value(type v) { return static_cast<size_t>(v); }
V8_INLINE size_t hash_value(type v) { return static_cast<size_t>(v); }
V8_BASE_HASH_VALUE_TRIVIAL(bool)
V8_BASE_HASH_VALUE_TRIVIAL(unsigned char)
V8_BASE_HASH_VALUE_TRIVIAL(unsigned short) // NOLINT(runtime/int)
@ -83,7 +83,7 @@ size_t hash_value(unsigned long); // NOLINT(runtime/int)
size_t hash_value(unsigned long long); // NOLINT(runtime/int)
#define V8_BASE_HASH_VALUE_SIGNED(type) \
inline size_t hash_value(signed type v) { \
V8_INLINE size_t hash_value(signed type v) { \
return hash_value(bit_cast<unsigned type>(v)); \
}
V8_BASE_HASH_VALUE_SIGNED(char)
@ -93,30 +93,36 @@ V8_BASE_HASH_VALUE_SIGNED(long) // NOLINT(runtime/int)
V8_BASE_HASH_VALUE_SIGNED(long long) // NOLINT(runtime/int)
#undef V8_BASE_HASH_VALUE_SIGNED
size_t hash_value(float v);
size_t hash_value(double v);
V8_INLINE size_t hash_value(float v) {
// 0 and -0 both hash to zero.
return v != 0.0f ? hash_value(bit_cast<uint32_t>(v)) : 0;
}
V8_INLINE size_t hash_value(double v) {
// 0 and -0 both hash to zero.
return v != 0.0 ? hash_value(bit_cast<uint64_t>(v)) : 0;
}
template <typename T>
inline size_t hash_value(T* const& v) {
size_t const x = bit_cast<size_t>(v);
return (x >> 3) + x;
V8_INLINE size_t hash_value(T* const& v) {
return hash_value(bit_cast<uintptr_t>(v));
}
template <typename T1, typename T2>
inline size_t hash_value(std::pair<T1, T2> const& v) {
V8_INLINE size_t hash_value(std::pair<T1, T2> const& v) {
return hash_combine(v.first, v.second);
}
template <typename T>
struct hash : public std::unary_function<T, size_t> {
size_t operator()(T const& v) const { return hash_value(v); }
V8_INLINE size_t operator()(T const& v) const { return hash_value(v); }
};
#define V8_BASE_HASH_SPECIALIZE(type) \
template <> \
struct hash<type> : public std::unary_function<type, size_t> { \
size_t operator()(type const v) const { \
V8_INLINE size_t operator()(type const v) const { \
return ::v8::base::hash_value(v); \
} \
};
@ -137,13 +143,15 @@ V8_BASE_HASH_SPECIALIZE(double)
template <typename T>
struct hash<T*> : public std::unary_function<T*, size_t> {
size_t operator()(T* const v) const { return ::v8::base::hash_value(v); }
V8_INLINE size_t operator()(T* const v) const {
return ::v8::base::hash_value(v);
}
};
template <typename T1, typename T2>
struct hash<std::pair<T1, T2> >
: public std::unary_function<std::pair<T1, T2>, size_t> {
size_t operator()(std::pair<T1, T2> const& v) const {
V8_INLINE size_t operator()(std::pair<T1, T2> const& v) const {
return ::v8::base::hash_value(v);
}
};

View File

@ -82,10 +82,8 @@ TYPED_TEST(FunctionalTest, HashIsStateless) {
TYPED_TEST(FunctionalTest, HashIsOkish) {
const size_t kValues = 128;
const size_t kMinHashes = kValues / 4;
std::set<TypeParam> vs;
while (vs.size() != kValues) {
for (size_t i = 0; i < 128; ++i) {
TypeParam v;
this->rng()->NextBytes(&v, sizeof(v));
vs.insert(v);
@ -95,7 +93,7 @@ TYPED_TEST(FunctionalTest, HashIsOkish) {
hash<TypeParam> h;
hs.insert(h(v));
}
EXPECT_LE(kMinHashes, hs.size());
EXPECT_LE(vs.size() / 4u, hs.size());
}