Hash.h 10.4 KB
Newer Older
1 2
#pragma once

A
Artem Zuikov 已提交
3
#include <common/types.h>
4
#include <Common/UInt128.h>
N
Nikolai Kochetov 已提交
5
#include <common/unaligned.h>
6

A
Alexey Milovidov 已提交
7 8
#include <type_traits>

9

F
f1yegor 已提交
10
/** Hash functions that are better than the trivial function std::hash.
11
  *
12
  * Example: when we do aggregation by the visitor ID, the performance increase is more than 5 times.
13 14 15 16 17
  * This is because of following reasons:
  * - in Yandex, visitor identifier is an integer that has timestamp with seconds resolution in lower bits;
  * - in typical implementation of standard library, hash function for integers is trivial and just use lower bits;
  * - traffic is non-uniformly distributed across a day;
  * - we are using open-addressing linear probing hash tables that are most critical to hash function quality,
M
maiha 已提交
18
  *   and trivial hash function gives disastrous results.
19
  */
20

21
/** Taken from MurmurHash. This is Murmur finalizer.
F
f1yegor 已提交
22
  * Faster than intHash32 when inserting into the hash table UInt64 -> UInt64, where the key is the visitor ID.
23 24 25
  */
inline DB::UInt64 intHash64(DB::UInt64 x)
{
26 27 28 29 30
    x ^= x >> 33;
    x *= 0xff51afd7ed558ccdULL;
    x ^= x >> 33;
    x *= 0xc4ceb9fe1a85ec53ULL;
    x ^= x >> 33;
31

32
    return x;
33 34
}

F
f1yegor 已提交
35
/** CRC32C is not very high-quality as a hash function,
36
  *  according to avalanche and bit independence tests (see SMHasher software), as well as a small number of bits,
F
f1yegor 已提交
37 38 39
  *  but can behave well when used in hash tables,
  *  due to high speed (latency 3 + 1 clock cycle, throughput 1 clock cycle).
  * Works only with SSE 4.2 support.
40
  */
41
#ifdef __SSE4_2__
42 43 44
#include <nmmintrin.h>
#endif

45
#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32)
46 47 48 49
#include <arm_acle.h>
#include <arm_neon.h>
#endif

50 51
inline DB::UInt64 intHashCRC32(DB::UInt64 x)
{
52
#ifdef __SSE4_2__
53
    return _mm_crc32_u64(-1ULL, x);
54
#elif defined(__aarch64__) && defined(__ARM_FEATURE_CRC32)
P
proller 已提交
55
    return __crc32cd(-1U, x);
56
#else
57
    /// On other platforms we do not have CRC32. NOTE This can be confusing.
58
    return intHash64(x);
59
#endif
60 61
}

62 63 64 65 66 67 68 69 70 71 72 73
inline DB::UInt64 intHashCRC32(DB::UInt64 x, DB::UInt64 updated_value)
{
#ifdef __SSE4_2__
    return _mm_crc32_u64(updated_value, x);
#elif defined(__aarch64__) && defined(__ARM_FEATURE_CRC32)
    return  __crc32cd(updated_value, x);
#else
    /// On other platforms we do not have CRC32. NOTE This can be confusing.
    return intHash64(x) ^ updated_value;
#endif
}

N
Nikolai Kochetov 已提交
74
template <typename T>
A
Alexey Milovidov 已提交
75
inline typename std::enable_if<(sizeof(T) > sizeof(DB::UInt64)), DB::UInt64>::type
N
Nikolai Kochetov 已提交
76 77
intHashCRC32(const T & x, DB::UInt64 updated_value)
{
N
Nikolai Kochetov 已提交
78
    auto * begin = reinterpret_cast<const char *>(&x);
N
Nikolai Kochetov 已提交
79 80
    for (size_t i = 0; i < sizeof(T); i += sizeof(UInt64))
    {
N
Nikolai Kochetov 已提交
81 82
        updated_value = intHashCRC32(unalignedLoad<DB::UInt64>(begin), updated_value);
        begin += sizeof(DB::UInt64);
N
Nikolai Kochetov 已提交
83 84 85 86 87
    }

    return updated_value;
}

