xref: /openbsd-src/gnu/llvm/libcxx/benchmarks/formatter_float.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 <format>
9 
10 #include <array>
11 #include <limits>
12 #include <random>
13 #include <string>
14 
15 #include "CartesianBenchmarks.h"
16 #include "benchmark/benchmark.h"
17 
18 // *** Localization ***
19 enum class LocalizationE { False, True };
20 struct AllLocalizations : EnumValuesAsTuple<AllLocalizations, LocalizationE, 2> {
21   static constexpr const char* Names[] = {"LocFalse", "LocTrue"};
22 };
23 
24 template <LocalizationE E>
25 struct Localization {};
26 
27 template <>
28 struct Localization<LocalizationE::False> {
29   static constexpr const char* fmt = "";
30 };
31 
32 template <>
33 struct Localization<LocalizationE::True> {
34   static constexpr const char* fmt = "L";
35 };
36 
37 // *** Types ***
38 enum class TypeE { Float, Double, LongDouble };
39 // TODO FMT Set to 3 after to_chars has long double suport.
40 struct AllTypes : EnumValuesAsTuple<AllTypes, TypeE, 2> {
41   static constexpr const char* Names[] = {"Float", "Double", "LongDouble"};
42 };
43 
44 template <TypeE E>
45 struct Type {};
46 
47 template <>
48 struct Type<TypeE::Float> {
49   using type = float;
50 };
51 
52 template <>
53 struct Type<TypeE::Double> {
54   using type = double;
55 };
56 
57 template <>
58 struct Type<TypeE::LongDouble> {
59   using type = long double;
60 };
61 
62 // *** Values ***
63 enum class ValueE { Inf, Random };
64 struct AllValues : EnumValuesAsTuple<AllValues, ValueE, 2> {
65   static constexpr const char* Names[] = {"Inf", "Random"};
66 };
67 
68 template <ValueE E>
69 struct Value {};
70 
71 template <>
72 struct Value<ValueE::Inf> {
73   template <class F>
make_dataValue74   static std::array<F, 1000> make_data() {
75     std::array<F, 1000> result;
76     std::fill(result.begin(), result.end(), -std::numeric_limits<F>::infinity());
77     return result;
78   }
79 };
80 
81 template <>
82 struct Value<ValueE::Random> {
83   template <class F>
make_dataValue84   static std::array<F, 1000> make_data() {
85     std::random_device seed;
86     std::mt19937 generator(seed());
87     std::uniform_int_distribution<std::conditional_t<sizeof(F) == sizeof(uint32_t), uint32_t, uint64_t>> distribution;
88 
89     std::array<F, 1000> result;
90     std::generate(result.begin(), result.end(), [&] {
91       while (true) {
92         auto result = std::bit_cast<F>(distribution(generator));
93         if (std::isfinite(result))
94           return result;
95       }
96     });
97     return result;
98   }
99 };
100 
101 // *** Display Type ***
102 enum class DisplayTypeE {
103   Default,
104   Hex,
105   Scientific,
106   Fixed,
107   General,
108 };
109 struct AllDisplayTypes : EnumValuesAsTuple<AllDisplayTypes, DisplayTypeE, 5> {
110   static constexpr const char* Names[] = {"DisplayDefault", "DisplayHex", "DisplayScientific", "DisplayFixed",
111                                           "DisplayGeneral"};
112 };
113 
114 template <DisplayTypeE E>
115 struct DisplayType {};
116 
117 template <>
118 struct DisplayType<DisplayTypeE::Default> {
119   static constexpr const char* fmt = "";
120 };
121 
122 template <>
123 struct DisplayType<DisplayTypeE::Hex> {
124   static constexpr const char* fmt = "a";
125 };
126 
127 template <>
128 struct DisplayType<DisplayTypeE::Scientific> {
129   static constexpr const char* fmt = "e";
130 };
131 
132 template <>
133 struct DisplayType<DisplayTypeE::Fixed> {
134   static constexpr const char* fmt = "f";
135 };
136 
137 template <>
138 struct DisplayType<DisplayTypeE::General> {
139   static constexpr const char* fmt = "g";
140 };
141 
142 // *** Alignment ***
143 enum class AlignmentE { None, Left, Center, Right, ZeroPadding };
144 struct AllAlignments : EnumValuesAsTuple<AllAlignments, AlignmentE, 5> {
145   static constexpr const char* Names[] = {"AlignNone", "AlignmentLeft", "AlignmentCenter", "AlignmentRight",
146                                           "ZeroPadding"};
147 };
148 
149 template <AlignmentE E>
150 struct Alignment {};
151 
152 template <>
153 struct Alignment<AlignmentE::None> {
154   static constexpr const char* fmt = "";
155 };
156 
157 template <>
158 struct Alignment<AlignmentE::Left> {
159   // Width > PrecisionE::Huge
160   static constexpr const char* fmt = "0<17500";
161 };
162 
163 template <>
164 struct Alignment<AlignmentE::Center> {
165   // Width > PrecisionE::Huge
166   static constexpr const char* fmt = "0^17500";
167 };
168 
169 template <>
170 struct Alignment<AlignmentE::Right> {
171   // Width > PrecisionE::Huge
172   static constexpr const char* fmt = "0>17500";
173 };
174 
175 template <>
176 struct Alignment<AlignmentE::ZeroPadding> {
177   // Width > PrecisionE::Huge
178   static constexpr const char* fmt = "017500";
179 };
180 
181 enum class PrecisionE { None, Zero, Small, Huge };
182 struct AllPrecisions : EnumValuesAsTuple<AllPrecisions, PrecisionE, 4> {
183   static constexpr const char* Names[] = {"PrecNone", "PrecZero", "PrecSmall", "PrecHuge"};
184 };
185 
186 template <PrecisionE E>
187 struct Precision {};
188 
189 template <>
190 struct Precision<PrecisionE::None> {
191   static constexpr const char* fmt = "";
192 };
193 
194 template <>
195 struct Precision<PrecisionE::Zero> {
196   static constexpr const char* fmt = ".0";
197 };
198 
199 template <>
200 struct Precision<PrecisionE::Small> {
201   static constexpr const char* fmt = ".10";
202 };
203 
204 template <>
205 struct Precision<PrecisionE::Huge> {
206   // The maximum precision for a minimal sub normal long double is +/- 0x1p-16494.
207   // This value is always larger than that value forcing the trailing zero path
208   // to be executed.
209   static constexpr const char* fmt = ".17000";
210 };
211 
212 template <class L, class DT, class T, class V, class A, class P>
213 struct FloatingPoint {
214   using F = typename Type<T::value>::type;
215 
runFloatingPoint216   void run(benchmark::State& state) const {
217     std::array<F, 1000> data{Value<V::value>::template make_data<F>()};
218     std::array<char, 20'000> output;
219 
220     while (state.KeepRunningBatch(1000))
221       for (F value : data)
222         benchmark::DoNotOptimize(std::format_to(output.begin(), std::string_view{fmt.data(), fmt.size()}, value));
223   }
224 
nameFloatingPoint225   std::string name() const {
226     return "FloatingPoint" + L::name() + DT::name() + T::name() + V::name() + A::name() + P::name();
227   }
228 
make_fmtFloatingPoint229   static constexpr std::string make_fmt() {
230     return std::string("{:") + Alignment<A::value>::fmt + Precision<P::value>::fmt + Localization<L::value>::fmt +
231            DisplayType<DT::value>::fmt + "}";
232   }
233 
__anone8c412140202FloatingPoint234   static constexpr auto fmt = []() {
235     constexpr size_t s = make_fmt().size();
236     std::array<char, s> r;
237     std::ranges::copy(make_fmt(), r.begin());
238     return r;
239   }();
240 };
241 
main(int argc,char ** argv)242 int main(int argc, char** argv) {
243   benchmark::Initialize(&argc, argv);
244   if (benchmark::ReportUnrecognizedArguments(argc, argv))
245     return 1;
246 
247   makeCartesianProductBenchmark<FloatingPoint, AllLocalizations, AllDisplayTypes, AllTypes, AllValues, AllAlignments,
248                                 AllPrecisions>();
249 
250   benchmark::RunSpecifiedBenchmarks();
251 }
252