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 ***
19enum class LocalizationE { False, True };
20struct AllLocalizations : EnumValuesAsTuple<AllLocalizations, LocalizationE, 2> {
21 static constexpr const char* Names[] = {"LocFalse", "LocTrue"};
22};
23
24template <LocalizationE E>
25struct Localization {};
26
27template <>
28struct Localization<LocalizationE::False> {
29 static constexpr const char* fmt = "";
30};
31
32template <>
33struct Localization<LocalizationE::True> {
34 static constexpr const char* fmt = "L";
35};
36
37// *** Types ***
38enum class TypeE { Float, Double, LongDouble };
39// TODO FMT Set to 3 after to_chars has long double suport.
40struct AllTypes : EnumValuesAsTuple<AllTypes, TypeE, 2> {
41 static constexpr const char* Names[] = {"Float", "Double", "LongDouble"};
42};
43
44template <TypeE E>
45struct Type {};
46
47template <>
48struct Type<TypeE::Float> {
49 using type = float;
50};
51
52template <>
53struct Type<TypeE::Double> {
54 using type = double;
55};
56
57template <>
58struct Type<TypeE::LongDouble> {
59 using type = long double;
60};
61
62// *** Values ***
63enum class ValueE { Inf, Random };
64struct AllValues : EnumValuesAsTuple<AllValues, ValueE, 2> {
65 static constexpr const char* Names[] = {"Inf", "Random"};
66};
67
68template <ValueE E>
69struct Value {};
70
71template <>
72struct Value<ValueE::Inf> {
73 template <class F>
74 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
81template <>
82struct Value<ValueE::Random> {
83 template <class F>
84 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 ***
102enum class DisplayTypeE {
103 Default,
104 Hex,
105 Scientific,
106 Fixed,
107 General,
108};
109struct AllDisplayTypes : EnumValuesAsTuple<AllDisplayTypes, DisplayTypeE, 5> {
110 static constexpr const char* Names[] = {
111 "DisplayDefault", "DisplayHex", "DisplayScientific", "DisplayFixed", "DisplayGeneral"};
112};
113
114template <DisplayTypeE E>
115struct DisplayType {};
116
117template <>
118struct DisplayType<DisplayTypeE::Default> {
119 static constexpr const char* fmt = "";
120};
121
122template <>
123struct DisplayType<DisplayTypeE::Hex> {
124 static constexpr const char* fmt = "a";
125};
126
127template <>
128struct DisplayType<DisplayTypeE::Scientific> {
129 static constexpr const char* fmt = "e";
130};
131
132template <>
133struct DisplayType<DisplayTypeE::Fixed> {
134 static constexpr const char* fmt = "f";
135};
136
137template <>
138struct DisplayType<DisplayTypeE::General> {
139 static constexpr const char* fmt = "g";
140};
141
142// *** Alignment ***
143enum class AlignmentE { None, Left, Center, Right, ZeroPadding };
144struct AllAlignments : EnumValuesAsTuple<AllAlignments, AlignmentE, 5> {
145 static constexpr const char* Names[] = {
146 "AlignNone", "AlignmentLeft", "AlignmentCenter", "AlignmentRight", "ZeroPadding"};
147};
148
149template <AlignmentE E>
150struct Alignment {};
151
152template <>
153struct Alignment<AlignmentE::None> {
154 static constexpr const char* fmt = "";
155};
156
157template <>
158struct Alignment<AlignmentE::Left> {
159 // Width > PrecisionE::Huge
160 static constexpr const char* fmt = "0<17500";
161};
162
163template <>
164struct Alignment<AlignmentE::Center> {
165 // Width > PrecisionE::Huge
166 static constexpr const char* fmt = "0^17500";
167};
168
169template <>
170struct Alignment<AlignmentE::Right> {
171 // Width > PrecisionE::Huge
172 static constexpr const char* fmt = "0>17500";
173};
174
175template <>
176struct Alignment<AlignmentE::ZeroPadding> {
177 // Width > PrecisionE::Huge
178 static constexpr const char* fmt = "017500";
179};
180
181enum class PrecisionE { None, Zero, Small, Huge };
182struct AllPrecisions : EnumValuesAsTuple<AllPrecisions, PrecisionE, 4> {
183 static constexpr const char* Names[] = {"PrecNone", "PrecZero", "PrecSmall", "PrecHuge"};
184};
185
186template <PrecisionE E>
187struct Precision {};
188
189template <>
190struct Precision<PrecisionE::None> {
191 static constexpr const char* fmt = "";
192};
193
194template <>
195struct Precision<PrecisionE::Zero> {
196 static constexpr const char* fmt = ".0";
197};
198
199template <>
200struct Precision<PrecisionE::Small> {
201 static constexpr const char* fmt = ".10";
202};
203
204template <>
205struct 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
212template <class L, class DT, class T, class V, class A, class P>
213struct FloatingPoint {
214 using F = typename Type<T::value>::type;
215
216 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
225 std::string name() const {
226 return "FloatingPoint" + L::name() + DT::name() + T::name() + V::name() + A::name() + P::name();
227 }
228
229 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
234 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
242int main(int argc, char** argv) {
243 benchmark::Initialize(&argc, argv);
244 if (benchmark::ReportUnrecognizedArguments(argc, argv))
245 return 1;
246
247 makeCartesianProductBenchmark<FloatingPoint,
248 AllLocalizations,
249 AllDisplayTypes,
250 AllTypes,
251 AllValues,
252 AllAlignments,
253 AllPrecisions>();
254
255 benchmark::RunSpecifiedBenchmarks();
256}
257