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 
15 #include <algorithm>
16 #include <cstdint>
17 #include <limits>
18 #include <random>
19 #include <vector>
20 
21 #include "benchmark/benchmark.h"
22 #include "absl/base/config.h"
23 #include "absl/numeric/int128.h"
24 
25 namespace {
26 
27 constexpr size_t kSampleSize = 1000000;
28 
MakeRandomEngine()29 std::mt19937 MakeRandomEngine() {
30   std::random_device r;
31   std::seed_seq seed({r(), r(), r(), r(), r(), r(), r(), r()});
32   return std::mt19937(seed);
33 }
34 
35 template <typename T,
36           typename H = typename std::conditional<
37               std::numeric_limits<T>::is_signed, int64_t, uint64_t>::type>
GetRandomClass128SampleUniformDivisor()38 std::vector<std::pair<T, T>> GetRandomClass128SampleUniformDivisor() {
39   std::vector<std::pair<T, T>> values;
40   std::mt19937 random = MakeRandomEngine();
41   std::uniform_int_distribution<H> uniform_h;
42   values.reserve(kSampleSize);
43   for (size_t i = 0; i < kSampleSize; ++i) {
44     T a{absl::MakeUint128(uniform_h(random), uniform_h(random))};
45     T b{absl::MakeUint128(uniform_h(random), uniform_h(random))};
46     values.emplace_back(std::max(a, b), std::max(T(2), std::min(a, b)));
47   }
48   return values;
49 }
50 
51 template <typename T>
BM_DivideClass128UniformDivisor(benchmark::State & state)52 void BM_DivideClass128UniformDivisor(benchmark::State& state) {
53   auto values = GetRandomClass128SampleUniformDivisor<T>();
54   while (state.KeepRunningBatch(values.size())) {
55     for (const auto& pair : values) {
56       benchmark::DoNotOptimize(pair.first / pair.second);
57     }
58   }
59 }
60 BENCHMARK_TEMPLATE(BM_DivideClass128UniformDivisor, absl::uint128);
61 BENCHMARK_TEMPLATE(BM_DivideClass128UniformDivisor, absl::int128);
62 
63 template <typename T>
BM_RemainderClass128UniformDivisor(benchmark::State & state)64 void BM_RemainderClass128UniformDivisor(benchmark::State& state) {
65   auto values = GetRandomClass128SampleUniformDivisor<T>();
66   while (state.KeepRunningBatch(values.size())) {
67     for (const auto& pair : values) {
68       benchmark::DoNotOptimize(pair.first % pair.second);
69     }
70   }
71 }
72 BENCHMARK_TEMPLATE(BM_RemainderClass128UniformDivisor, absl::uint128);
73 BENCHMARK_TEMPLATE(BM_RemainderClass128UniformDivisor, absl::int128);
74 
75 template <typename T,
76           typename H = typename std::conditional<
77               std::numeric_limits<T>::is_signed, int64_t, uint64_t>::type>
GetRandomClass128SampleSmallDivisor()78 std::vector<std::pair<T, H>> GetRandomClass128SampleSmallDivisor() {
79   std::vector<std::pair<T, H>> values;
80   std::mt19937 random = MakeRandomEngine();
81   std::uniform_int_distribution<H> uniform_h;
82   values.reserve(kSampleSize);
83   for (size_t i = 0; i < kSampleSize; ++i) {
84     T a{absl::MakeUint128(uniform_h(random), uniform_h(random))};
85     H b{std::max(H{2}, uniform_h(random))};
86     values.emplace_back(std::max(a, T(b)), b);
87   }
88   return values;
89 }
90 
91 template <typename T>
BM_DivideClass128SmallDivisor(benchmark::State & state)92 void BM_DivideClass128SmallDivisor(benchmark::State& state) {
93   auto values = GetRandomClass128SampleSmallDivisor<T>();
94   while (state.KeepRunningBatch(values.size())) {
95     for (const auto& pair : values) {
96       benchmark::DoNotOptimize(pair.first / pair.second);
97     }
98   }
99 }
100 BENCHMARK_TEMPLATE(BM_DivideClass128SmallDivisor, absl::uint128);
101 BENCHMARK_TEMPLATE(BM_DivideClass128SmallDivisor, absl::int128);
102 
103 template <typename T>
BM_RemainderClass128SmallDivisor(benchmark::State & state)104 void BM_RemainderClass128SmallDivisor(benchmark::State& state) {
105   auto values = GetRandomClass128SampleSmallDivisor<T>();
106   while (state.KeepRunningBatch(values.size())) {
107     for (const auto& pair : values) {
108       benchmark::DoNotOptimize(pair.first % pair.second);
109     }
110   }
111 }
112 BENCHMARK_TEMPLATE(BM_RemainderClass128SmallDivisor, absl::uint128);
113 BENCHMARK_TEMPLATE(BM_RemainderClass128SmallDivisor, absl::int128);
114 
GetRandomClass128Sample()115 std::vector<std::pair<absl::uint128, absl::uint128>> GetRandomClass128Sample() {
116   std::vector<std::pair<absl::uint128, absl::uint128>> values;
117   std::mt19937 random = MakeRandomEngine();
118   std::uniform_int_distribution<uint64_t> uniform_uint64;
119   values.reserve(kSampleSize);
120   for (size_t i = 0; i < kSampleSize; ++i) {
121     values.emplace_back(
122         absl::MakeUint128(uniform_uint64(random), uniform_uint64(random)),
123         absl::MakeUint128(uniform_uint64(random), uniform_uint64(random)));
124   }
125   return values;
126 }
127 
BM_MultiplyClass128(benchmark::State & state)128 void BM_MultiplyClass128(benchmark::State& state) {
129   auto values = GetRandomClass128Sample();
130   while (state.KeepRunningBatch(values.size())) {
131     for (const auto& pair : values) {
132       benchmark::DoNotOptimize(pair.first * pair.second);
133     }
134   }
135 }
136 BENCHMARK(BM_MultiplyClass128);
137 
BM_AddClass128(benchmark::State & state)138 void BM_AddClass128(benchmark::State& state) {
139   auto values = GetRandomClass128Sample();
140   while (state.KeepRunningBatch(values.size())) {
141     for (const auto& pair : values) {
142       benchmark::DoNotOptimize(pair.first + pair.second);
143     }
144   }
145 }
146 BENCHMARK(BM_AddClass128);
147 
148 #ifdef ABSL_HAVE_INTRINSIC_INT128
149 
150 // Some implementations of <random> do not support __int128 when it is
151 // available, so we make our own uniform_int_distribution-like type.
152 template <typename T,
153           typename H = typename std::conditional<
154               std::is_same<T, __int128>::value, int64_t, uint64_t>::type>
155 class UniformIntDistribution128 {
156  public:
157   // NOLINTNEXTLINE: mimicking std::uniform_int_distribution API
operator ()(std::mt19937 & generator)158   T operator()(std::mt19937& generator) {
159     return (static_cast<T>(dist64_(generator)) << 64) | dist64_(generator);
160   }
161 
162  private:
163   std::uniform_int_distribution<H> dist64_;
164 };
165 
166 template <typename T,
167           typename H = typename std::conditional<
168               std::is_same<T, __int128>::value, int64_t, uint64_t>::type>
GetRandomIntrinsic128SampleUniformDivisor()169 std::vector<std::pair<T, T>> GetRandomIntrinsic128SampleUniformDivisor() {
170   std::vector<std::pair<T, T>> values;
171   std::mt19937 random = MakeRandomEngine();
172   UniformIntDistribution128<T> uniform_128;
173   values.reserve(kSampleSize);
174   for (size_t i = 0; i < kSampleSize; ++i) {
175     T a = uniform_128(random);
176     T b = uniform_128(random);
177     values.emplace_back(std::max(a, b),
178                         std::max(static_cast<T>(2), std::min(a, b)));
179   }
180   return values;
181 }
182 
183 template <typename T>
BM_DivideIntrinsic128UniformDivisor(benchmark::State & state)184 void BM_DivideIntrinsic128UniformDivisor(benchmark::State& state) {
185   auto values = GetRandomIntrinsic128SampleUniformDivisor<T>();
186   while (state.KeepRunningBatch(values.size())) {
187     for (const auto& pair : values) {
188       benchmark::DoNotOptimize(pair.first / pair.second);
189     }
190   }
191 }
192 BENCHMARK_TEMPLATE(BM_DivideIntrinsic128UniformDivisor, unsigned __int128);
193 BENCHMARK_TEMPLATE(BM_DivideIntrinsic128UniformDivisor, __int128);
194 
195 template <typename T>
BM_RemainderIntrinsic128UniformDivisor(benchmark::State & state)196 void BM_RemainderIntrinsic128UniformDivisor(benchmark::State& state) {
197   auto values = GetRandomIntrinsic128SampleUniformDivisor<T>();
198   while (state.KeepRunningBatch(values.size())) {
199     for (const auto& pair : values) {
200       benchmark::DoNotOptimize(pair.first % pair.second);
201     }
202   }
203 }
204 BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128UniformDivisor, unsigned __int128);
205 BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128UniformDivisor, __int128);
206 
207 template <typename T,
208           typename H = typename std::conditional<
209               std::is_same<T, __int128>::value, int64_t, uint64_t>::type>
GetRandomIntrinsic128SampleSmallDivisor()210 std::vector<std::pair<T, H>> GetRandomIntrinsic128SampleSmallDivisor() {
211   std::vector<std::pair<T, H>> values;
212   std::mt19937 random = MakeRandomEngine();
213   UniformIntDistribution128<T> uniform_int128;
214   std::uniform_int_distribution<H> uniform_int64;
215   values.reserve(kSampleSize);
216   for (size_t i = 0; i < kSampleSize; ++i) {
217     T a = uniform_int128(random);
218     H b = std::max(H{2}, uniform_int64(random));
219     values.emplace_back(std::max(a, static_cast<T>(b)), b);
220   }
221   return values;
222 }
223 
224 template <typename T>
BM_DivideIntrinsic128SmallDivisor(benchmark::State & state)225 void BM_DivideIntrinsic128SmallDivisor(benchmark::State& state) {
226   auto values = GetRandomIntrinsic128SampleSmallDivisor<T>();
227   while (state.KeepRunningBatch(values.size())) {
228     for (const auto& pair : values) {
229       benchmark::DoNotOptimize(pair.first / pair.second);
230     }
231   }
232 }
233 BENCHMARK_TEMPLATE(BM_DivideIntrinsic128SmallDivisor, unsigned __int128);
234 BENCHMARK_TEMPLATE(BM_DivideIntrinsic128SmallDivisor, __int128);
235 
236 template <typename T>
BM_RemainderIntrinsic128SmallDivisor(benchmark::State & state)237 void BM_RemainderIntrinsic128SmallDivisor(benchmark::State& state) {
238   auto values = GetRandomIntrinsic128SampleSmallDivisor<T>();
239   while (state.KeepRunningBatch(values.size())) {
240     for (const auto& pair : values) {
241       benchmark::DoNotOptimize(pair.first % pair.second);
242     }
243   }
244 }
245 BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128SmallDivisor, unsigned __int128);
246 BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128SmallDivisor, __int128);
247 
248 std::vector<std::pair<unsigned __int128, unsigned __int128>>
GetRandomIntrinsic128Sample()249       GetRandomIntrinsic128Sample() {
250   std::vector<std::pair<unsigned __int128, unsigned __int128>> values;
251   std::mt19937 random = MakeRandomEngine();
252   UniformIntDistribution128<unsigned __int128> uniform_uint128;
253   values.reserve(kSampleSize);
254   for (size_t i = 0; i < kSampleSize; ++i) {
255     values.emplace_back(uniform_uint128(random), uniform_uint128(random));
256   }
257   return values;
258 }
259 
BM_MultiplyIntrinsic128(benchmark::State & state)260 void BM_MultiplyIntrinsic128(benchmark::State& state) {
261   auto values = GetRandomIntrinsic128Sample();
262   while (state.KeepRunningBatch(values.size())) {
263     for (const auto& pair : values) {
264       benchmark::DoNotOptimize(pair.first * pair.second);
265     }
266   }
267 }
268 BENCHMARK(BM_MultiplyIntrinsic128);
269 
BM_AddIntrinsic128(benchmark::State & state)270 void BM_AddIntrinsic128(benchmark::State& state) {
271   auto values = GetRandomIntrinsic128Sample();
272   while (state.KeepRunningBatch(values.size())) {
273     for (const auto& pair : values) {
274       benchmark::DoNotOptimize(pair.first + pair.second);
275     }
276   }
277 }
278 BENCHMARK(BM_AddIntrinsic128);
279 
280 #endif  // ABSL_HAVE_INTRINSIC_INT128
281 
282 }  // namespace
283