]> git.ipfire.org Git - thirdparty/make.git/commitdiff
Use Jenkins hash.
authorPaolo Bonzini <pbonzini@redhat.com>
Fri, 11 Aug 2017 11:44:28 +0000 (13:44 +0200)
committerPaul Smith <psmith@gnu.org>
Sat, 11 Nov 2017 15:08:30 +0000 (10:08 -0500)
This is about twice as fast as the current hash, and removes the
need for double hashing (improving locality of reference).  The
hash function is based on Bob Jenkins' design, slightly adapted
wherever Make needs to hash NUL-terminated strings.  The old hash
function is kept for case-insensitive hashing.

This saves 8.5% on QEMU's no-op build (from 12.87s to 11.78s).

* configure.ac: Check endianness.
* hash.c (rol32, jhash_mix, jhash_final, JHASH_INITVAL,
sum_get_unaligned_32, jhash): New.
* hash.h (STRING_HASH_1, STRING_N_HASH_1): Use jhash.
(STRING_HASH_2, STRING_N_HASH_2): Return a dummy value.
(STRING_N_COMPARE, return_STRING_N_COMPARE): Prefer memcmp to strncmp.

config.h-vms.template
config.h.W32.template
configure.ac
hash.c
hash.h
tests/scripts/targets/INTERMEDIATE
tests/scripts/targets/SECONDARY

index 780ea0b05b2895c222dfd987d2df0cec4f6bc3af..3cb9a74a465dfed4077657b0bc577e1a3ee5827a 100644 (file)
@@ -417,6 +417,10 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  */
 #define alloca(n)       __ALLOCA(n)
 #endif
 
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+/* #  undef WORDS_BIGENDIAN */
+
 /* Output sync sypport */
 #define NO_OUTPUT_SYNC
 
index c36935c4d1db8adda331cb262bec9c6f757759fc..4b5da0032e6811027c3f808f1d3ac09502bfe214 100644 (file)
@@ -450,6 +450,10 @@ char *ttyname (int);
 /* # undef _ALL_SOURCE */
 #endif
 
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+/* #  undef WORDS_BIGENDIAN */
+
 /* Number of bits in a file offset, on hosts where this is settable. */
 /* #undef _FILE_OFFSET_BITS */
 
index 1b03135f39d5c69095327dd5915d2fc4d653b60e..9b8aed968f0f1b8fa2e88889c57f9e892108fd33 100644 (file)
@@ -49,6 +49,7 @@ AC_CANONICAL_HOST
 AC_AIX
 AC_ISC_POSIX
 AC_MINIX
+AC_C_BIGENDIAN
 
 # Enable gettext, in "external" mode.
 AM_GNU_GETTEXT_VERSION([0.19.4])
diff --git a/hash.c b/hash.c
index e1688870232fd766a0f02204a6ad0ac88ad9f414..c138aaf6b5d2adee12332b19e4441c3cdc36c794 100644 (file)
--- a/hash.c
+++ b/hash.c
@@ -327,3 +327,151 @@ round_up_2 (unsigned long n)
 
   return n + 1;
 }