88

89
inline UInt32 updateWeakHash32(const DB::UInt8 * pos, size_t size, DB::UInt32 updated_value)
N
Nikolai Kochetov 已提交
90 91 92
{
    if (size < 8)
    {
N
Nikolai Kochetov 已提交
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
        DB::UInt64 value = 0;
        auto * value_ptr = reinterpret_cast<unsigned char *>(&value);

        typedef __attribute__((__aligned__(1))) uint16_t uint16_unaligned_t;
        typedef __attribute__((__aligned__(1))) uint32_t uint32_unaligned_t;

        /// Adopted code from FastMemcpy.h (memcpy_tiny)
        switch (size)
        {
            case 0:
                break;
            case 1:
                value_ptr[0] = pos[0];
                break;
            case 2:
                *reinterpret_cast<uint16_t *>(value_ptr) = *reinterpret_cast<const uint16_unaligned_t *>(pos);
                break;
N
Nikolai Kochetov 已提交
110 111 112 113
            case 3:
                *reinterpret_cast<uint16_t *>(value_ptr) = *reinterpret_cast<const uint16_unaligned_t *>(pos);
                value_ptr[2] = pos[2];
                break;
N
Nikolai Kochetov 已提交
114 115 116
            case 4:
                *reinterpret_cast<uint32_t *>(value_ptr) = *reinterpret_cast<const uint32_unaligned_t *>(pos);
                break;
N
Nikolai Kochetov 已提交
117 118 119 120
            case 5:
                *reinterpret_cast<uint32_t *>(value_ptr) = *reinterpret_cast<const uint32_unaligned_t *>(pos);
                value_ptr[4] = pos[4];
                break;
N
Nikolai Kochetov 已提交
121 122
            case 6:
                *reinterpret_cast<uint32_t *>(value_ptr) = *reinterpret_cast<const uint32_unaligned_t *>(pos);
N
Nikolai Kochetov 已提交
123 124
                *reinterpret_cast<uint16_unaligned_t *>(value_ptr + 4) =
                        *reinterpret_cast<const uint16_unaligned_t *>(pos + 4);
N
Nikolai Kochetov 已提交
125 126 127 128 129 130 131 132 133 134
                break;
            case 7:
                *reinterpret_cast<uint32_t *>(value_ptr) = *reinterpret_cast<const uint32_unaligned_t *>(pos);
                *reinterpret_cast<uint32_unaligned_t *>(value_ptr + 3) =
                        *reinterpret_cast<const uint32_unaligned_t *>(pos + 3);
                break;
            default:
                __builtin_unreachable();
        }

N
Nikolai Kochetov 已提交
135
        value_ptr[7] = size;
N
Nikolai Kochetov 已提交
136 137 138 139
        return intHashCRC32(value, updated_value);
    }

    const auto * end = pos + size;
N
Nikolai Kochetov 已提交
140
    while (pos + 8 <= end)
N
Nikolai Kochetov 已提交
141 142 143 144 145 146 147
    {
        auto word = unalignedLoad<UInt64>(pos);
        updated_value = intHashCRC32(word, updated_value);

        pos += 8;
    }

N
Nikolai Kochetov 已提交
148 149
    if (pos < end)
    {
N
Nikolai Kochetov 已提交
150 151
        /// If string size is not divisible by 8.
        /// Lets' assume the string was 'abcdefghXYZ', so it's tail is 'XYZ'.
N
Nikolai Kochetov 已提交
152
        DB::UInt8 tail_size = end - pos;
N
Nikolai Kochetov 已提交
153
        /// Load tailing 8 bytes. Word is 'defghXYZ'.
N
Nikolai Kochetov 已提交
154
        auto word = unalignedLoad<UInt64>(end - 8);
N
Nikolai Kochetov 已提交
155 156
        /// Prepare mask which will set other 5 bytes to 0. It is 0xFFFFFFFFFFFFFFFF << 5 = 0xFFFFFF0000000000.
        /// word & mask = '\0\0\0\0\0XYZ' (bytes are reversed because of little ending)
N
Nikolai Kochetov 已提交
157
        word &= (~UInt64(0)) << DB::UInt8(8 * (8 - tail_size));
N
Nikolai Kochetov 已提交
158
        /// Use least byte to store tail length.
N
Nikolai Kochetov 已提交
159
        word |= tail_size;
N
Nikolai Kochetov 已提交
160
        /// Now word is '\3\0\0\0\0XYZ'
N
Nikolai Kochetov 已提交
161 162 163 164
        updated_value = intHashCRC32(word, updated_value);
    }

    return updated_value;
N
Nikolai Kochetov 已提交
165
}
166

