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