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