| #include <unordered_set> | |
| #include <vector> | |
| #include <functional> | |
| #include <cstdint> | |
| #include <cstdlib> | |
| #include <cstring> | |
| #include "benchmark/benchmark.h" | |
| #include "ContainerBenchmarks.h" | |
| #include "GenerateInput.h" | |
| #include "test_macros.h" | |
| using namespace ContainerBenchmarks; | |
| constexpr std::size_t TestNumInputs = 1024; | |
| template <class _Size> | |
| inline TEST_ALWAYS_INLINE | |
| _Size loadword(const void* __p) { | |
| _Size __r; | |
| std::memcpy(&__r, __p, sizeof(__r)); | |
| return __r; | |
| } | |
| inline TEST_ALWAYS_INLINE | |
| std::size_t rotate_by_at_least_1(std::size_t __val, int __shift) { | |
| return (__val >> __shift) | (__val << (64 - __shift)); | |
| } | |
| inline TEST_ALWAYS_INLINE | |
| std::size_t hash_len_16(std::size_t __u, std::size_t __v) { | |
| const std::size_t __mul = 0x9ddfea08eb382d69ULL; | |
| std::size_t __a = (__u ^ __v) * __mul; | |
| __a ^= (__a >> 47); | |
| std::size_t __b = (__v ^ __a) * __mul; | |
| __b ^= (__b >> 47); | |
| __b *= __mul; | |
| return __b; | |
| } | |
| template <std::size_t _Len> | |
| inline TEST_ALWAYS_INLINE | |
| std::size_t hash_len_0_to_8(const char* __s) { | |
| static_assert(_Len == 4 || _Len == 8, ""); | |
| const uint64_t __a = loadword<uint32_t>(__s); | |
| const uint64_t __b = loadword<uint32_t>(__s + _Len - 4); | |
| return hash_len_16(_Len + (__a << 3), __b); | |
| } | |
| struct UInt32Hash { | |
| UInt32Hash() = default; | |
| inline TEST_ALWAYS_INLINE | |
| std::size_t operator()(uint32_t data) const { | |
| return hash_len_0_to_8<4>(reinterpret_cast<const char*>(&data)); | |
| } | |
| }; | |
| struct UInt64Hash { | |
| UInt64Hash() = default; | |
| inline TEST_ALWAYS_INLINE | |
| std::size_t operator()(uint64_t data) const { | |
| return hash_len_0_to_8<8>(reinterpret_cast<const char*>(&data)); | |
| } | |
| }; | |
| struct UInt128Hash { | |
| UInt128Hash() = default; | |
| inline TEST_ALWAYS_INLINE | |
| std::size_t operator()(__uint128_t data) const { | |
| const __uint128_t __mask = static_cast<std::size_t>(-1); | |
| const std::size_t __a = (std::size_t)(data & __mask); | |
| const std::size_t __b = (std::size_t)((data & (__mask << 64)) >> 64); | |
| return hash_len_16(__a, rotate_by_at_least_1(__b + 16, 16)) ^ __b; | |
| } | |
| }; | |
| struct UInt32Hash2 { | |
| UInt32Hash2() = default; | |
| inline TEST_ALWAYS_INLINE | |
| std::size_t operator()(uint32_t data) const { | |
| const uint32_t __m = 0x5bd1e995; | |
| const uint32_t __r = 24; | |
| uint32_t __h = 4; | |
| uint32_t __k = data; | |
| __k *= __m; | |
| __k ^= __k >> __r; | |
| __k *= __m; | |
| __h *= __m; | |
| __h ^= __k; | |
| __h ^= __h >> 13; | |
| __h *= __m; | |
| __h ^= __h >> 15; | |
| return __h; | |
| } | |
| }; | |
| struct UInt64Hash2 { | |
| UInt64Hash2() = default; | |
| inline TEST_ALWAYS_INLINE | |
| std::size_t operator()(uint64_t data) const { | |
| return hash_len_0_to_8<8>(reinterpret_cast<const char*>(&data)); | |
| } | |
| }; | |
| //----------------------------------------------------------------------------// | |
| // BM_Hash | |
| // ---------------------------------------------------------------------------// | |
| template <class HashFn, class GenInputs> | |
| void BM_Hash(benchmark::State& st, HashFn fn, GenInputs gen) { | |
| auto in = gen(st.range(0)); | |
| const auto end = in.data() + in.size(); | |
| std::size_t last_hash = 0; | |
| benchmark::DoNotOptimize(&last_hash); | |
| while (st.KeepRunning()) { | |
| for (auto it = in.data(); it != end; ++it) { | |
| benchmark::DoNotOptimize(last_hash += fn(*it)); | |
| } | |
| benchmark::ClobberMemory(); | |
| } | |
| } | |
| BENCHMARK_CAPTURE(BM_Hash, | |
| uint32_random_std_hash, | |
| std::hash<uint32_t>{}, | |
| getRandomIntegerInputs<uint32_t>) -> Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_Hash, | |
| uint32_random_custom_hash, | |
| UInt32Hash{}, | |
| getRandomIntegerInputs<uint32_t>) -> Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_Hash, | |
| uint32_top_std_hash, | |
| std::hash<uint32_t>{}, | |
| getSortedTopBitsIntegerInputs<uint32_t>) -> Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_Hash, | |
| uint32_top_custom_hash, | |
| UInt32Hash{}, | |
| getSortedTopBitsIntegerInputs<uint32_t>) -> Arg(TestNumInputs); | |
| //----------------------------------------------------------------------------// | |
| // BM_InsertValue | |
| // ---------------------------------------------------------------------------// | |
| // Sorted Assending // | |
| BENCHMARK_CAPTURE(BM_InsertValue, | |
| unordered_set_uint32, | |
| std::unordered_set<uint32_t>{}, | |
| getRandomIntegerInputs<uint32_t>)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_InsertValue, | |
| unordered_set_uint32_sorted, | |
| std::unordered_set<uint32_t>{}, | |
| getSortedIntegerInputs<uint32_t>)->Arg(TestNumInputs); | |
| // Top Bytes // | |
| BENCHMARK_CAPTURE(BM_InsertValue, | |
| unordered_set_top_bits_uint32, | |
| std::unordered_set<uint32_t>{}, | |
| getSortedTopBitsIntegerInputs<uint32_t>)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_InsertValueRehash, | |
| unordered_set_top_bits_uint32, | |
| std::unordered_set<uint32_t, UInt32Hash>{}, | |
| getSortedTopBitsIntegerInputs<uint32_t>)->Arg(TestNumInputs); | |
| // String // | |
| BENCHMARK_CAPTURE(BM_InsertValue, | |
| unordered_set_string, | |
| std::unordered_set<std::string>{}, | |
| getRandomStringInputs)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_InsertValueRehash, | |
| unordered_set_string, | |
| std::unordered_set<std::string>{}, | |
| getRandomStringInputs)->Arg(TestNumInputs); | |
| //----------------------------------------------------------------------------// | |
| // BM_Find | |
| // ---------------------------------------------------------------------------// | |
| // Random // | |
| BENCHMARK_CAPTURE(BM_Find, | |
| unordered_set_random_uint64, | |
| std::unordered_set<uint64_t>{}, | |
| getRandomIntegerInputs<uint64_t>)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_FindRehash, | |
| unordered_set_random_uint64, | |
| std::unordered_set<uint64_t, UInt64Hash>{}, | |
| getRandomIntegerInputs<uint64_t>)->Arg(TestNumInputs); | |
| // Sorted // | |
| BENCHMARK_CAPTURE(BM_Find, | |
| unordered_set_sorted_uint64, | |
| std::unordered_set<uint64_t>{}, | |
| getSortedIntegerInputs<uint64_t>)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_FindRehash, | |
| unordered_set_sorted_uint64, | |
| std::unordered_set<uint64_t, UInt64Hash>{}, | |
| getSortedIntegerInputs<uint64_t>)->Arg(TestNumInputs); | |
| // Sorted // | |
| #if 1 | |
| BENCHMARK_CAPTURE(BM_Find, | |
| unordered_set_sorted_uint128, | |
| std::unordered_set<__uint128_t, UInt128Hash>{}, | |
| getSortedTopBitsIntegerInputs<__uint128_t>)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_FindRehash, | |
| unordered_set_sorted_uint128, | |
| std::unordered_set<__uint128_t, UInt128Hash>{}, | |
| getSortedTopBitsIntegerInputs<__uint128_t>)->Arg(TestNumInputs); | |
| #endif | |
| // Sorted // | |
| BENCHMARK_CAPTURE(BM_Find, | |
| unordered_set_sorted_uint32, | |
| std::unordered_set<uint32_t>{}, | |
| getSortedIntegerInputs<uint32_t>)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_FindRehash, | |
| unordered_set_sorted_uint32, | |
| std::unordered_set<uint32_t, UInt32Hash2>{}, | |
| getSortedIntegerInputs<uint32_t>)->Arg(TestNumInputs); | |
| // Sorted Ascending // | |
| BENCHMARK_CAPTURE(BM_Find, | |
| unordered_set_sorted_large_uint64, | |
| std::unordered_set<uint64_t>{}, | |
| getSortedLargeIntegerInputs<uint64_t>)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_FindRehash, | |
| unordered_set_sorted_large_uint64, | |
| std::unordered_set<uint64_t, UInt64Hash>{}, | |
| getSortedLargeIntegerInputs<uint64_t>)->Arg(TestNumInputs); | |
| // Top Bits // | |
| BENCHMARK_CAPTURE(BM_Find, | |
| unordered_set_top_bits_uint64, | |
| std::unordered_set<uint64_t>{}, | |
| getSortedTopBitsIntegerInputs<uint64_t>)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_FindRehash, | |
| unordered_set_top_bits_uint64, | |
| std::unordered_set<uint64_t, UInt64Hash>{}, | |
| getSortedTopBitsIntegerInputs<uint64_t>)->Arg(TestNumInputs); | |
| // String // | |
| BENCHMARK_CAPTURE(BM_Find, | |
| unordered_set_string, | |
| std::unordered_set<std::string>{}, | |
| getRandomStringInputs)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_FindRehash, | |
| unordered_set_string, | |
| std::unordered_set<std::string>{}, | |
| getRandomStringInputs)->Arg(TestNumInputs); | |
| /////////////////////////////////////////////////////////////////////////////// | |
| BENCHMARK_CAPTURE(BM_InsertDuplicate, | |
| unordered_set_int, | |
| std::unordered_set<int>{}, | |
| getRandomIntegerInputs<int>)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_InsertDuplicate, | |
| unordered_set_string, | |
| std::unordered_set<std::string>{}, | |
| getRandomStringInputs)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_EmplaceDuplicate, | |
| unordered_set_int, | |
| std::unordered_set<int>{}, | |
| getRandomIntegerInputs<int>)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_EmplaceDuplicate, | |
| unordered_set_string, | |
| std::unordered_set<std::string>{}, | |
| getRandomStringInputs)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_InsertDuplicate, | |
| unordered_set_int_insert_arg, | |
| std::unordered_set<int>{}, | |
| getRandomIntegerInputs<int>)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_InsertDuplicate, | |
| unordered_set_string_insert_arg, | |
| std::unordered_set<std::string>{}, | |
| getRandomStringInputs)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_EmplaceDuplicate, | |
| unordered_set_int_insert_arg, | |
| std::unordered_set<int>{}, | |
| getRandomIntegerInputs<unsigned>)->Arg(TestNumInputs); | |
| BENCHMARK_CAPTURE(BM_EmplaceDuplicate, | |
| unordered_set_string_arg, | |
| std::unordered_set<std::string>{}, | |
| getRandomCStringInputs)->Arg(TestNumInputs); | |
| BENCHMARK_MAIN(); |