167
template <typename T>
168
inline size_t DefaultHash64(std::enable_if_t<(sizeof(T) <= sizeof(UInt64)), T> key)
169
{
170 171 172 173 174 175 176 177
    union
    {
        T in;
        DB::UInt64 out;
    } u;
    u.out = 0;
    u.in = key;
    return intHash64(u.out);
178 179
}

A
Alexey Milovidov 已提交
180
template <typename T>
181
inline size_t DefaultHash64(std::enable_if_t<(sizeof(T) > sizeof(UInt64)), T> key)
A
Alexey Milovidov 已提交
182
{
183
    if constexpr (std::is_same_v<T, DB::Int128>)
A
Alexey Milovidov 已提交
184
    {
185
        return intHash64(static_cast<UInt64>(key) ^ static_cast<UInt64>(key >> 64));
A
Alexey Milovidov 已提交
186
    }
187 188 189 190
    if constexpr (std::is_same_v<T, DB::UInt128>)
    {
        return intHash64(key.low ^ key.high);
    }
191
    else if constexpr (is_big_int_v<T> && sizeof(T) == 32)
192 193 194 195 196 197 198 199 200 201 202
    {
        return intHash64(static_cast<UInt64>(key) ^
            static_cast<UInt64>(key >> 64) ^
            static_cast<UInt64>(key >> 128) ^
            static_cast<UInt64>(key >> 256));
    }
    __builtin_unreachable();
}

template <typename T, typename Enable = void>
struct DefaultHash;
203

204
template <typename T>
205
struct DefaultHash<T, std::enable_if_t<!DB::IsDecimalNumber<T>>>
206 207 208
{
    size_t operator() (T key) const
    {
209
        return DefaultHash64<T>(key);
210 211 212 213
    }
};

template <typename T>
214
struct DefaultHash<T, std::enable_if_t<DB::IsDecimalNumber<T>>>
215 216 217
{
    size_t operator() (T key) const
    {
218
        return DefaultHash64<typename T::NativeType>(key);
219 220
    }
};
221 222 223 224

template <typename T> struct HashCRC32;

template <typename T>
225
inline size_t hashCRC32(std::enable_if_t<(sizeof(T) <= sizeof(UInt64)), T> key)
226
{
227 228 229 230 231 232 233 234
    union
    {
        T in;
        DB::UInt64 out;
    } u;
    u.out = 0;
    u.in = key;
    return intHashCRC32(u.out);
235 236
}

237 238 239
template <typename T>
inline size_t hashCRC32(std::enable_if_t<(sizeof(T) > sizeof(UInt64)), T> key)
{
A
Alexey Milovidov 已提交
240
    return intHashCRC32(key, -1);
241 242
}

243 244 245
#define DEFINE_HASH(T) \
template <> struct HashCRC32<T>\
{\
246 247 248 249
    size_t operator() (T key) const\
    {\
        return hashCRC32<T>(key);\
    }\
250 251 252 253 254 255
};

