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