unordered_set_operations.bench.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. #include <unordered_set>
  2. #include <vector>
  3. #include <functional>
  4. #include <cstdint>
  5. #include <cstdlib>
  6. #include <cstring>
  7. #include "benchmark/benchmark.h"
  8. #include "ContainerBenchmarks.hpp"
  9. #include "GenerateInput.hpp"
  10. #include "test_macros.h"
  11. using namespace ContainerBenchmarks;
  12. constexpr std::size_t TestNumInputs = 1024;
  13. template <class _Size>
  14. inline TEST_ALWAYS_INLINE
  15. _Size loadword(const void* __p) {
  16. _Size __r;
  17. std::memcpy(&__r, __p, sizeof(__r));
  18. return __r;
  19. }
  20. inline TEST_ALWAYS_INLINE
  21. std::size_t rotate_by_at_least_1(std::size_t __val, int __shift) {
  22. return (__val >> __shift) | (__val << (64 - __shift));
  23. }
  24. inline TEST_ALWAYS_INLINE
  25. std::size_t hash_len_16(std::size_t __u, std::size_t __v) {
  26. const std::size_t __mul = 0x9ddfea08eb382d69ULL;
  27. std::size_t __a = (__u ^ __v) * __mul;
  28. __a ^= (__a >> 47);
  29. std::size_t __b = (__v ^ __a) * __mul;
  30. __b ^= (__b >> 47);
  31. __b *= __mul;
  32. return __b;
  33. }
  34. template <std::size_t _Len>
  35. inline TEST_ALWAYS_INLINE
  36. std::size_t hash_len_0_to_8(const char* __s) {
  37. static_assert(_Len == 4 || _Len == 8, "");
  38. const uint64_t __a = loadword<uint32_t>(__s);
  39. const uint64_t __b = loadword<uint32_t>(__s + _Len - 4);
  40. return hash_len_16(_Len + (__a << 3), __b);
  41. }
  42. struct UInt32Hash {
  43. UInt32Hash() = default;
  44. inline TEST_ALWAYS_INLINE
  45. std::size_t operator()(uint32_t data) const {
  46. return hash_len_0_to_8<4>(reinterpret_cast<const char*>(&data));
  47. }
  48. };
  49. struct UInt64Hash {
  50. UInt64Hash() = default;
  51. inline TEST_ALWAYS_INLINE
  52. std::size_t operator()(uint64_t data) const {
  53. return hash_len_0_to_8<8>(reinterpret_cast<const char*>(&data));
  54. }
  55. };
  56. struct UInt128Hash {
  57. UInt128Hash() = default;
  58. inline TEST_ALWAYS_INLINE
  59. std::size_t operator()(__uint128_t data) const {
  60. const __uint128_t __mask = static_cast<std::size_t>(-1);
  61. const std::size_t __a = (std::size_t)(data & __mask);
  62. const std::size_t __b = (std::size_t)((data & (__mask << 64)) >> 64);
  63. return hash_len_16(__a, rotate_by_at_least_1(__b + 16, 16)) ^ __b;
  64. }
  65. };
  66. struct UInt32Hash2 {
  67. UInt32Hash2() = default;
  68. inline TEST_ALWAYS_INLINE
  69. std::size_t operator()(uint32_t data) const {
  70. const uint32_t __m = 0x5bd1e995;
  71. const uint32_t __r = 24;
  72. uint32_t __h = 4;
  73. uint32_t __k = data;
  74. __k *= __m;
  75. __k ^= __k >> __r;
  76. __k *= __m;
  77. __h *= __m;
  78. __h ^= __k;
  79. __h ^= __h >> 13;
  80. __h *= __m;
  81. __h ^= __h >> 15;
  82. return __h;
  83. }
  84. };
  85. struct UInt64Hash2 {
  86. UInt64Hash2() = default;
  87. inline TEST_ALWAYS_INLINE
  88. std::size_t operator()(uint64_t data) const {
  89. return hash_len_0_to_8<8>(reinterpret_cast<const char*>(&data));
  90. }
  91. };
  92. //----------------------------------------------------------------------------//
  93. // BM_Hash
  94. // ---------------------------------------------------------------------------//
  95. template <class HashFn, class GenInputs>
  96. void BM_Hash(benchmark::State& st, HashFn fn, GenInputs gen) {
  97. auto in = gen(st.range(0));
  98. const auto end = in.data() + in.size();
  99. std::size_t last_hash = 0;
  100. benchmark::DoNotOptimize(&last_hash);
  101. while (st.KeepRunning()) {
  102. for (auto it = in.data(); it != end; ++it) {
  103. benchmark::DoNotOptimize(last_hash += fn(*it));
  104. }
  105. benchmark::ClobberMemory();
  106. }
  107. }
  108. BENCHMARK_CAPTURE(BM_Hash,
  109. uint32_random_std_hash,
  110. std::hash<uint32_t>{},
  111. getRandomIntegerInputs<uint32_t>) -> Arg(TestNumInputs);
  112. BENCHMARK_CAPTURE(BM_Hash,
  113. uint32_random_custom_hash,
  114. UInt32Hash{},
  115. getRandomIntegerInputs<uint32_t>) -> Arg(TestNumInputs);
  116. BENCHMARK_CAPTURE(BM_Hash,
  117. uint32_top_std_hash,
  118. std::hash<uint32_t>{},
  119. getSortedTopBitsIntegerInputs<uint32_t>) -> Arg(TestNumInputs);
  120. BENCHMARK_CAPTURE(BM_Hash,
  121. uint32_top_custom_hash,
  122. UInt32Hash{},
  123. getSortedTopBitsIntegerInputs<uint32_t>) -> Arg(TestNumInputs);
  124. //----------------------------------------------------------------------------//
  125. // BM_InsertValue
  126. // ---------------------------------------------------------------------------//
  127. // Sorted Assending //
  128. BENCHMARK_CAPTURE(BM_InsertValue,
  129. unordered_set_uint32,
  130. std::unordered_set<uint32_t>{},
  131. getRandomIntegerInputs<uint32_t>)->Arg(TestNumInputs);
  132. BENCHMARK_CAPTURE(BM_InsertValue,
  133. unordered_set_uint32_sorted,
  134. std::unordered_set<uint32_t>{},
  135. getSortedIntegerInputs<uint32_t>)->Arg(TestNumInputs);
  136. // Top Bytes //
  137. BENCHMARK_CAPTURE(BM_InsertValue,
  138. unordered_set_top_bits_uint32,
  139. std::unordered_set<uint32_t>{},
  140. getSortedTopBitsIntegerInputs<uint32_t>)->Arg(TestNumInputs);
  141. BENCHMARK_CAPTURE(BM_InsertValueRehash,
  142. unordered_set_top_bits_uint32,
  143. std::unordered_set<uint32_t, UInt32Hash>{},
  144. getSortedTopBitsIntegerInputs<uint32_t>)->Arg(TestNumInputs);
  145. // String //
  146. BENCHMARK_CAPTURE(BM_InsertValue,
  147. unordered_set_string,
  148. std::unordered_set<std::string>{},
  149. getRandomStringInputs)->Arg(TestNumInputs);
  150. BENCHMARK_CAPTURE(BM_InsertValueRehash,
  151. unordered_set_string,
  152. std::unordered_set<std::string>{},
  153. getRandomStringInputs)->Arg(TestNumInputs);
  154. //----------------------------------------------------------------------------//
  155. // BM_Find
  156. // ---------------------------------------------------------------------------//
  157. // Random //
  158. BENCHMARK_CAPTURE(BM_Find,
  159. unordered_set_random_uint64,
  160. std::unordered_set<uint64_t>{},
  161. getRandomIntegerInputs<uint64_t>)->Arg(TestNumInputs);
  162. BENCHMARK_CAPTURE(BM_FindRehash,
  163. unordered_set_random_uint64,
  164. std::unordered_set<uint64_t, UInt64Hash>{},
  165. getRandomIntegerInputs<uint64_t>)->Arg(TestNumInputs);
  166. // Sorted //
  167. BENCHMARK_CAPTURE(BM_Find,
  168. unordered_set_sorted_uint64,
  169. std::unordered_set<uint64_t>{},
  170. getSortedIntegerInputs<uint64_t>)->Arg(TestNumInputs);
  171. BENCHMARK_CAPTURE(BM_FindRehash,
  172. unordered_set_sorted_uint64,
  173. std::unordered_set<uint64_t, UInt64Hash>{},
  174. getSortedIntegerInputs<uint64_t>)->Arg(TestNumInputs);
  175. // Sorted //
  176. #if 1
  177. BENCHMARK_CAPTURE(BM_Find,
  178. unordered_set_sorted_uint128,
  179. std::unordered_set<__uint128_t, UInt128Hash>{},
  180. getSortedTopBitsIntegerInputs<__uint128_t>)->Arg(TestNumInputs);
  181. BENCHMARK_CAPTURE(BM_FindRehash,
  182. unordered_set_sorted_uint128,
  183. std::unordered_set<__uint128_t, UInt128Hash>{},
  184. getSortedTopBitsIntegerInputs<__uint128_t>)->Arg(TestNumInputs);
  185. #endif
  186. // Sorted //
  187. BENCHMARK_CAPTURE(BM_Find,
  188. unordered_set_sorted_uint32,
  189. std::unordered_set<uint32_t>{},
  190. getSortedIntegerInputs<uint32_t>)->Arg(TestNumInputs);
  191. BENCHMARK_CAPTURE(BM_FindRehash,
  192. unordered_set_sorted_uint32,
  193. std::unordered_set<uint32_t, UInt32Hash2>{},
  194. getSortedIntegerInputs<uint32_t>)->Arg(TestNumInputs);
  195. // Sorted Ascending //
  196. BENCHMARK_CAPTURE(BM_Find,
  197. unordered_set_sorted_large_uint64,
  198. std::unordered_set<uint64_t>{},
  199. getSortedLargeIntegerInputs<uint64_t>)->Arg(TestNumInputs);
  200. BENCHMARK_CAPTURE(BM_FindRehash,
  201. unordered_set_sorted_large_uint64,
  202. std::unordered_set<uint64_t, UInt64Hash>{},
  203. getSortedLargeIntegerInputs<uint64_t>)->Arg(TestNumInputs);
  204. // Top Bits //
  205. BENCHMARK_CAPTURE(BM_Find,
  206. unordered_set_top_bits_uint64,
  207. std::unordered_set<uint64_t>{},
  208. getSortedTopBitsIntegerInputs<uint64_t>)->Arg(TestNumInputs);
  209. BENCHMARK_CAPTURE(BM_FindRehash,
  210. unordered_set_top_bits_uint64,
  211. std::unordered_set<uint64_t, UInt64Hash>{},
  212. getSortedTopBitsIntegerInputs<uint64_t>)->Arg(TestNumInputs);
  213. // String //
  214. BENCHMARK_CAPTURE(BM_Find,
  215. unordered_set_string,
  216. std::unordered_set<std::string>{},
  217. getRandomStringInputs)->Arg(TestNumInputs);
  218. BENCHMARK_CAPTURE(BM_FindRehash,
  219. unordered_set_string,
  220. std::unordered_set<std::string>{},
  221. getRandomStringInputs)->Arg(TestNumInputs);
  222. ///////////////////////////////////////////////////////////////////////////////
  223. BENCHMARK_CAPTURE(BM_InsertDuplicate,
  224. unordered_set_int,
  225. std::unordered_set<int>{},
  226. getRandomIntegerInputs<int>)->Arg(TestNumInputs);
  227. BENCHMARK_CAPTURE(BM_InsertDuplicate,
  228. unordered_set_string,
  229. std::unordered_set<std::string>{},
  230. getRandomStringInputs)->Arg(TestNumInputs);
  231. BENCHMARK_CAPTURE(BM_EmplaceDuplicate,
  232. unordered_set_int,
  233. std::unordered_set<int>{},
  234. getRandomIntegerInputs<int>)->Arg(TestNumInputs);
  235. BENCHMARK_CAPTURE(BM_EmplaceDuplicate,
  236. unordered_set_string,
  237. std::unordered_set<std::string>{},
  238. getRandomStringInputs)->Arg(TestNumInputs);
  239. BENCHMARK_CAPTURE(BM_InsertDuplicate,
  240. unordered_set_int_insert_arg,
  241. std::unordered_set<int>{},
  242. getRandomIntegerInputs<int>)->Arg(TestNumInputs);
  243. BENCHMARK_CAPTURE(BM_InsertDuplicate,
  244. unordered_set_string_insert_arg,
  245. std::unordered_set<std::string>{},
  246. getRandomStringInputs)->Arg(TestNumInputs);
  247. BENCHMARK_CAPTURE(BM_EmplaceDuplicate,
  248. unordered_set_int_insert_arg,
  249. std::unordered_set<int>{},
  250. getRandomIntegerInputs<unsigned>)->Arg(TestNumInputs);
  251. BENCHMARK_CAPTURE(BM_EmplaceDuplicate,
  252. unordered_set_string_arg,
  253. std::unordered_set<std::string>{},
  254. getRandomCStringInputs)->Arg(TestNumInputs);
  255. BENCHMARK_MAIN();