diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 3afe2d7539a8010af5f6aea8b4cd95ae2dee7ebb..1671fb88c946559acf6970c30257cd79fbf8a3a7 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -559,8 +559,6 @@ SET(SQL_SHARED_SOURCES uniques.cc xa.cc ssl_acceptor_context.cc - city.cc - hashlib.cc ) # BISON_TARGET( diff --git a/sql/city.cc b/sql/city.cc deleted file mode 100644 index cff07f754cbf3b332ebf21de1266e39362857d8a..0000000000000000000000000000000000000000 --- a/sql/city.cc +++ /dev/null @@ -1,651 +0,0 @@ -// Copyright (c) 2011 Google, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -// CityHash, by Geoff Pike and Jyrki Alakuijala -// -// This file provides CityHash64() and related functions. -// -// It's probably possible to create even faster hash functions by -// writing a program that systematically explores some of the space of -// possible hash functions, by using SIMD instructions, or by -// compromising on hash quality. - -#include "config.h" -#include "city.h" - -#include -#include -using namespace std; - -static uint64 UNALIGNED_LOAD64(const char* p) { - uint64 result; - memcpy(&result, p, sizeof(result)); - return result; -} - -static uint32 UNALIGNED_LOAD32(const char* p) { - uint32 result; - memcpy(&result, p, sizeof(result)); - return result; -} - -#ifdef _MSC_VER - -#include -#define bswap_32(x) _byteswap_ulong(x) -#define bswap_64(x) _byteswap_uint64(x) - -#elif defined(__APPLE__) - -// Mac OS X / Darwin features -#include -#define bswap_32(x) OSSwapInt32(x) -#define bswap_64(x) OSSwapInt64(x) - -#elif defined(__sun) || defined(sun) - -#include -#define bswap_32(x) BSWAP_32(x) -#define bswap_64(x) BSWAP_64(x) - -#elif defined(__FreeBSD__) - -#include -#define bswap_32(x) bswap32(x) -#define bswap_64(x) bswap64(x) - -#elif defined(__OpenBSD__) - -#include -#define bswap_32(x) swap32(x) -#define bswap_64(x) swap64(x) - -#elif defined(__NetBSD__) - -#include -#include -#if defined(__BSWAP_RENAME) && !defined(__bswap_32) -#define bswap_32(x) bswap32(x) -#define bswap_64(x) bswap64(x) -#endif - -#else - -#include - -#endif - -#ifdef WORDS_BIGENDIAN -#define uint32_in_expected_order(x) (bswap_32(x)) -#define uint64_in_expected_order(x) (bswap_64(x)) -#else -#define uint32_in_expected_order(x) (x) -#define uint64_in_expected_order(x) (x) -#endif - -#if !defined(LIKELY) -#if HAVE_BUILTIN_EXPECT -#define LIKELY(x) (__builtin_expect(!!(x), 1)) -#else -#define LIKELY(x) (x) -#endif -#endif - -static uint64 Fetch64(const char* p) { - return uint64_in_expected_order(UNALIGNED_LOAD64(p)); -} - -static uint32 Fetch32(const char* p) { - return uint32_in_expected_order(UNALIGNED_LOAD32(p)); -} - -// Some primes between 2^63 and 2^64 for various uses. -static const uint64 k0 = 0xc3a5c85c97cb3127ULL; -static const uint64 k1 = 0xb492b66fbe98f273ULL; -static const uint64 k2 = 0x9ae16a3b2f90404fULL; - -// Magic numbers for 32-bit hashing. Copied from Murmur3. -static const uint32 c1 = 0xcc9e2d51; -static const uint32 c2 = 0x1b873593; - -// A 32-bit to 32-bit integer hash copied from Murmur3. -static uint32 fmix(uint32 h) -{ - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; - return h; -} - -static uint32 Rotate32(uint32 val, int shift) { - // Avoid shifting by 32: doing so yields an undefined result. - return shift == 0 ? val : ((val >> shift) | (val << (32 - shift))); -} - -#undef PERMUTE3 -#define PERMUTE3(a, b, c) do { std::swap(a, b); std::swap(a, c); } while (0) - -static uint32 Mur(uint32 a, uint32 h) { - // Helper from Murmur3 for combining two 32-bit values. - a *= c1; - a = Rotate32(a, 17); - a *= c2; - h ^= a; - h = Rotate32(h, 19); - return h * 5 + 0xe6546b64; -} - -static uint32 Hash32Len13to24(const char* s, size_t len) { - uint32 a = Fetch32(s - 4 + (len >> 1)); - uint32 b = Fetch32(s + 4); - uint32 c = Fetch32(s + len - 8); - uint32 d = Fetch32(s + (len >> 1)); - uint32 e = Fetch32(s); - uint32 f = Fetch32(s + len - 4); - uint32 h = static_cast(len); - - return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h))))))); -} - -static uint32 Hash32Len0to4(const char* s, size_t len) { - uint32 b = 0; - uint32 c = 9; - for (size_t i = 0; i < len; i++) { - signed char v = static_cast(s[i]); - b = b * c1 + static_cast(v); - c ^= b; - } - return fmix(Mur(b, Mur(static_cast(len), c))); -} - -static uint32 Hash32Len5to12(const char* s, size_t len) { - uint32 a = static_cast(len), b = a * 5, c = 9, d = b; - a += Fetch32(s); - b += Fetch32(s + len - 4); - c += Fetch32(s + ((len >> 1) & 4)); - return fmix(Mur(c, Mur(b, Mur(a, d)))); -} - -uint32 CityHash32(const char* s, size_t len) { - if (len <= 24) { - return len <= 12 ? - (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) : - Hash32Len13to24(s, len); - } - - // len > 24 - uint32 h = static_cast(len), g = c1 * h, f = g; - uint32 a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2; - uint32 a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2; - uint32 a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2; - uint32 a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2; - uint32 a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2; - h ^= a0; - h = Rotate32(h, 19); - h = h * 5 + 0xe6546b64; - h ^= a2; - h = Rotate32(h, 19); - h = h * 5 + 0xe6546b64; - g ^= a1; - g = Rotate32(g, 19); - g = g * 5 + 0xe6546b64; - g ^= a3; - g = Rotate32(g, 19); - g = g * 5 + 0xe6546b64; - f += a4; - f = Rotate32(f, 19); - f = f * 5 + 0xe6546b64; - size_t iters = (len - 1) / 20; - do { - uint32 a0 = Rotate32(Fetch32(s) * c1, 17) * c2; - uint32 a1 = Fetch32(s + 4); - uint32 a2 = Rotate32(Fetch32(s + 8) * c1, 17) * c2; - uint32 a3 = Rotate32(Fetch32(s + 12) * c1, 17) * c2; - uint32 a4 = Fetch32(s + 16); - h ^= a0; - h = Rotate32(h, 18); - h = h * 5 + 0xe6546b64; - f += a1; - f = Rotate32(f, 19); - f = f * c1; - g += a2; - g = Rotate32(g, 18); - g = g * 5 + 0xe6546b64; - h ^= a3 + a1; - h = Rotate32(h, 19); - h = h * 5 + 0xe6546b64; - g ^= a4; - g = bswap_32(g) * 5; - h += a4 * 5; - h = bswap_32(h); - f += a0; - PERMUTE3(f, h, g); - s += 20; - } while (--iters != 0); - g = Rotate32(g, 11) * c1; - g = Rotate32(g, 17) * c1; - f = Rotate32(f, 11) * c1; - f = Rotate32(f, 17) * c1; - h = Rotate32(h + g, 19); - h = h * 5 + 0xe6546b64; - h = Rotate32(h, 17) * c1; - h = Rotate32(h + f, 19); - h = h * 5 + 0xe6546b64; - h = Rotate32(h, 17) * c1; - return h; -} - -// Bitwise right rotate. Normally this will compile to a single -// instruction, especially if the shift is a manifest constant. -static uint64 Rotate(uint64 val, int shift) { - // Avoid shifting by 64: doing so yields an undefined result. - return shift == 0 ? val : ((val >> shift) | (val << (64 - shift))); -} - -static uint64 ShiftMix(uint64 val) { - return val ^ (val >> 47); -} - -static uint64 HashLen16(uint64 u, uint64 v) { - return Hash128to64(uint128(u, v)); -} - -static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) { - // Murmur-inspired hashing. - uint64 a = (u ^ v) * mul; - a ^= (a >> 47); - uint64 b = (v ^ a) * mul; - b ^= (b >> 47); - b *= mul; - return b; -} - -static uint64 HashLen0to16(const char* s, size_t len) { - if (len >= 8) { - uint64 mul = k2 + len * 2; - uint64 a = Fetch64(s) + k2; - uint64 b = Fetch64(s + len - 8); - uint64 c = Rotate(b, 37) * mul + a; - uint64 d = (Rotate(a, 25) + b) * mul; - return HashLen16(c, d, mul); - } - if (len >= 4) { - uint64 mul = k2 + len * 2; - uint64 a = Fetch32(s); - return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul); - } - if (len > 0) { - uint8 a = static_cast(s[0]); - uint8 b = static_cast(s[len >> 1]); - uint8 c = static_cast(s[len - 1]); - uint32 y = static_cast(a) + (static_cast(b) << 8); - uint32 z = static_cast(len) + (static_cast(c) << 2); - return ShiftMix(y * k2 ^ z * k0) * k2; - } - return k2; -} - -// This probably works well for 16-byte strings as well, but it may be overkill -// in that case. -static uint64 HashLen17to32(const char* s, size_t len) { - uint64 mul = k2 + len * 2; - uint64 a = Fetch64(s) * k1; - uint64 b = Fetch64(s + 8); - uint64 c = Fetch64(s + len - 8) * mul; - uint64 d = Fetch64(s + len - 16) * k2; - return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, - a + Rotate(b + k2, 18) + c, mul); -} - -// Return a 16-byte hash for 48 bytes. Quick and dirty. -// Callers do best to use "random-looking" values for a and b. -static pair WeakHashLen32WithSeeds( - uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, uint64 b) { - a += w; - b = Rotate(b + a + z, 21); - uint64 c = a; - a += x; - a += y; - b += Rotate(a, 44); - return make_pair(a + z, b + c); -} - -// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. -static pair WeakHashLen32WithSeeds( - const char* s, uint64 a, uint64 b) { - return WeakHashLen32WithSeeds(Fetch64(s), - Fetch64(s + 8), - Fetch64(s + 16), - Fetch64(s + 24), - a, - b); -} - -// Return an 8-byte hash for 33 to 64 bytes. -static uint64 HashLen33to64(const char* s, size_t len) { - uint64 mul = k2 + len * 2; - uint64 a = Fetch64(s) * k2; - uint64 b = Fetch64(s + 8); - uint64 c = Fetch64(s + len - 24); - uint64 d = Fetch64(s + len - 32); - uint64 e = Fetch64(s + 16) * k2; - uint64 f = Fetch64(s + 24) * 9; - uint64 g = Fetch64(s + len - 8); - uint64 h = Fetch64(s + len - 16) * mul; - uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9; - uint64 v = ((a + g) ^ d) + f + 1; - uint64 w = bswap_64((u + v) * mul) + h; - uint64 x = Rotate(e + f, 42) + c; - uint64 y = (bswap_64((v + w) * mul) + g) * mul; - uint64 z = e + f + c; - a = bswap_64((x + z) * mul + y) + b; - b = ShiftMix((z + a) * mul + d + h) * mul; - return b + x; -} - -uint64 CityHash64(const char* s, size_t len) { - if (len <= 32) { - if (len <= 16) { - return HashLen0to16(s, len); - } - else { - return HashLen17to32(s, len); - } - } - else if (len <= 64) { - return HashLen33to64(s, len); - } - - // For strings over 64 bytes we hash the end first, and then as we - // loop we keep 56 bytes of state: v, w, x, y, and z. - uint64 x = Fetch64(s + len - 40); - uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56); - uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24)); - pair v = WeakHashLen32WithSeeds(s + len - 64, len, z); - pair w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x); - x = x * k1 + Fetch64(s); - - // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. - len = (len - 1) & ~static_cast(63); - do { - x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; - y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; - x ^= w.second; - y += v.first + Fetch64(s + 40); - z = Rotate(z + w.first, 33) * k1; - v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); - w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); - std::swap(z, x); - s += 64; - len -= 64; - } while (len != 0); - return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z, - HashLen16(v.second, w.second) + x); -} - -uint64 CityHash64WithSeed(const char* s, size_t len, uint64 seed) { - return CityHash64WithSeeds(s, len, k2, seed); -} - -uint64 CityHash64WithSeeds(const char* s, size_t len, - uint64 seed0, uint64 seed1) { - return HashLen16(CityHash64(s, len) - seed0, seed1); -} - -// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings -// of any length representable in signed long. Based on City and Murmur. -static uint128 CityMurmur(const char* s, size_t len, uint128 seed) { - uint64 a = Uint128Low64(seed); - uint64 b = Uint128High64(seed); - uint64 c = 0; - uint64 d = 0; - if (len <= 16) { - a = ShiftMix(a * k1) * k1; - c = b * k1 + HashLen0to16(s, len); - d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c)); - } - else { - c = HashLen16(Fetch64(s + len - 8) + k1, a); - d = HashLen16(b + len, c + Fetch64(s + len - 16)); - a += d; - // len > 16 here, so do...while is safe - do { - a ^= ShiftMix(Fetch64(s) * k1) * k1; - a *= k1; - b ^= a; - c ^= ShiftMix(Fetch64(s + 8) * k1) * k1; - c *= k1; - d ^= c; - s += 16; - len -= 16; - } while (len > 16); - } - a = HashLen16(a, c); - b = HashLen16(d, b); - return uint128(a ^ b, HashLen16(b, a)); -} - -uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) { - if (len < 128) { - return CityMurmur(s, len, seed); - } - - // We expect len >= 128 to be the common case. Keep 56 bytes of state: - // v, w, x, y, and z. - pair v, w; - uint64 x = Uint128Low64(seed); - uint64 y = Uint128High64(seed); - uint64 z = len * k1; - v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s); - v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8); - w.first = Rotate(y + z, 35) * k1 + x; - w.second = Rotate(x + Fetch64(s + 88), 53) * k1; - - // This is the same inner loop as CityHash64(), manually unrolled. - do { - x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; - y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; - x ^= w.second; - y += v.first + Fetch64(s + 40); - z = Rotate(z + w.first, 33) * k1; - v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); - w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); - std::swap(z, x); - s += 64; - x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; - y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; - x ^= w.second; - y += v.first + Fetch64(s + 40); - z = Rotate(z + w.first, 33) * k1; - v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); - w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); - std::swap(z, x); - s += 64; - len -= 128; - } while (LIKELY(len >= 128)); - x += Rotate(v.first + z, 49) * k0; - y = y * k0 + Rotate(w.second, 37); - z = z * k0 + Rotate(w.first, 27); - w.first *= 9; - v.first *= k0; - // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. - for (size_t tail_done = 0; tail_done < len; ) { - tail_done += 32; - y = Rotate(x + y, 42) * k0 + v.second; - w.first += Fetch64(s + len - tail_done + 16); - x = x * k0 + w.first; - z += w.second + Fetch64(s + len - tail_done); - w.second += v.first; - v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second); - v.first *= k0; - } - // At this point our 56 bytes of state should contain more than - // enough information for a strong 128-bit hash. We use two - // different 56-byte-to-8-byte hashes to get a 16-byte final result. - x = HashLen16(x, v.first); - y = HashLen16(y + z, w.first); - return uint128(HashLen16(x + v.second, w.second) + y, - HashLen16(x + w.second, y + v.second)); -} - -uint128 CityHash128(const char* s, size_t len) { - return len >= 16 ? - CityHash128WithSeed(s + 16, len - 16, - uint128(Fetch64(s), Fetch64(s + 8) + k0)) : - CityHash128WithSeed(s, len, uint128(k0, k1)); -} - -#ifdef __SSE4_2__ -#include -#include - -// Requires len >= 240. -static void CityHashCrc256Long(const char* s, size_t len, - uint32 seed, uint64* result) { - uint64 a = Fetch64(s + 56) + k0; - uint64 b = Fetch64(s + 96) + k0; - uint64 c = result[0] = HashLen16(b, len); - uint64 d = result[1] = Fetch64(s + 120) * k0 + len; - uint64 e = Fetch64(s + 184) + seed; - uint64 f = 0; - uint64 g = 0; - uint64 h = c + d; - uint64 x = seed; - uint64 y = 0; - uint64 z = 0; - - // 240 bytes of input per iter. - size_t iters = len / 240; - len -= iters * 240; - do { -#undef CHUNK -#define CHUNK(r) \ - PERMUTE3(x, z, y); \ - b += Fetch64(s); \ - c += Fetch64(s + 8); \ - d += Fetch64(s + 16); \ - e += Fetch64(s + 24); \ - f += Fetch64(s + 32); \ - a += b; \ - h += f; \ - b += c; \ - f += d; \ - g += e; \ - e += z; \ - g += x; \ - z = _mm_crc32_u64(z, b + g); \ - y = _mm_crc32_u64(y, e + h); \ - x = _mm_crc32_u64(x, f + a); \ - e = Rotate(e, r); \ - c += e; \ - s += 40 - - CHUNK(0); PERMUTE3(a, h, c); - CHUNK(33); PERMUTE3(a, h, f); - CHUNK(0); PERMUTE3(b, h, f); - CHUNK(42); PERMUTE3(b, h, d); - CHUNK(0); PERMUTE3(b, h, e); - CHUNK(33); PERMUTE3(a, h, e); - } while (--iters > 0); - - while (len >= 40) { - CHUNK(29); - e ^= Rotate(a, 20); - h += Rotate(b, 30); - g ^= Rotate(c, 40); - f += Rotate(d, 34); - PERMUTE3(c, h, g); - len -= 40; - } - if (len > 0) { - s = s + len - 40; - CHUNK(33); - e ^= Rotate(a, 43); - h += Rotate(b, 42); - g ^= Rotate(c, 41); - f += Rotate(d, 40); - } - result[0] ^= h; - result[1] ^= g; - g += h; - a = HashLen16(a, g + z); - x += y << 32; - b += x; - c = HashLen16(c, z) + h; - d = HashLen16(d, e + result[0]); - g += e; - h += HashLen16(x, f); - e = HashLen16(a, d) + g; - z = HashLen16(b, c) + a; - y = HashLen16(g, h) + c; - result[0] = e + z + y + x; - a = ShiftMix((a + y) * k0) * k0 + b; - result[1] += a + result[0]; - a = ShiftMix(a * k0) * k0 + c; - result[2] = a + result[1]; - a = ShiftMix((a + e) * k0) * k0; - result[3] = a + result[2]; -} - -// Requires len < 240. -static void CityHashCrc256Short(const char* s, size_t len, uint64* result) { - char buf[240]; - memcpy(buf, s, len); - memset(buf + len, 0, 240 - len); - CityHashCrc256Long(buf, 240, ~static_cast(len), result); -} - -void CityHashCrc256(const char* s, size_t len, uint64* result) { - if (LIKELY(len >= 240)) { - CityHashCrc256Long(s, len, 0, result); - } - else { - CityHashCrc256Short(s, len, result); - } -} - -uint128 CityHashCrc128WithSeed(const char* s, size_t len, uint128 seed) { - if (len <= 900) { - return CityHash128WithSeed(s, len, seed); - } - else { - uint64 result[4]; - CityHashCrc256(s, len, result); - uint64 u = Uint128High64(seed) + result[0]; - uint64 v = Uint128Low64(seed) + result[1]; - return uint128(HashLen16(u, v + result[2]), - HashLen16(Rotate(v, 32), u * k0 + result[3])); - } -} - -uint128 CityHashCrc128(const char* s, size_t len) { - if (len <= 900) { - return CityHash128(s, len); - } - else { - uint64 result[4]; - CityHashCrc256(s, len, result); - return uint128(result[2], result[3]); - } -} - -#endif diff --git a/sql/city.h b/sql/city.h deleted file mode 100644 index 28962d52dacc9e34512b755020c21bfddf62d762..0000000000000000000000000000000000000000 --- a/sql/city.h +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once -// Copyright (c) 2011 Google, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -// CityHash, by Geoff Pike and Jyrki Alakuijala -// -// http://code.google.com/p/cityhash/ -// -// This file provides a few functions for hashing strings. All of them are -// high-quality functions in the sense that they pass standard tests such -// as Austin Appleby's SMHasher. They are also fast. -// -// For 64-bit x86 code, on short strings, we don't know of anything faster than -// CityHash64 that is of comparable quality. We believe our nearest competitor -// is Murmur3. For 64-bit x86 code, CityHash64 is an excellent choice for hash -// tables and most other hashing (excluding cryptography). -// -// For 64-bit x86 code, on long strings, the picture is more complicated. -// On many recent Intel CPUs, such as Nehalem, Westmere, Sandy Bridge, etc., -// CityHashCrc128 appears to be faster than all competitors of comparable -// quality. CityHash128 is also good but not quite as fast. We believe our -// nearest competitor is Bob Jenkins' Spooky. We don't have great data for -// other 64-bit CPUs, but for long strings we know that Spooky is slightly -// faster than CityHash on some relatively recent AMD x86-64 CPUs, for example. -// Note that CityHashCrc128 is declared in citycrc.h. -// -// For 32-bit x86 code, we don't know of anything faster than CityHash32 that -// is of comparable quality. We believe our nearest competitor is Murmur3A. -// (On 64-bit CPUs, it is typically faster to use the other CityHash variants.) -// -// Functions in the CityHash family are not suitable for cryptography. -// -// Please see CityHash's README file for more details on our performance -// measurements and so on. -// -// WARNING: This code has been only lightly tested on big-endian platforms! -// It is known to work well on little-endian platforms that have a small penalty -// for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs. -// It should work on all 32-bit and 64-bit platforms that allow unaligned reads; -// bug reports are welcome. -// -// By the way, for some hash functions, given strings a and b, the hash -// of a+b is easily derived from the hashes of a and b. This property -// doesn't hold for any hash functions in this file. - -#ifndef CITY_HASH_H_ -#define CITY_HASH_H_ - -#include // for size_t. -#include -#include - -typedef uint8_t uint8; -typedef uint32_t uint32; -typedef uint64_t uint64; -typedef std::pair uint128; - -inline uint64 Uint128Low64(const uint128& x) { return x.first; }//128位取低位 -inline uint64 Uint128High64(const uint128& x) { return x.second; } - -// Hash function for a byte array. -uint64 CityHash64(const char* buf, size_t len); - -// Hash function for a byte array. For convenience, a 64-bit seed is also -// hashed into the result. -uint64 CityHash64WithSeed(const char* buf, size_t len, uint64 seed); - -// Hash function for a byte array. For convenience, two seeds are also -// hashed into the result. -uint64 CityHash64WithSeeds(const char* buf, size_t len, - uint64 seed0, uint64 seed1); - -// Hash function for a byte array. -uint128 CityHash128(const char* s, size_t len); - -// Hash function for a byte array. For convenience, a 128-bit seed is also -// hashed into the result. -uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed); - -// Hash function for a byte array. Most useful in 32-bit binaries. -uint32 CityHash32(const char* buf, size_t len); - -// Hash 128 input bits down to 64 bits of output. -// This is intended to be a reasonably good hash function. -inline uint64 Hash128to64(const uint128& x) { - // Murmur-inspired hashing. - const uint64 kMul = 0x9ddfea08eb382d69ULL; - uint64 a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; - a ^= (a >> 47); - uint64 b = (Uint128High64(x) ^ a) * kMul; - b ^= (b >> 47); - b *= kMul; - return b; -} - -#endif // CITY_HASH_H_ diff --git a/sql/hash_join_buffer.cc b/sql/hash_join_buffer.cc index 24bcb3b44f110a3fbfe06b73224667d285aee82f..e207a2c0a72f0d0b2561a1311d92af3e334b3cd2 100644 --- a/sql/hash_join_buffer.cc +++ b/sql/hash_join_buffer.cc @@ -446,7 +446,9 @@ StoreRowResult HashJoinRowBuffer::StoreRow( m_last_row_stored = m_hash_map->emplace(key, BufferRow(row, row_size)); - if (m_mem_root.allocated_size() > m_max_mem_available) { + size_t max_mem_available = get_max_mem(); + + if (m_mem_root.allocated_size() > max_mem_available) { return StoreRowResult::BUFFER_FULL; } return StoreRowResult::ROW_STORED; @@ -454,7 +456,7 @@ StoreRowResult HashJoinRowBuffer::StoreRow( bool HashJoinRowBuffer::bloom_filter_init(size_t bloom_size) { if (bloom_size > m_bloom_max_mem_available) { - bloom_size = m_bloom_max_mem_available; + bloom_size = m_bloom_max_mem_available; } size_t bloom_size_pow2 = 1; diff --git a/sql/hash_join_buffer.h b/sql/hash_join_buffer.h index 09c1dff84237ec53e56791b15c258105d39d42b1..3d1457c7dedb0dfb5b0bcfc67ffcfdd5f3acfcb0 100644 --- a/sql/hash_join_buffer.h +++ b/sql/hash_join_buffer.h @@ -68,7 +68,7 @@ #include #include -// #include "extra/lz4/my_xxhash.h" +#include "extra/lz4/my_xxhash.h" #include "field_types.h" #include "map_helpers.h" #include "my_alloc.h" @@ -79,7 +79,6 @@ #include "sql/table.h" #include "sql_string.h" #include "bloom_helpers.h" -#include "hashlib.h" class Field; @@ -238,9 +237,7 @@ class KeyHasher { explicit KeyHasher(uint32_t seed) : m_seed(seed) {} size_t operator()(hash_join_buffer::Key key) const { - // return MY_XXH64(key.data(), key.size(), m_seed); - int chosen=0; - return selectHashFunction(key.data(), key.size(), m_seed,chosen); + return MY_XXH64(key.data(), key.size(), m_seed); } private: @@ -337,7 +334,12 @@ class HashJoinRowBuffer { bool contains(const Key &key) const { return find(key) != end(); } - size_t get_max_mem() const {return m_max_mem_available;} + size_t get_max_mem() const { + if(!need_bloom()) { + return m_bloom_max_mem_available + m_max_mem_available; + } + return m_max_mem_available; + } private: const std::vector m_join_conditions; @@ -384,9 +386,9 @@ class HashJoinRowBuffer { size_t bloom_filter_hash(const Key &key) { return m_bloom_filter_hasher(key); } size_t bloom_filter_get_size() const { return m_bloom_filter->getSize(); } size_t bloom_filter_get_max_mem() const { return m_bloom_max_mem_available; } - HashStore *get_hash_store() { return m_hash_store.get(); } - bool has_dominant_hash() { return m_hash_store->HasDominantHash(); } - bool need_bloom() { return m_need_bloom; } + HashStore *get_hash_store() const { return m_hash_store.get(); } + bool has_dominant_hash() const { return m_hash_store->HasDominantHash(); } + bool need_bloom() const { return m_need_bloom; } void set_need_bloom(bool need_bloom) { m_need_bloom = need_bloom; } }; diff --git a/sql/hash_join_iterator.cc b/sql/hash_join_iterator.cc index 7ab93c27c15535a190ca9befa8c56b340fc4de9a..e22d629713ab4f8ab603010162e79aa9c70e55d4 100644 --- a/sql/hash_join_iterator.cc +++ b/sql/hash_join_iterator.cc @@ -29,7 +29,7 @@ #include #include -// #include "extra/lz4/my_xxhash.h" +#include "extra/lz4/my_xxhash.h" #include "field_types.h" #include "math.h" #include "my_alloc.h" @@ -53,7 +53,6 @@ #include "sql/table.h" #include "sql/basic_row_iterators.h" #include "tables_contained_in.h" -#include "hashlib.h" constexpr size_t HashJoinIterator::kMaxChunks; @@ -331,11 +330,8 @@ bool HashJoinIterator::WriteRowToChunk( m_row_buffer.get_hash_store()->SaveKey(bloom_key_hash); } - // Choose the hash function. - int chosen=0; - const uint64_t join_key_hash = - selectHashFunction(key, - len, xxhash_seed, chosen); + const uint64_t join_key_hash = MY_XXH64(key, len, xxhash_seed); + DBUG_ASSERT((chunks->size() & (chunks->size() - 1)) == 0); // Since we know that the number of chunks will be a power of two, do a // bitwise AND instead of (join_key_hash % chunks->size()). @@ -971,64 +967,44 @@ void HashJoinIterator::LookupProbeRowInHashTable() { pointer_cast(m_temporary_row_and_join_key_buffer.ptr()), m_temporary_row_and_join_key_buffer.length()); - - if (m_row_buffer.need_bloom()) { - if (!(m_row_buffer.bloom_filter_test( - m_row_buffer.bloom_filter_hash(key)))) { - if ((m_join_type == JoinType::SEMI || m_join_type == JoinType::ANTI) && - m_extra_condition == nullptr) { - // find() has a better average complexity than equal_range() (constant - // vs. linear in the number of matching elements). And for semijoins, we - // are only interested in the first match anyways, so this may give a - // nice speedup. An exception to this is if we have any "extra" - // conditions that needs to be evaluated after the hash table lookup, - // but before the row is returned; we may need to read through the - // entire hash table to find a row that satisfies the extra - // condition(s). - m_hash_map_iterator = m_row_buffer.find(key); - m_hash_map_end = m_row_buffer.end(); - } else { - auto range = m_row_buffer.equal_range(key); - m_hash_map_iterator = range.first; - m_hash_map_end = range.second; + if (m_row_buffer.need_bloom() && + m_row_buffer.bloom_filter_test(m_row_buffer.bloom_filter_hash(key))) { + m_filtered_by_bloom = true; + if (m_join_type == JoinType::ANTI || m_join_type == JoinType::OUTER) { + m_hash_map_iterator = m_row_buffer.end(); + m_hash_map_end = m_row_buffer.end(); + m_state = State::READING_FIRST_ROW_FROM_HASH_TABLE; + if(m_write_to_probe_row_saving) { + // If some time we enable bloom filter while processing chunk files, + // we get here only if there are still rows to be read from the build + // chunk. Therefore, we cannot set m_filtered_by_bloom to true as a + // null-complemented row will be returned soon after we leave + // LookupProbeRowInHashTable() and enter ReadNextJoinedRowFromHashTable(). + m_filtered_by_bloom = false; } } else { - m_filtered_by_bloom = true; - if (m_join_type == JoinType::ANTI || m_join_type == JoinType::OUTER) { - m_hash_map_iterator = m_row_buffer.end(); - m_hash_map_end = m_row_buffer.end(); - m_state = State::READING_FIRST_ROW_FROM_HASH_TABLE; - if(m_write_to_probe_row_saving) { - // If some time we enable bloom filter while processing chunk files, - // we get here only if there are still rows to be read from the build - // chunk. Therefore, we cannot set m_filtered_by_bloom to true as a - // null-complemented row will be returned soon after we leave - // LookupProbeRowInHashTable() and enter ReadNextJoinedRowFromHashTable(). - m_filtered_by_bloom = false; - } - } else { - SetReadingProbeRowState(); - } - return; + SetReadingProbeRowState(); } + return; + } + + + if ((m_join_type == JoinType::SEMI || m_join_type == JoinType::ANTI) && + m_extra_condition == nullptr) { + // find() has a better average complexity than equal_range() (constant + // vs. linear in the number of matching elements). And for semijoins, we + // are only interested in the first match anyways, so this may give a + // nice speedup. An exception to this is if we have any "extra" + // conditions that needs to be evaluated after the hash table lookup, + // but before the row is returned; we may need to read through the + // entire hash table to find a row that satisfies the extra + // condition(s). + m_hash_map_iterator = m_row_buffer.find(key); + m_hash_map_end = m_row_buffer.end(); } else { - if ((m_join_type == JoinType::SEMI || m_join_type == JoinType::ANTI) && - m_extra_condition == nullptr) { - // find() has a better average complexity than equal_range() (constant - // vs. linear in the number of matching elements). And for semijoins, we - // are only interested in the first match anyways, so this may give a - // nice speedup. An exception to this is if we have any "extra" - // conditions that needs to be evaluated after the hash table lookup, - // but before the row is returned; we may need to read through the - // entire hash table to find a row that satisfies the extra - // condition(s). - m_hash_map_iterator = m_row_buffer.find(key); - m_hash_map_end = m_row_buffer.end(); - } else { - auto range = m_row_buffer.equal_range(key); - m_hash_map_iterator = range.first; - m_hash_map_end = range.second; - } + auto range = m_row_buffer.equal_range(key); + m_hash_map_iterator = range.first; + m_hash_map_end = range.second; } m_state = State::READING_FIRST_ROW_FROM_HASH_TABLE; @@ -1440,10 +1416,16 @@ static bool NeedBloom(THD *thd, const unique_ptr_destroy_only &buil bool probe_is_table_scan_iter = typeid(*probe_input.get()) == typeid(TableScanIterator); bool build_input_too_big = false; + double n_build_rows = build_input->expected_rows(); + double n_probe_rows = probe_input->expected_rows(); + if ((build_is_table_scan_iter && probe_is_table_scan_iter) || is_recursive_join) { - build_input_too_big = build_input->expected_rows() > probe_input->expected_rows() * 4; + // Do not use bloom filter if the build input has much more rows than the probe input + build_input_too_big |= n_build_rows > n_probe_rows * 4; + // or if there is probably not enough space for the bloom filter + build_input_too_big |= n_build_rows > max_bloom_size * 8192; } else if(build_is_table_scan_iter) { - build_input_too_big = build_input->expected_rows() > probe_input->expected_rows(); + build_input_too_big |= n_build_rows > max_bloom_size * 8192; } bool no_condition = m_join_conditions.size() == 0; @@ -1519,20 +1501,17 @@ static bool ShouldStartRecursiveJoin( if(max_bloom_size == 0) return false; if(m_has_dominant_hash) return false; + // I/O cost of non-recursive join and recursive join, assuming evenly + // distributed hash values of joinkeys. double alpha = num_row_build_chunk / num_read; double cost_direct = num_row_build_chunk + ceil(alpha) * num_row_probe_chunk; double cost_recursive = - 3 * num_row_build_chunk + - (2 + ceil(alpha / max_bucket_count)) * num_row_probe_chunk; - - // cost for probe row saving file + 3 * num_row_build_chunk + (2 + ceil((alpha - 1) / max_bucket_count)) * num_row_probe_chunk - num_read; + + // I/O cost of probe row saving file if (!is_inner_join) { - cost_direct += ceil(alpha - 1) * num_row_probe_chunk / 2; - cost_recursive += ceil(alpha / max_bucket_count) * num_row_probe_chunk / 2; + cost_direct += ceil(alpha) * num_row_probe_chunk; + cost_recursive += (ceil((alpha - 1) / max_bucket_count) + 1) * num_row_probe_chunk; } - - // At the point ShouldStartRecursiveJoin() is called, we have read some of the - // build chunk. So that should also be counted in. - cost_recursive += num_read; return cost_direct > cost_recursive * 1.05; } \ No newline at end of file diff --git a/sql/hashlib.cc b/sql/hashlib.cc deleted file mode 100644 index 701493dc018e315fae471fa154dc3380de36ae6a..0000000000000000000000000000000000000000 --- a/sql/hashlib.cc +++ /dev/null @@ -1,13 +0,0 @@ -#include "hashlib.h" - -size_t selectHashFunction( - const void *keyValue, - size_t length, - unsigned long long seed, - int chosen) { - if (chosen == 1){ - const char *buf = reinterpret_cast(keyValue); - return CityHash64WithSeed(buf, length, seed); - } - return MY_XXH64(keyValue, length, seed); -} \ No newline at end of file diff --git a/sql/hashlib.h b/sql/hashlib.h deleted file mode 100644 index bda67b89be3cd21b7b1fc59e482b56345ea03f69..0000000000000000000000000000000000000000 --- a/sql/hashlib.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#ifndef HASHLIB_INCLUDED -#define HASHLIB_INCLUDED - -#include "city.h" -#include "extra/lz4/my_xxhash.h" -#include - -size_t selectHashFunction( - const void *keyValue, - size_t length, - unsigned long long seed, - int chosen); - -#endif \ No newline at end of file