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:
parent
e3294b1f09
commit
0a6e406927
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user