]> granicus.if.org Git - php/commitdiff
Improve PHP hash function.
authorDmitry Stogov <dmitry@zend.com>
Wed, 8 May 2019 13:43:54 +0000 (16:43 +0300)
committerDmitry Stogov <dmitry@zend.com>
Wed, 8 May 2019 13:47:12 +0000 (16:47 +0300)
See Daniel Lemire's blog post https://lemire.me/blog/2016/07/21/accelerating-php-hashing-by-unoptimizing-it/

Zend/zend_string.h

index 4ede3f03819e0738a774c571b9842bebffa32a61..793a519ce21a2b896b1c280ba78128da2924ee4a 100644 (file)
@@ -361,6 +361,49 @@ static zend_always_inline zend_ulong zend_inline_hash_func(const char *str, size
 {
        zend_ulong hash = Z_UL(5381);
 
+#if defined(_WIN32) || defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
+       /* Version with multiplication works better on modern CPU */
+       for (; len >= 8; len -= 8, str += 8) {
+               hash =
+                       hash   * 33 * 33 * 33 * 33 +
+                       str[0] * 33 * 33 * 33 +
+                       str[1] * 33 * 33 +
+                       str[2] * 33 +
+                       str[3];
+               hash =
+                       hash   * 33 * 33 * 33 * 33 +
+                       str[4] * 33 * 33 * 33 +
+                       str[5] * 33 * 33 +
+                       str[6] * 33 +
+                       str[7];
+       }
+       if (len >= 4) {
+               hash =
+                       hash   * 33 * 33 * 33 * 33 +
+                       str[0] * 33 * 33 * 33 +
+                       str[1] * 33 * 33 +
+                       str[2] * 33 +
+                       str[3];
+               len -= 4;
+               str += 4;
+       }
+       if (len >= 2) {
+               if (len > 2) {
+                       hash =
+                               hash   * 33 * 33 * 33 +
+                               str[0] * 33 * 33 +
+                               str[1] * 33 +
+                               str[2];
+               } else {
+                       hash =
+                               hash   * 33 * 33 +
+                               str[0] * 33 +
+                               str[1];
+               }
+       } else if (len != 0) {
+               hash = hash * 33 + *str;
+       }
+#else
        /* variant with the hash unrolled eight times */
        for (; len >= 8; len -= 8) {
                hash = ((hash << 5) + hash) + *str++;
@@ -383,6 +426,7 @@ static zend_always_inline zend_ulong zend_inline_hash_func(const char *str, size
                case 0: break;
 EMPTY_SWITCH_DEFAULT_CASE()
        }
+#endif
 
        /* Hash value can't be zero, so we always set the high bit */
 #if SIZEOF_ZEND_LONG == 8