1*4bdff4beSrobert //===----------------------------------------------------------------------===//
2*4bdff4beSrobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3*4bdff4beSrobert // See https://llvm.org/LICENSE.txt for license information.
4*4bdff4beSrobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5*4bdff4beSrobert //
6*4bdff4beSrobert //===----------------------------------------------------------------------===//
7*4bdff4beSrobert
8*4bdff4beSrobert #include <array>
9*4bdff4beSrobert #include <format>
10*4bdff4beSrobert #include <random>
11*4bdff4beSrobert
12*4bdff4beSrobert #include "benchmark/benchmark.h"
13*4bdff4beSrobert #include "CartesianBenchmarks.h"
14*4bdff4beSrobert
15*4bdff4beSrobert // Tests the full range of the value.
16*4bdff4beSrobert template <class T>
17*4bdff4beSrobert static std::array<T, 1000>
generate(std::uniform_int_distribution<T> distribution=std::uniform_int_distribution<T>{ std::numeric_limits<T>::min(), std::numeric_limits<T>::max()})18*4bdff4beSrobert generate(std::uniform_int_distribution<T> distribution = std::uniform_int_distribution<T>{
19*4bdff4beSrobert std::numeric_limits<T>::min(), std::numeric_limits<T>::max()}) {
20*4bdff4beSrobert std::mt19937 generator;
21*4bdff4beSrobert std::array<T, 1000> result;
__anonb665c0690102null22*4bdff4beSrobert std::generate_n(result.begin(), result.size(), [&] { return distribution(generator); });
23*4bdff4beSrobert return result;
24*4bdff4beSrobert }
25*4bdff4beSrobert
26*4bdff4beSrobert template <class T>
BM_Basic(benchmark::State & state)27*4bdff4beSrobert static void BM_Basic(benchmark::State& state) {
28*4bdff4beSrobert std::array data{generate<T>()};
29*4bdff4beSrobert std::array<char, 100> output;
30*4bdff4beSrobert
31*4bdff4beSrobert while (state.KeepRunningBatch(data.size()))
32*4bdff4beSrobert for (auto value : data)
33*4bdff4beSrobert benchmark::DoNotOptimize(std::format_to(output.begin(), "{}", value));
34*4bdff4beSrobert }
35*4bdff4beSrobert BENCHMARK_TEMPLATE(BM_Basic, uint32_t);
36*4bdff4beSrobert BENCHMARK_TEMPLATE(BM_Basic, int32_t);
37*4bdff4beSrobert BENCHMARK_TEMPLATE(BM_Basic, uint64_t);
38*4bdff4beSrobert BENCHMARK_TEMPLATE(BM_Basic, int64_t);
39*4bdff4beSrobert
40*4bdff4beSrobert // Ideally the low values of a 128-bit value are all dispatched to a 64-bit routine.
41*4bdff4beSrobert template <class T>
BM_BasicLow(benchmark::State & state)42*4bdff4beSrobert static void BM_BasicLow(benchmark::State& state) {
43*4bdff4beSrobert using U = std::conditional_t<std::is_signed_v<T>, int64_t, uint64_t>;
44*4bdff4beSrobert std::array data{
45*4bdff4beSrobert generate<T>(std::uniform_int_distribution<T>{std::numeric_limits<U>::min(), std::numeric_limits<U>::max()})};
46*4bdff4beSrobert std::array<char, 100> output;
47*4bdff4beSrobert
48*4bdff4beSrobert while (state.KeepRunningBatch(data.size()))
49*4bdff4beSrobert for (auto value : data)
50*4bdff4beSrobert benchmark::DoNotOptimize(std::format_to(output.begin(), "{}", value));
51*4bdff4beSrobert }
52*4bdff4beSrobert BENCHMARK_TEMPLATE(BM_BasicLow, __uint128_t);
53*4bdff4beSrobert BENCHMARK_TEMPLATE(BM_BasicLow, __int128_t);
54*4bdff4beSrobert
55*4bdff4beSrobert BENCHMARK_TEMPLATE(BM_Basic, __uint128_t);
56*4bdff4beSrobert BENCHMARK_TEMPLATE(BM_Basic, __int128_t);
57*4bdff4beSrobert
58*4bdff4beSrobert // *** Localization ***
59*4bdff4beSrobert enum class LocalizationE { False, True };
60*4bdff4beSrobert struct AllLocalizations : EnumValuesAsTuple<AllLocalizations, LocalizationE, 2> {
61*4bdff4beSrobert static constexpr const char* Names[] = {"LocFalse", "LocTrue"};
62*4bdff4beSrobert };
63*4bdff4beSrobert
64*4bdff4beSrobert template <LocalizationE E>
65*4bdff4beSrobert struct Localization {};
66*4bdff4beSrobert
67*4bdff4beSrobert template <>
68*4bdff4beSrobert struct Localization<LocalizationE::False> {
69*4bdff4beSrobert static constexpr const char* fmt = "";
70*4bdff4beSrobert };
71*4bdff4beSrobert
72*4bdff4beSrobert template <>
73*4bdff4beSrobert struct Localization<LocalizationE::True> {
74*4bdff4beSrobert static constexpr const char* fmt = "L";
75*4bdff4beSrobert };
76*4bdff4beSrobert
77*4bdff4beSrobert // *** Base ***
78*4bdff4beSrobert enum class BaseE {
79*4bdff4beSrobert Binary,
80*4bdff4beSrobert Octal,
81*4bdff4beSrobert Decimal,
82*4bdff4beSrobert Hex,
83*4bdff4beSrobert HexUpper,
84*4bdff4beSrobert };
85*4bdff4beSrobert struct AllBases : EnumValuesAsTuple<AllBases, BaseE, 5> {
86*4bdff4beSrobert static constexpr const char* Names[] = {"BaseBin", "BaseOct", "BaseDec", "BaseHex", "BaseHexUpper"};
87*4bdff4beSrobert };
88*4bdff4beSrobert
89*4bdff4beSrobert template <BaseE E>
90*4bdff4beSrobert struct Base {};
91*4bdff4beSrobert
92*4bdff4beSrobert template <>
93*4bdff4beSrobert struct Base<BaseE::Binary> {
94*4bdff4beSrobert static constexpr const char* fmt = "b";
95*4bdff4beSrobert };
96*4bdff4beSrobert
97*4bdff4beSrobert template <>
98*4bdff4beSrobert struct Base<BaseE::Octal> {
99*4bdff4beSrobert static constexpr const char* fmt = "o";
100*4bdff4beSrobert };
101*4bdff4beSrobert
102*4bdff4beSrobert template <>
103*4bdff4beSrobert struct Base<BaseE::Decimal> {
104*4bdff4beSrobert static constexpr const char* fmt = "d";
105*4bdff4beSrobert };
106*4bdff4beSrobert
107*4bdff4beSrobert template <>
108*4bdff4beSrobert struct Base<BaseE::Hex> {
109*4bdff4beSrobert static constexpr const char* fmt = "x";
110*4bdff4beSrobert };
111*4bdff4beSrobert
112*4bdff4beSrobert template <>
113*4bdff4beSrobert struct Base<BaseE::HexUpper> {
114*4bdff4beSrobert static constexpr const char* fmt = "X";
115*4bdff4beSrobert };
116*4bdff4beSrobert
117*4bdff4beSrobert // *** Types ***
118*4bdff4beSrobert enum class TypeE { Int64, Uint64 };
119*4bdff4beSrobert struct AllTypes : EnumValuesAsTuple<AllTypes, TypeE, 2> {
120*4bdff4beSrobert static constexpr const char* Names[] = {"Int64", "Uint64"};
121*4bdff4beSrobert };
122*4bdff4beSrobert
123*4bdff4beSrobert template <TypeE E>
124*4bdff4beSrobert struct Type {};
125*4bdff4beSrobert
126*4bdff4beSrobert template <>
127*4bdff4beSrobert struct Type<TypeE::Int64> {
128*4bdff4beSrobert using type = int64_t;
129*4bdff4beSrobert
make_dataType130*4bdff4beSrobert static std::array<type, 1000> make_data() { return generate<type>(); }
131*4bdff4beSrobert };
132*4bdff4beSrobert
133*4bdff4beSrobert template <>
134*4bdff4beSrobert struct Type<TypeE::Uint64> {
135*4bdff4beSrobert using type = uint64_t;
136*4bdff4beSrobert
make_dataType137*4bdff4beSrobert static std::array<type, 1000> make_data() { return generate<type>(); }
138*4bdff4beSrobert };
139*4bdff4beSrobert
140*4bdff4beSrobert // *** Alignment ***
141*4bdff4beSrobert enum class AlignmentE { None, Left, Center, Right, ZeroPadding };
142*4bdff4beSrobert struct AllAlignments : EnumValuesAsTuple<AllAlignments, AlignmentE, 5> {
143*4bdff4beSrobert static constexpr const char* Names[] = {
144*4bdff4beSrobert "AlignNone", "AlignmentLeft", "AlignmentCenter", "AlignmentRight", "ZeroPadding"};
145*4bdff4beSrobert };
146*4bdff4beSrobert
147*4bdff4beSrobert template <AlignmentE E>
148*4bdff4beSrobert struct Alignment {};
149*4bdff4beSrobert
150*4bdff4beSrobert template <>
151*4bdff4beSrobert struct Alignment<AlignmentE::None> {
152*4bdff4beSrobert static constexpr const char* fmt = "";
153*4bdff4beSrobert };
154*4bdff4beSrobert
155*4bdff4beSrobert template <>
156*4bdff4beSrobert struct Alignment<AlignmentE::Left> {
157*4bdff4beSrobert static constexpr const char* fmt = "0<512";
158*4bdff4beSrobert };
159*4bdff4beSrobert
160*4bdff4beSrobert template <>
161*4bdff4beSrobert struct Alignment<AlignmentE::Center> {
162*4bdff4beSrobert static constexpr const char* fmt = "0^512";
163*4bdff4beSrobert };
164*4bdff4beSrobert
165*4bdff4beSrobert template <>
166*4bdff4beSrobert struct Alignment<AlignmentE::Right> {
167*4bdff4beSrobert static constexpr const char* fmt = "0>512";
168*4bdff4beSrobert };
169*4bdff4beSrobert
170*4bdff4beSrobert template <>
171*4bdff4beSrobert struct Alignment<AlignmentE::ZeroPadding> {
172*4bdff4beSrobert static constexpr const char* fmt = "0512";
173*4bdff4beSrobert };
174*4bdff4beSrobert
175*4bdff4beSrobert template <class L, class B, class T, class A>
176*4bdff4beSrobert struct Integral {
runIntegral177*4bdff4beSrobert void run(benchmark::State& state) const {
178*4bdff4beSrobert std::array data{Type<T::value>::make_data()};
179*4bdff4beSrobert std::array<char, 512> output;
180*4bdff4beSrobert
181*4bdff4beSrobert while (state.KeepRunningBatch(data.size()))
182*4bdff4beSrobert for (auto value : data)
183*4bdff4beSrobert benchmark::DoNotOptimize(std::format_to(output.begin(), std::string_view{fmt.data(), fmt.size()}, value));
184*4bdff4beSrobert }
185*4bdff4beSrobert
nameIntegral186*4bdff4beSrobert std::string name() const { return "Integral" + L::name() + B::name() + A::name() + T::name(); }
187*4bdff4beSrobert
make_fmtIntegral188*4bdff4beSrobert static constexpr std::string make_fmt() {
189*4bdff4beSrobert return std::string("{:") + Alignment<A::value>::fmt + Localization<L::value>::fmt + Base<B::value>::fmt + "}";
190*4bdff4beSrobert }
191*4bdff4beSrobert
__anonb665c0690202Integral192*4bdff4beSrobert static constexpr auto fmt = []() {
193*4bdff4beSrobert constexpr size_t s = make_fmt().size();
194*4bdff4beSrobert std::array<char, s> r;
195*4bdff4beSrobert std::ranges::copy(make_fmt(), r.begin());
196*4bdff4beSrobert return r;
197*4bdff4beSrobert }();
198*4bdff4beSrobert };
199*4bdff4beSrobert
main(int argc,char ** argv)200*4bdff4beSrobert int main(int argc, char** argv) {
201*4bdff4beSrobert benchmark::Initialize(&argc, argv);
202*4bdff4beSrobert if (benchmark::ReportUnrecognizedArguments(argc, argv))
203*4bdff4beSrobert return 1;
204*4bdff4beSrobert
205*4bdff4beSrobert makeCartesianProductBenchmark<Integral, AllLocalizations, AllBases, AllTypes, AllAlignments>();
206*4bdff4beSrobert
207*4bdff4beSrobert benchmark::RunSpecifiedBenchmarks();
208*4bdff4beSrobert }
209