[flang][runtime] Make defined formatted I/O process format elementally (#74150)
[llvm-project.git] / libcxx / benchmarks / formatter_float.bench.cpp
blobe7a673bb5d6553f0ab1743a60cfcf4882dd6b2e0
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 //===----------------------------------------------------------------------===//
8 #include <format>
10 #include <array>
11 #include <limits>
12 #include <random>
13 #include <string>
15 #include "CartesianBenchmarks.h"
16 #include "benchmark/benchmark.h"
18 // *** Localization ***
19 enum class LocalizationE { False, True };
20 struct AllLocalizations : EnumValuesAsTuple<AllLocalizations, LocalizationE, 2> {
21 static constexpr const char* Names[] = {"LocFalse", "LocTrue"};
24 template <LocalizationE E>
25 struct Localization {};
27 template <>
28 struct Localization<LocalizationE::False> {
29 static constexpr const char* fmt = "";
32 template <>
33 struct Localization<LocalizationE::True> {
34 static constexpr const char* fmt = "L";
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"};
44 template <TypeE E>
45 struct Type {};
47 template <>
48 struct Type<TypeE::Float> {
49 using type = float;
52 template <>
53 struct Type<TypeE::Double> {
54 using type = double;
57 template <>
58 struct Type<TypeE::LongDouble> {
59 using type = long double;
62 // *** Values ***
63 enum class ValueE { Inf, Random };
64 struct AllValues : EnumValuesAsTuple<AllValues, ValueE, 2> {
65 static constexpr const char* Names[] = {"Inf", "Random"};
68 template <ValueE E>
69 struct Value {};
71 template <>
72 struct 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;
81 template <>
82 struct 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;
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;
96 });
97 return result;
101 // *** Display Type ***
102 enum class DisplayTypeE {
103 Default,
104 Hex,
105 Scientific,
106 Fixed,
107 General,
109 struct AllDisplayTypes : EnumValuesAsTuple<AllDisplayTypes, DisplayTypeE, 5> {
110 static constexpr const char* Names[] = {
111 "DisplayDefault", "DisplayHex", "DisplayScientific", "DisplayFixed", "DisplayGeneral"};
114 template <DisplayTypeE E>
115 struct DisplayType {};
117 template <>
118 struct DisplayType<DisplayTypeE::Default> {
119 static constexpr const char* fmt = "";
122 template <>
123 struct DisplayType<DisplayTypeE::Hex> {
124 static constexpr const char* fmt = "a";
127 template <>
128 struct DisplayType<DisplayTypeE::Scientific> {
129 static constexpr const char* fmt = "e";
132 template <>
133 struct DisplayType<DisplayTypeE::Fixed> {
134 static constexpr const char* fmt = "f";
137 template <>
138 struct DisplayType<DisplayTypeE::General> {
139 static constexpr const char* fmt = "g";
142 // *** Alignment ***
143 enum class AlignmentE { None, Left, Center, Right, ZeroPadding };
144 struct AllAlignments : EnumValuesAsTuple<AllAlignments, AlignmentE, 5> {
145 static constexpr const char* Names[] = {
146 "AlignNone", "AlignmentLeft", "AlignmentCenter", "AlignmentRight", "ZeroPadding"};
149 template <AlignmentE E>
150 struct Alignment {};
152 template <>
153 struct Alignment<AlignmentE::None> {
154 static constexpr const char* fmt = "";
157 template <>
158 struct Alignment<AlignmentE::Left> {
159 // Width > PrecisionE::Huge
160 static constexpr const char* fmt = "0<17500";
163 template <>
164 struct Alignment<AlignmentE::Center> {
165 // Width > PrecisionE::Huge
166 static constexpr const char* fmt = "0^17500";
169 template <>
170 struct Alignment<AlignmentE::Right> {
171 // Width > PrecisionE::Huge
172 static constexpr const char* fmt = "0>17500";
175 template <>
176 struct Alignment<AlignmentE::ZeroPadding> {
177 // Width > PrecisionE::Huge
178 static constexpr const char* fmt = "017500";
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"};
186 template <PrecisionE E>
187 struct Precision {};
189 template <>
190 struct Precision<PrecisionE::None> {
191 static constexpr const char* fmt = "";
194 template <>
195 struct Precision<PrecisionE::Zero> {
196 static constexpr const char* fmt = ".0";
199 template <>
200 struct Precision<PrecisionE::Small> {
201 static constexpr const char* fmt = ".10";
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";
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;
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;
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));
225 std::string name() const {
226 return "FloatingPoint" + L::name() + DT::name() + T::name() + V::name() + A::name() + P::name();
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 + "}";
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 }();
242 int main(int argc, char** argv) {
243 benchmark::Initialize(&argc, argv);
244 if (benchmark::ReportUnrecognizedArguments(argc, argv))
245 return 1;
247 makeCartesianProductBenchmark<FloatingPoint,
248 AllLocalizations,
249 AllDisplayTypes,
250 AllTypes,
251 AllValues,
252 AllAlignments,
253 AllPrecisions>();
255 benchmark::RunSpecifiedBenchmarks();