int128_benchmark.cc 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. // Copyright 2017 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include <algorithm>
  15. #include <cstdint>
  16. #include <limits>
  17. #include <random>
  18. #include <vector>
  19. #include "benchmark/benchmark.h"
  20. #include "absl/base/config.h"
  21. #include "absl/numeric/int128.h"
  22. namespace {
  23. constexpr size_t kSampleSize = 1000000;
  24. std::mt19937 MakeRandomEngine() {
  25. std::random_device r;
  26. std::seed_seq seed({r(), r(), r(), r(), r(), r(), r(), r()});
  27. return std::mt19937(seed);
  28. }
  29. template <typename T,
  30. typename H = typename std::conditional<
  31. std::numeric_limits<T>::is_signed, int64_t, uint64_t>::type>
  32. std::vector<std::pair<T, T>> GetRandomClass128SampleUniformDivisor() {
  33. std::vector<std::pair<T, T>> values;
  34. std::mt19937 random = MakeRandomEngine();
  35. std::uniform_int_distribution<H> uniform_h;
  36. values.reserve(kSampleSize);
  37. for (size_t i = 0; i < kSampleSize; ++i) {
  38. T a{absl::MakeUint128(uniform_h(random), uniform_h(random))};
  39. T b{absl::MakeUint128(uniform_h(random), uniform_h(random))};
  40. values.emplace_back(std::max(a, b), std::max(T(2), std::min(a, b)));
  41. }
  42. return values;
  43. }
  44. template <typename T>
  45. void BM_DivideClass128UniformDivisor(benchmark::State& state) {
  46. auto values = GetRandomClass128SampleUniformDivisor<T>();
  47. while (state.KeepRunningBatch(values.size())) {
  48. for (const auto& pair : values) {
  49. benchmark::DoNotOptimize(pair.first / pair.second);
  50. }
  51. }
  52. }
  53. BENCHMARK_TEMPLATE(BM_DivideClass128UniformDivisor, absl::uint128);
  54. BENCHMARK_TEMPLATE(BM_DivideClass128UniformDivisor, absl::int128);
  55. template <typename T>
  56. void BM_RemainderClass128UniformDivisor(benchmark::State& state) {
  57. auto values = GetRandomClass128SampleUniformDivisor<T>();
  58. while (state.KeepRunningBatch(values.size())) {
  59. for (const auto& pair : values) {
  60. benchmark::DoNotOptimize(pair.first % pair.second);
  61. }
  62. }
  63. }
  64. BENCHMARK_TEMPLATE(BM_RemainderClass128UniformDivisor, absl::uint128);
  65. BENCHMARK_TEMPLATE(BM_RemainderClass128UniformDivisor, absl::int128);
  66. template <typename T,
  67. typename H = typename std::conditional<
  68. std::numeric_limits<T>::is_signed, int64_t, uint64_t>::type>
  69. std::vector<std::pair<T, H>> GetRandomClass128SampleSmallDivisor() {
  70. std::vector<std::pair<T, H>> values;
  71. std::mt19937 random = MakeRandomEngine();
  72. std::uniform_int_distribution<H> uniform_h;
  73. values.reserve(kSampleSize);
  74. for (size_t i = 0; i < kSampleSize; ++i) {
  75. T a{absl::MakeUint128(uniform_h(random), uniform_h(random))};
  76. H b{std::max(H{2}, uniform_h(random))};
  77. values.emplace_back(std::max(a, T(b)), b);
  78. }
  79. return values;
  80. }
  81. template <typename T>
  82. void BM_DivideClass128SmallDivisor(benchmark::State& state) {
  83. auto values = GetRandomClass128SampleSmallDivisor<T>();
  84. while (state.KeepRunningBatch(values.size())) {
  85. for (const auto& pair : values) {
  86. benchmark::DoNotOptimize(pair.first / pair.second);
  87. }
  88. }
  89. }
  90. BENCHMARK_TEMPLATE(BM_DivideClass128SmallDivisor, absl::uint128);
  91. BENCHMARK_TEMPLATE(BM_DivideClass128SmallDivisor, absl::int128);
  92. template <typename T>
  93. void BM_RemainderClass128SmallDivisor(benchmark::State& state) {
  94. auto values = GetRandomClass128SampleSmallDivisor<T>();
  95. while (state.KeepRunningBatch(values.size())) {
  96. for (const auto& pair : values) {
  97. benchmark::DoNotOptimize(pair.first % pair.second);
  98. }
  99. }
  100. }
  101. BENCHMARK_TEMPLATE(BM_RemainderClass128SmallDivisor, absl::uint128);
  102. BENCHMARK_TEMPLATE(BM_RemainderClass128SmallDivisor, absl::int128);
  103. std::vector<std::pair<absl::uint128, absl::uint128>> GetRandomClass128Sample() {
  104. std::vector<std::pair<absl::uint128, absl::uint128>> values;
  105. std::mt19937 random = MakeRandomEngine();
  106. std::uniform_int_distribution<uint64_t> uniform_uint64;
  107. values.reserve(kSampleSize);
  108. for (size_t i = 0; i < kSampleSize; ++i) {
  109. values.emplace_back(
  110. absl::MakeUint128(uniform_uint64(random), uniform_uint64(random)),
  111. absl::MakeUint128(uniform_uint64(random), uniform_uint64(random)));
  112. }
  113. return values;
  114. }
  115. void BM_MultiplyClass128(benchmark::State& state) {
  116. auto values = GetRandomClass128Sample();
  117. while (state.KeepRunningBatch(values.size())) {
  118. for (const auto& pair : values) {
  119. benchmark::DoNotOptimize(pair.first * pair.second);
  120. }
  121. }
  122. }
  123. BENCHMARK(BM_MultiplyClass128);
  124. void BM_AddClass128(benchmark::State& state) {
  125. auto values = GetRandomClass128Sample();
  126. while (state.KeepRunningBatch(values.size())) {
  127. for (const auto& pair : values) {
  128. benchmark::DoNotOptimize(pair.first + pair.second);
  129. }
  130. }
  131. }
  132. BENCHMARK(BM_AddClass128);
  133. #ifdef ABSL_HAVE_INTRINSIC_INT128
  134. // Some implementations of <random> do not support __int128 when it is
  135. // available, so we make our own uniform_int_distribution-like type.
  136. template <typename T,
  137. typename H = typename std::conditional<
  138. std::is_same<T, __int128>::value, int64_t, uint64_t>::type>
  139. class UniformIntDistribution128 {
  140. public:
  141. // NOLINTNEXTLINE: mimicking std::uniform_int_distribution API
  142. T operator()(std::mt19937& generator) {
  143. return (static_cast<T>(dist64_(generator)) << 64) | dist64_(generator);
  144. }
  145. private:
  146. std::uniform_int_distribution<H> dist64_;
  147. };
  148. template <typename T,
  149. typename H = typename std::conditional<
  150. std::is_same<T, __int128>::value, int64_t, uint64_t>::type>
  151. std::vector<std::pair<T, T>> GetRandomIntrinsic128SampleUniformDivisor() {
  152. std::vector<std::pair<T, T>> values;
  153. std::mt19937 random = MakeRandomEngine();
  154. UniformIntDistribution128<T> uniform_128;
  155. values.reserve(kSampleSize);
  156. for (size_t i = 0; i < kSampleSize; ++i) {
  157. T a = uniform_128(random);
  158. T b = uniform_128(random);
  159. values.emplace_back(std::max(a, b),
  160. std::max(static_cast<T>(2), std::min(a, b)));
  161. }
  162. return values;
  163. }
  164. template <typename T>
  165. void BM_DivideIntrinsic128UniformDivisor(benchmark::State& state) {
  166. auto values = GetRandomIntrinsic128SampleUniformDivisor<T>();
  167. while (state.KeepRunningBatch(values.size())) {
  168. for (const auto& pair : values) {
  169. benchmark::DoNotOptimize(pair.first / pair.second);
  170. }
  171. }
  172. }
  173. BENCHMARK_TEMPLATE(BM_DivideIntrinsic128UniformDivisor, unsigned __int128);
  174. BENCHMARK_TEMPLATE(BM_DivideIntrinsic128UniformDivisor, __int128);
  175. template <typename T>
  176. void BM_RemainderIntrinsic128UniformDivisor(benchmark::State& state) {
  177. auto values = GetRandomIntrinsic128SampleUniformDivisor<T>();
  178. while (state.KeepRunningBatch(values.size())) {
  179. for (const auto& pair : values) {
  180. benchmark::DoNotOptimize(pair.first % pair.second);
  181. }
  182. }
  183. }
  184. BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128UniformDivisor, unsigned __int128);
  185. BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128UniformDivisor, __int128);
  186. template <typename T,
  187. typename H = typename std::conditional<
  188. std::is_same<T, __int128>::value, int64_t, uint64_t>::type>
  189. std::vector<std::pair<T, H>> GetRandomIntrinsic128SampleSmallDivisor() {
  190. std::vector<std::pair<T, H>> values;
  191. std::mt19937 random = MakeRandomEngine();
  192. UniformIntDistribution128<T> uniform_int128;
  193. std::uniform_int_distribution<H> uniform_int64;
  194. values.reserve(kSampleSize);
  195. for (size_t i = 0; i < kSampleSize; ++i) {
  196. T a = uniform_int128(random);
  197. H b = std::max(H{2}, uniform_int64(random));
  198. values.emplace_back(std::max(a, static_cast<T>(b)), b);
  199. }
  200. return values;
  201. }
  202. template <typename T>
  203. void BM_DivideIntrinsic128SmallDivisor(benchmark::State& state) {
  204. auto values = GetRandomIntrinsic128SampleSmallDivisor<T>();
  205. while (state.KeepRunningBatch(values.size())) {
  206. for (const auto& pair : values) {
  207. benchmark::DoNotOptimize(pair.first / pair.second);
  208. }
  209. }
  210. }
  211. BENCHMARK_TEMPLATE(BM_DivideIntrinsic128SmallDivisor, unsigned __int128);
  212. BENCHMARK_TEMPLATE(BM_DivideIntrinsic128SmallDivisor, __int128);
  213. template <typename T>
  214. void BM_RemainderIntrinsic128SmallDivisor(benchmark::State& state) {
  215. auto values = GetRandomIntrinsic128SampleSmallDivisor<T>();
  216. while (state.KeepRunningBatch(values.size())) {
  217. for (const auto& pair : values) {
  218. benchmark::DoNotOptimize(pair.first % pair.second);
  219. }
  220. }
  221. }
  222. BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128SmallDivisor, unsigned __int128);
  223. BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128SmallDivisor, __int128);
  224. std::vector<std::pair<unsigned __int128, unsigned __int128>>
  225. GetRandomIntrinsic128Sample() {
  226. std::vector<std::pair<unsigned __int128, unsigned __int128>> values;
  227. std::mt19937 random = MakeRandomEngine();
  228. UniformIntDistribution128<unsigned __int128> uniform_uint128;
  229. values.reserve(kSampleSize);
  230. for (size_t i = 0; i < kSampleSize; ++i) {
  231. values.emplace_back(uniform_uint128(random), uniform_uint128(random));
  232. }
  233. return values;
  234. }
  235. void BM_MultiplyIntrinsic128(benchmark::State& state) {
  236. auto values = GetRandomIntrinsic128Sample();
  237. while (state.KeepRunningBatch(values.size())) {
  238. for (const auto& pair : values) {
  239. benchmark::DoNotOptimize(pair.first * pair.second);
  240. }
  241. }
  242. }
  243. BENCHMARK(BM_MultiplyIntrinsic128);
  244. void BM_AddIntrinsic128(benchmark::State& state) {
  245. auto values = GetRandomIntrinsic128Sample();
  246. while (state.KeepRunningBatch(values.size())) {
  247. for (const auto& pair : values) {
  248. benchmark::DoNotOptimize(pair.first + pair.second);
  249. }
  250. }
  251. }
  252. BENCHMARK(BM_AddIntrinsic128);
  253. #endif // ABSL_HAVE_INTRINSIC_INT128
  254. } // namespace