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 <format> 9 10 #include <array> 11 #include <limits> 12 #include <random> 13 #include <string> 14 15 #include "CartesianBenchmarks.h" 16 #include "benchmark/benchmark.h" 17 18 // *** Localization *** 19 enum class LocalizationE { False, True }; 20 struct AllLocalizations : EnumValuesAsTuple<AllLocalizations, LocalizationE, 2> { 21 static constexpr const char* Names[] = {"LocFalse", "LocTrue"}; 22 }; 23 24 template <LocalizationE E> 25 struct Localization {}; 26 27 template <> 28 struct Localization<LocalizationE::False> { 29 static constexpr const char* fmt = ""; 30 }; 31 32 template <> 33 struct Localization<LocalizationE::True> { 34 static constexpr const char* fmt = "L"; 35 }; 36 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"}; 42 }; 43 44 template <TypeE E> 45 struct Type {}; 46 47 template <> 48 struct Type<TypeE::Float> { 49 using type = float; 50 }; 51 52 template <> 53 struct Type<TypeE::Double> { 54 using type = double; 55 }; 56 57 template <> 58 struct Type<TypeE::LongDouble> { 59 using type = long double; 60 }; 61 62 // *** Values *** 63 enum class ValueE { Inf, Random }; 64 struct AllValues : EnumValuesAsTuple<AllValues, ValueE, 2> { 65 static constexpr const char* Names[] = {"Inf", "Random"}; 66 }; 67 68 template <ValueE E> 69 struct Value {}; 70 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; 78 } 79 }; 80 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; 88 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; 95 } 96 }); 97 return result; 98 } 99 }; 100 101 // *** Display Type *** 102 enum class DisplayTypeE { 103 Default, 104 Hex, 105 Scientific, 106 Fixed, 107 General, 108 }; 109 struct AllDisplayTypes : EnumValuesAsTuple<AllDisplayTypes, DisplayTypeE, 5> { 110 static constexpr const char* Names[] = {"DisplayDefault", "DisplayHex", "DisplayScientific", "DisplayFixed", 111 "DisplayGeneral"}; 112 }; 113 114 template <DisplayTypeE E> 115 struct DisplayType {}; 116 117 template <> 118 struct DisplayType<DisplayTypeE::Default> { 119 static constexpr const char* fmt = ""; 120 }; 121 122 template <> 123 struct DisplayType<DisplayTypeE::Hex> { 124 static constexpr const char* fmt = "a"; 125 }; 126 127 template <> 128 struct DisplayType<DisplayTypeE::Scientific> { 129 static constexpr const char* fmt = "e"; 130 }; 131 132 template <> 133 struct DisplayType<DisplayTypeE::Fixed> { 134 static constexpr const char* fmt = "f"; 135 }; 136 137 template <> 138 struct DisplayType<DisplayTypeE::General> { 139 static constexpr const char* fmt = "g"; 140 }; 141 142 // *** Alignment *** 143 enum class AlignmentE { None, Left, Center, Right, ZeroPadding }; 144 struct AllAlignments : EnumValuesAsTuple<AllAlignments, AlignmentE, 5> { 145 static constexpr const char* Names[] = {"AlignNone", "AlignmentLeft", "AlignmentCenter", "AlignmentRight", 146 "ZeroPadding"}; 147 }; 148 149 template <AlignmentE E> 150 struct Alignment {}; 151 152 template <> 153 struct Alignment<AlignmentE::None> { 154 static constexpr const char* fmt = ""; 155 }; 156 157 template <> 158 struct Alignment<AlignmentE::Left> { 159 // Width > PrecisionE::Huge 160 static constexpr const char* fmt = "0<17500"; 161 }; 162 163 template <> 164 struct Alignment<AlignmentE::Center> { 165 // Width > PrecisionE::Huge 166 static constexpr const char* fmt = "0^17500"; 167 }; 168 169 template <> 170 struct Alignment<AlignmentE::Right> { 171 // Width > PrecisionE::Huge 172 static constexpr const char* fmt = "0>17500"; 173 }; 174 175 template <> 176 struct Alignment<AlignmentE::ZeroPadding> { 177 // Width > PrecisionE::Huge 178 static constexpr const char* fmt = "017500"; 179 }; 180 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"}; 184 }; 185 186 template <PrecisionE E> 187 struct Precision {}; 188 189 template <> 190 struct Precision<PrecisionE::None> { 191 static constexpr const char* fmt = ""; 192 }; 193 194 template <> 195 struct Precision<PrecisionE::Zero> { 196 static constexpr const char* fmt = ".0"; 197 }; 198 199 template <> 200 struct Precision<PrecisionE::Small> { 201 static constexpr const char* fmt = ".10"; 202 }; 203 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"; 210 }; 211 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; 215 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; 219 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)); 223 } 224 225 std::string name() const { 226 return "FloatingPoint" + L::name() + DT::name() + T::name() + V::name() + A::name() + P::name(); 227 } 228 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 + "}"; 232 } 233 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 }(); 240 }; 241 242 int main(int argc, char** argv) { 243 benchmark::Initialize(&argc, argv); 244 if (benchmark::ReportUnrecognizedArguments(argc, argv)) 245 return 1; 246 247 makeCartesianProductBenchmark<FloatingPoint, AllLocalizations, AllDisplayTypes, AllTypes, AllValues, AllAlignments, 248 AllPrecisions>(); 249 250 benchmark::RunSpecifiedBenchmarks(); 251 } 252