Further improve hashing of pointers and integers.
authorbmeurer@chromium.org <bmeurer@chromium.org>
Tue, 7 Oct 2014 07:39:19 +0000 (07:39 +0000)
committerbmeurer@chromium.org <bmeurer@chromium.org>
Tue, 7 Oct 2014 07:39:19 +0000 (07:39 +0000)
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

src/base/functional.cc
src/base/functional.h
test/unittests/base/functional-unittest.cc

index 4c0f3c8..d212912 100644 (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
index d815c72..7c86f32 100644 (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);
   }
 };
index 4015485..d15aa5d 100644 (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());
 }