DEFINE_HASH(DB::UInt8)
DEFINE_HASH(DB::UInt16)
DEFINE_HASH(DB::UInt32)
DEFINE_HASH(DB::UInt64)
256
DEFINE_HASH(DB::UInt128)
257
DEFINE_HASH(DB::UInt256)
258 259 260 261
DEFINE_HASH(DB::Int8)
DEFINE_HASH(DB::Int16)
DEFINE_HASH(DB::Int32)
DEFINE_HASH(DB::Int64)
262
DEFINE_HASH(DB::Int128)
263
DEFINE_HASH(DB::Int256)
264 265 266 267
DEFINE_HASH(DB::Float32)
DEFINE_HASH(DB::Float64)

#undef DEFINE_HASH
268 269


270 271 272 273
template <>
struct DefaultHash<DB::UInt128> : public DB::UInt128Hash {};

template <>
274
struct DefaultHash<DB::DummyUInt256> : public DB::UInt256Hash {};
275 276


F
f1yegor 已提交
277
/// It is reasonable to use for UInt8, UInt16 with sufficient hash table size.
278 279
struct TrivialHash
{
280 281 282 283 284
    template <typename T>
    size_t operator() (T key) const
    {
        return key;
    }
285
};
A
Alexey Milovidov 已提交
286 287


288
/** A relatively good non-cryptographic hash function from UInt64 to UInt32.
F
f1yegor 已提交
289 290
  * But worse (both in quality and speed) than just cutting intHash64.
  * Taken from here: http://www.concentric.net/~ttwang/tech/inthash.htm
A
Alexey Milovidov 已提交
291
  *
F
f1yegor 已提交
292 293
  * Slightly changed compared to the function by link: shifts to the right are accidentally replaced by a cyclic shift to the right.
  * This change did not affect the smhasher test results.
A
Alexey Milovidov 已提交
294
  *
F
f1yegor 已提交
295
  * It is recommended to use different salt for different tasks.
296
  * That was the case that in the database values were sorted by hash (for low-quality pseudo-random spread),
F
f1yegor 已提交
297 298
  *  and in another place, in the aggregate function, the same hash was used in the hash table,
  *  as a result, this aggregate function was monstrously slowed due to collisions.
299 300 301 302
  *
  * NOTE Salting is far from perfect, because it commutes with first steps of calculation.
  *
  * NOTE As mentioned, this function is slower than intHash64.
M
maiha 已提交
303
  * But occasionally, it is faster, when written in a loop and loop is vectorized.
A
Alexey Milovidov 已提交
304
  */
305 306
template <DB::UInt64 salt>
inline DB::UInt32 intHash32(DB::UInt64 key)
A
Alexey Milovidov 已提交
307
{
308
    key ^= salt;
A
Alexey Milovidov 已提交
309

310 311 312 313 314 315
    key = (~key) + (key << 18);
    key = key ^ ((key >> 31) | (key << 33));
    key = key * 21;
    key = key ^ ((key >> 11) | (key << 53));
    key = key + (key << 6);
    key = key ^ ((key >> 22) | (key << 42));
A
Alexey Milovidov 已提交
316

317
    return key;
A
Alexey Milovidov 已提交
318 319 320
}


F
f1yegor 已提交
321
/// For containers.
322
template <typename T, DB::UInt64 salt = 0>
A
Alexey Milovidov 已提交
323 324
struct IntHash32
{
325 326
    size_t operator() (const T & key) const
    {
327 328 329 330 331 332 333 334
        if constexpr (std::is_same_v<T, DB::Int128>)
        {
            return intHash32<salt>(static_cast<UInt64>(key) ^ static_cast<UInt64>(key >> 64));
        }
        else if constexpr (std::is_same_v<T, DB::UInt128>)
        {
            return intHash32<salt>(key.low ^ key.high);
        }
335
        else if constexpr (is_big_int_v<T> && sizeof(T) == 32)
336 337 338 339 340 341 342 343 344
        {
            return intHash32<salt>(static_cast<UInt64>(key) ^
                static_cast<UInt64>(key >> 64) ^
                static_cast<UInt64>(key >> 128) ^
                static_cast<UInt64>(key >> 256));
        }
        else if constexpr (sizeof(T) <= sizeof(UInt64))
            return intHash32<salt>(key);
        __builtin_unreachable();
345
    }
A
Alexey Milovidov 已提交
346
};