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 <array>
9#include <format>
10#include <random>
11
12#include "CartesianBenchmarks.h"
13#include "benchmark/benchmark.h"
14
15// Tests the full range of the value.
16template <class T>
17static std::array<T, 1000> generate(std::uniform_int_distribution<T> distribution = std::uniform_int_distribution<T>{
18 std::numeric_limits<T>::min(), std::numeric_limits<T>::max()}) {
19 std::mt19937 generator;
20 std::array<T, 1000> result;
21 std::generate_n(result.begin(), result.size(), [&] { return distribution(generator); });
22 return result;
23}
24
25template <class T>
26static void BM_Basic(benchmark::State& state) {
27 std::array data{generate<T>()};
28 std::array<char, 100> output;
29
30 while (state.KeepRunningBatch(data.size()))
31 for (auto value : data)
32 benchmark::DoNotOptimize(std::format_to(output.begin(), "{}", value));
33}
34BENCHMARK_TEMPLATE(BM_Basic, uint32_t);
35BENCHMARK_TEMPLATE(BM_Basic, int32_t);
36BENCHMARK_TEMPLATE(BM_Basic, uint64_t);
37BENCHMARK_TEMPLATE(BM_Basic, int64_t);
38
39// Ideally the low values of a 128-bit value are all dispatched to a 64-bit routine.
40template <class T>
41static void BM_BasicLow(benchmark::State& state) {
42 using U = std::conditional_t<std::is_signed_v<T>, int64_t, uint64_t>;
43 std::array data{
44 generate<T>(std::uniform_int_distribution<T>{std::numeric_limits<U>::min(), std::numeric_limits<U>::max()})};
45 std::array<char, 100> output;
46
47 while (state.KeepRunningBatch(data.size()))
48 for (auto value : data)
49 benchmark::DoNotOptimize(std::format_to(output.begin(), "{}", value));
50}
51BENCHMARK_TEMPLATE(BM_BasicLow, __uint128_t);
52BENCHMARK_TEMPLATE(BM_BasicLow, __int128_t);
53
54BENCHMARK_TEMPLATE(BM_Basic, __uint128_t);
55BENCHMARK_TEMPLATE(BM_Basic, __int128_t);
56
57// *** Localization ***
58enum class LocalizationE { False, True };
59struct AllLocalizations : EnumValuesAsTuple<AllLocalizations, LocalizationE, 2> {
60 static constexpr const char* Names[] = {"LocFalse", "LocTrue"};
61};
62
63template <LocalizationE E>
64struct Localization {};
65
66template <>
67struct Localization<LocalizationE::False> {
68 static constexpr const char* fmt = "";
69};
70
71template <>
72struct Localization<LocalizationE::True> {
73 static constexpr const char* fmt = "L";
74};
75
76// *** Base ***
77enum class BaseE {
78 Binary,
79 Octal,
80 Decimal,
81 Hex,
82 HexUpper,
83};
84struct AllBases : EnumValuesAsTuple<AllBases, BaseE, 5> {
85 static constexpr const char* Names[] = {"BaseBin", "BaseOct", "BaseDec", "BaseHex", "BaseHexUpper"};
86};
87
88template <BaseE E>
89struct Base {};
90
91template <>
92struct Base<BaseE::Binary> {
93 static constexpr const char* fmt = "b";
94};
95
96template <>
97struct Base<BaseE::Octal> {
98 static constexpr const char* fmt = "o";
99};
100
101template <>
102struct Base<BaseE::Decimal> {
103 static constexpr const char* fmt = "d";
104};
105
106template <>
107struct Base<BaseE::Hex> {
108 static constexpr const char* fmt = "x";
109};
110
111template <>
112struct Base<BaseE::HexUpper> {
113 static constexpr const char* fmt = "X";
114};
115
116// *** Types ***
117enum class TypeE { Int64, Uint64 };
118struct AllTypes : EnumValuesAsTuple<AllTypes, TypeE, 2> {
119 static constexpr const char* Names[] = {"Int64", "Uint64"};
120};
121
122template <TypeE E>
123struct Type {};
124
125template <>
126struct Type<TypeE::Int64> {
127 using type = int64_t;
128
129 static std::array<type, 1000> make_data() { return generate<type>(); }
130};
131
132template <>
133struct Type<TypeE::Uint64> {
134 using type = uint64_t;
135
136 static std::array<type, 1000> make_data() { return generate<type>(); }
137};
138
139// *** Alignment ***
140enum class AlignmentE { None, Left, Center, Right, ZeroPadding };
141struct AllAlignments : EnumValuesAsTuple<AllAlignments, AlignmentE, 5> {
142 static constexpr const char* Names[] = {
143 "AlignNone", "AlignmentLeft", "AlignmentCenter", "AlignmentRight", "ZeroPadding"};
144};
145
146template <AlignmentE E>
147struct Alignment {};
148
149template <>
150struct Alignment<AlignmentE::None> {
151 static constexpr const char* fmt = "";
152};
153
154template <>
155struct Alignment<AlignmentE::Left> {
156 static constexpr const char* fmt = "0<512";
157};
158
159template <>
160struct Alignment<AlignmentE::Center> {
161 static constexpr const char* fmt = "0^512";
162};
163
164template <>
165struct Alignment<AlignmentE::Right> {
166 static constexpr const char* fmt = "0>512";
167};
168
169template <>
170struct Alignment<AlignmentE::ZeroPadding> {
171 static constexpr const char* fmt = "0512";
172};
173
174template <class L, class B, class T, class A>
175struct Integral {
176 void run(benchmark::State& state) const {
177 std::array data{Type<T::value>::make_data()};
178 std::array<char, 512> output;
179
180 while (state.KeepRunningBatch(data.size()))
181 for (auto value : data)
182 benchmark::DoNotOptimize(std::format_to(output.begin(), std::string_view{fmt.data(), fmt.size()}, value));
183 }
184
185 std::string name() const { return "Integral" + L::name() + B::name() + A::name() + T::name(); }
186
187 static constexpr std::string make_fmt() {
188 return std::string("{:") + Alignment<A::value>::fmt + Localization<L::value>::fmt + Base<B::value>::fmt + "}";
189 }
190
191 static constexpr auto fmt = []() {
192 constexpr size_t s = make_fmt().size();
193 std::array<char, s> r;
194 std::ranges::copy(make_fmt(), r.begin());
195 return r;
196 }();
197};
198
199int main(int argc, char** argv) {
200 benchmark::Initialize(&argc, argv);
201 if (benchmark::ReportUnrecognizedArguments(argc, argv))
202 return 1;
203
204 makeCartesianProductBenchmark<Integral, AllLocalizations, AllBases, AllTypes, AllAlignments>();
205
206 benchmark::RunSpecifiedBenchmarks();
207}
208