+
+#define rol32(v, n) \
+       ((v) << (n) | ((v) >> (32 - (n))))
+
+/* jhash_mix -- mix 3 32-bit values reversibly. */
+#define jhash_mix(a, b, c)                      \
+{                                               \
+        a -= c;  a ^= rol32(c, 4);  c += b;     \
+        b -= a;  b ^= rol32(a, 6);  a += c;     \
+        c -= b;  c ^= rol32(b, 8);  b += a;     \
+        a -= c;  a ^= rol32(c, 16); c += b;     \
+        b -= a;  b ^= rol32(a, 19); a += c;     \
+        c -= b;  c ^= rol32(b, 4);  b += a;     \
+}
+
+/* jhash_final - final mixing of 3 32-bit values (a,b,c) into c */
+#define jhash_final(a, b, c)                    \
+{                                               \
+        c ^= b; c -= rol32(b, 14);              \
+        a ^= c; a -= rol32(c, 11);              \
+        b ^= a; b -= rol32(a, 25);              \
+        c ^= b; c -= rol32(b, 16);              \
+        a ^= c; a -= rol32(c, 4);               \
+        b ^= a; b -= rol32(a, 14);              \
+        c ^= b; c -= rol32(b, 24);              \
+}
+
+/* An arbitrary initial parameter */
+#define JHASH_INITVAL           0xdeadbeef
+
+#define sum_get_unaligned_32(r, p)              \
+  do {                                          \
+    unsigned int val;                           \
+    memcpy(&val, (p), 4);                       \
+    r += val;                                   \
+  } while(0);
+
+unsigned jhash(unsigned const char *k, int length)
+{
+  unsigned int a, b, c;
+
+  /* Set up the internal state */
+  a = b = c = JHASH_INITVAL + length;
+
+  /* All but the last block: affect some 32 bits of (a,b,c) */
+  while (length > 12) {
+    sum_get_unaligned_32(a, k);
+    sum_get_unaligned_32(b, k + 4);
+    sum_get_unaligned_32(c, k + 8);
+    jhash_mix(a, b, c);
+    length -= 12;
+    k += 12;
+  }
+
+  if (!length)
+    return c;
+
+  if (length > 8)
+    {
+      sum_get_unaligned_32(a, k);
+      length -= 4;
+      k += 4;
+    }
+  if (length > 4)
+    {
+      sum_get_unaligned_32(b, k);
+      length -= 4;
+      k += 4;
+    }
+
+  if (length == 4)
+    c += (unsigned)k[3]<<24;
+  if (length >= 3)
+    c += (unsigned)k[2]<<16;
+  if (length >= 2)
+    c += (unsigned)k[1]<<8;
+  c += k[0];
+  jhash_final(a, b, c);
+  return c;
+}
+
+#ifdef WORDS_BIGENDIAN
+/* The ifs are ordered from the first byte in memory to the last.  */
+#define sum_up_to_nul(r, p, flag)         \
+  do {                                    \
+    unsigned int val;                     \
+    memcpy(&val, (p), 4);                 \
+    if ((val & 0xFF000000) == 0)          \
+      flag = 1;                           \
+    else if ((val & 0xFF0000) == 0)       \
+      r += val & ~0xFFFF, flag = 1;       \
+    else if ((val & 0xFF00) == 0)         \
+      r += val & ~0xFF, flag = 1;         \
+    else                                  \
+      r += val, flag = (val & 0xFF) == 0; \
+  } while (0)
+#else
+/* First detect the presence of zeroes.  If there is none, we can
+   sum the 4 bytes directly.  Otherwise, the ifs are ordered as in the
+   big endian case, from the first byte in memory to the last.  */
+#define sum_up_to_nul(r, p, flag)                   \
+  do {                                              \
+    unsigned int val;                               \
+    unsigned int zeroes;                            \
+    memcpy(&val, (p), 4);                           \
+    zeroes = ((val - 0x01010101) & ~val);           \
+    if (!(zeroes & 0x80808080))                     \
+      r += val;                                     \
+    else if ((val & 0xFF) == 0)                     \
+      flag = 1;                                     \
+    else if ((val & 0xFF00) == 0)                   \
+      r += val & 0xFF, flag = 1;                    \
+    else if ((val & 0xFF0000) == 0)                 \
+      r += val & 0xFFFF, flag = 1;                  \
+    else                                            \
+      r += val, flag = 1;                           \
+  } while (0)
+#endif
+
+unsigned jhash_string(unsigned const char *k)
+{
+  unsigned int a, b, c;
+  unsigned int have_nul = 0;
+  unsigned const char *start = k;
+
+  /* Set up the internal state */
+  a = b = c = JHASH_INITVAL;
+
+  /* All but the last block: affect some 32 bits of (a,b,c) */
+  for (;;) {
+    sum_up_to_nul(a, k, have_nul);
+    if (have_nul)
+      break;
+    k += 4;
+    sum_up_to_nul(b, k, have_nul);
+    if (have_nul)
+      break;
+    k += 4;
+    sum_up_to_nul(c, k, have_nul);
+    if (have_nul)
+      break;
+    k += 4;
+    jhash_mix(a, b, c);
+  }
+
+  jhash_final(a, b, c);
+  return c + (k - start);
+}
diff --git a/hash.h b/hash.h
index 960cbd78b1f1d38a89c3d24035847b146f11125f..667d6508267065630284c51ff678aab5689966be 100644 (file)
--- a/hash.h
+++ b/hash.h
@@ -73,6 +73,9 @@ void hash_map_arg __P((struct hash_table *ht, hash_map_arg_func_t map, void *arg
 void hash_print_stats __P((struct hash_table *ht, FILE *out_FILE));
 void **hash_dump __P((struct hash_table *ht, void **vector_0, qsort_cmp_t compare));
 
+extern unsigned jhash(unsigned char const *key, int n);
+extern unsigned jhash_string(unsigned char const *key);
+
 extern void *hash_deleted_item;
 #define HASH_VACANT(item) ((item) == 0 || (void *) (item) == hash_deleted_item)
 
@@ -83,9 +86,8 @@ extern void *hash_deleted_item;
    be identical.  Take advantage of that to short-circuit string compares.  */
 
 #define STRING_HASH_1(KEY, RESULT) do { \
-  unsigned char const *_key_ = (unsigned char const *) (KEY) - 1; \
-  while (*++_key_) \
-    (RESULT) += (*_key_ << (_key_[1] & 0xf)); \
+  unsigned char const *_key_ = (unsigned char const *) (KEY); \
+  (RESULT) += jhash_string(_key_); \
 } while (0)
 #define return_STRING_HASH_1(KEY) do { \
   unsigned long _result_ = 0; \
@@ -93,10 +95,11 @@ extern void *hash_deleted_item;
   return _result_; \
 } while (0)
 
