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