unordered_set_operations.bench.cpp 9.1 KB

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