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

source code of libcxx/test/benchmarks/format/formatter_float.bench.cpp