+/* No need for a second hash because jhash already provides
+   pretty good results.  However, do evaluate the arguments
+   to avoid warnings.  */
 #define STRING_HASH_2(KEY, RESULT) do { \
-  unsigned char const *_key_ = (unsigned char const *) (KEY) - 1; \
-  while (*++_key_) \
-    (RESULT) += (*_key_ << (_key_[1] & 0x7)); \
+  (void)(KEY); \
 } while (0)
 #define return_STRING_HASH_2(KEY) do { \
   unsigned long _result_ = 0; \
@@ -113,27 +116,24 @@ extern void *hash_deleted_item;
 
 
 #define STRING_N_HASH_1(KEY, N, RESULT) do { \
-  unsigned char const *_key_ = (unsigned char const *) (KEY) - 1; \
-  int _n_ = (N); \
-  if (_n_) \
-    while (--_n_ && *++_key_) \
-      (RESULT) += (*_key_ << (_key_[1] & 0xf)); \
-  (RESULT) += *++_key_; \
+  unsigned char const *_key_ = (unsigned char const *) (KEY); \
+  (RESULT) += jhash(_key_, N); \
 } while (0)
+
 #define return_STRING_N_HASH_1(KEY, N) do { \
   unsigned long _result_ = 0; \
   STRING_N_HASH_1 ((KEY), (N), _result_); \
   return _result_; \
 } while (0)
 
+/* No need for a second hash because jhash already provides
+   pretty good results.  However, do evaluate the arguments
+   to avoid warnings.  */
 #define STRING_N_HASH_2(KEY, N, RESULT) do { \
-  unsigned char const *_key_ = (unsigned char const *) (KEY) - 1; \
-  int _n_ = (N); \
-  if (_n_) \
-    while (--_n_ && *++_key_) \
-      (RESULT) += (*_key_ << (_key_[1] & 0x7)); \
-  (RESULT) += *++_key_; \
+  (void)(KEY); \
+  (void)(N); \
 } while (0)
+
 #define return_STRING_N_HASH_2(KEY, N) do { \
   unsigned long _result_ = 0; \
   STRING_N_HASH_2 ((KEY), (N), _result_); \
@@ -141,10 +141,10 @@ extern void *hash_deleted_item;
 } while (0)
 
 #define STRING_N_COMPARE(X, Y, N, RESULT) do { \
-  RESULT = (X) == (Y) ? 0 : strncmp ((X), (Y), (N)); \
+  RESULT = (X) == (Y) ? 0 : memcmp ((X), (Y), (N)); \
 } while (0)
 #define return_STRING_N_COMPARE(X, Y, N) do { \
-  return (X) == (Y) ? 0 : strncmp ((X), (Y), (N)); \
+  return (X) == (Y) ? 0 : memcmp ((X), (Y), (N)); \
 } while (0)
 
 #ifdef HAVE_CASE_INSENSITIVE_FS
index 2b3021b2352cfd53a6b92cf17a64ea8d9dee15e6..512498c3093e8ccc8609ccda4a246a1dfef0ff10 100644 (file)
@@ -57,7 +57,7 @@ $answer = "cp foo.f foo.e\ncp foo.e foo.d\nrm foo.e\n";
 # TEST #3
 
 &run_make_with_options($makefile,'foo.c',&get_logfile);
-$answer = "cp foo.f foo.e\ncp bar.f bar.e\ncat foo.e bar.e > foo.c\nrm bar.e foo.e\n";
+$answer = "cp foo.f foo.e\ncp bar.f bar.e\ncat foo.e bar.e > foo.c\nrm foo.e bar.e\n";
 &compare_output($answer, &get_logfile(1));
 
 # TEST #4
@@ -72,7 +72,7 @@ $answer = "$make_name: 'foo.c' is up to date.\n";
 &touch('foo.f');
 
 &run_make_with_options($makefile,'foo.c',&get_logfile);
-$answer = "cp foo.f foo.e\ncp bar.f bar.e\ncat foo.e bar.e > foo.c\nrm bar.e foo.e\n";
+$answer = "cp foo.f foo.e\ncp bar.f bar.e\ncat foo.e bar.e > foo.c\nrm foo.e bar.e\n";
 &compare_output($answer, &get_logfile(1));
 
 # TEST #6 -- added for PR/1669: don't remove files mentioned on the cmd line.
index 447c275e8563102bdf32772597bddb2637300742..963c240597a35b9b7aa14f49bb0f6bb3f22194a2 100644 (file)
@@ -93,8 +93,7 @@ print MAKEFILE <<'EOF';
 final: intermediate
 intermediate: source
 
-final intermediate source:
-       echo $< > $@
+final intermediate source: ; echo $< > $@
 EOF
 
 close(MAKEFILE);
@@ -135,7 +134,7 @@ all : 1.c 2.c
 cp 1.b 1.c
 cp 2.a 2.b
 cp 2.b 2.c
-rm 1.b 2.b');
+rm 2.b 1.b');
 
 unlink(qw(1.a 2.a 1.c 2.c));