forked from OSchip/llvm-project
242 lines
6.3 KiB
C++
242 lines
6.3 KiB
C++
//===----------------------------------------------------------------------===//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include <format>
|
|
|
|
#include <array>
|
|
#include <limits>
|
|
#include <random>
|
|
#include <string>
|
|
|
|
#include "CartesianBenchmarks.h"
|
|
#include "benchmark/benchmark.h"
|
|
|
|
// *** Localization ***
|
|
enum class LocalizationE { False, True };
|
|
struct AllLocalizations : EnumValuesAsTuple<AllLocalizations, LocalizationE, 2> {
|
|
static constexpr const char* Names[] = {"LocFalse", "LocTrue"};
|
|
};
|
|
|
|
template <LocalizationE E>
|
|
struct Localization {};
|
|
|
|
template <>
|
|
struct Localization<LocalizationE::False> {
|
|
static constexpr const char* fmt = "";
|
|
};
|
|
|
|
template <>
|
|
struct Localization<LocalizationE::True> {
|
|
static constexpr const char* fmt = "L";
|
|
};
|
|
|
|
// *** Types ***
|
|
enum class TypeE { Float, Double, LongDouble };
|
|
// TODO FMT Set to 3 after to_chars has long double suport.
|
|
struct AllTypes : EnumValuesAsTuple<AllTypes, TypeE, 2> {
|
|
static constexpr const char* Names[] = {"Float", "Double", "LongDouble"};
|
|
};
|
|
|
|
template <TypeE E>
|
|
struct Type {};
|
|
|
|
template <>
|
|
struct Type<TypeE::Float> {
|
|
using type = float;
|
|
};
|
|
|
|
template <>
|
|
struct Type<TypeE::Double> {
|
|
using type = double;
|
|
};
|
|
|
|
template <>
|
|
struct Type<TypeE::LongDouble> {
|
|
using type = long double;
|
|
};
|
|
|
|
// *** Values ***
|
|
enum class ValueE { Inf, Random };
|
|
struct AllValues : EnumValuesAsTuple<AllValues, ValueE, 2> {
|
|
static constexpr const char* Names[] = {"Inf", "Random"};
|
|
};
|
|
|
|
template <ValueE E>
|
|
struct Value {};
|
|
|
|
template <>
|
|
struct Value<ValueE::Inf> {
|
|
template <class F>
|
|
static std::array<F, 1000> make_data() {
|
|
std::array<F, 1000> result;
|
|
std::fill(result.begin(), result.end(), -std::numeric_limits<F>::infinity());
|
|
return result;
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct Value<ValueE::Random> {
|
|
template <class F>
|
|
static std::array<F, 1000> make_data() {
|
|
std::random_device seed;
|
|
std::mt19937 generator(seed());
|
|
std::uniform_int_distribution<std::conditional_t<sizeof(F) == sizeof(uint32_t), uint32_t, uint64_t>> distribution;
|
|
|
|
std::array<F, 1000> result;
|
|
std::generate(result.begin(), result.end(), [&] {
|
|
while (true) {
|
|
auto result = std::bit_cast<F>(distribution(generator));
|
|
if (std::isfinite(result))
|
|
return result;
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
};
|
|
|
|
// *** Display Type ***
|
|
enum class DisplayTypeE {
|
|
Default,
|
|
Hex,
|
|
Scientific,
|
|
Fixed,
|
|
General,
|
|
};
|
|
struct AllDisplayTypes : EnumValuesAsTuple<AllDisplayTypes, DisplayTypeE, 5> {
|
|
static constexpr const char* Names[] = {"DisplayDefault", "DisplayHex", "DisplayScientific", "DisplayFixed",
|
|
"DisplayGeneral"};
|
|
};
|
|
|
|
template <DisplayTypeE E>
|
|
struct DisplayType {};
|
|
|
|
template <>
|
|
struct DisplayType<DisplayTypeE::Default> {
|
|
static constexpr const char* fmt = "";
|
|
};
|
|
|
|
template <>
|
|
struct DisplayType<DisplayTypeE::Hex> {
|
|
static constexpr const char* fmt = "a";
|
|
};
|
|
|
|
template <>
|
|
struct DisplayType<DisplayTypeE::Scientific> {
|
|
static constexpr const char* fmt = "e";
|
|
};
|
|
|
|
template <>
|
|
struct DisplayType<DisplayTypeE::Fixed> {
|
|
static constexpr const char* fmt = "f";
|
|
};
|
|
|
|
template <>
|
|
struct DisplayType<DisplayTypeE::General> {
|
|
static constexpr const char* fmt = "g";
|
|
};
|
|
|
|
// *** Alignment ***
|
|
enum class AlignmentE { None, Left, Center, Right, ZeroPadding };
|
|
struct AllAlignments : EnumValuesAsTuple<AllAlignments, AlignmentE, 5> {
|
|
static constexpr const char* Names[] = {"AlignNone", "AlignmentLeft", "AlignmentCenter", "AlignmentRight",
|
|
"ZeroPadding"};
|
|
};
|
|
|
|
template <AlignmentE E>
|
|
struct Alignment {};
|
|
|
|
template <>
|
|
struct Alignment<AlignmentE::None> {
|
|
static constexpr const char* fmt = "";
|
|
};
|
|
|
|
template <>
|
|
struct Alignment<AlignmentE::Left> {
|
|
// Width > PrecisionE::Huge
|
|
static constexpr const char* fmt = "0<17500";
|
|
};
|
|
|
|
template <>
|
|
struct Alignment<AlignmentE::Center> {
|
|
// Width > PrecisionE::Huge
|
|
static constexpr const char* fmt = "0^17500";
|
|
};
|
|
|
|
template <>
|
|
struct Alignment<AlignmentE::Right> {
|
|
// Width > PrecisionE::Huge
|
|
static constexpr const char* fmt = "0>17500";
|
|
};
|
|
|
|
template <>
|
|
struct Alignment<AlignmentE::ZeroPadding> {
|
|
// Width > PrecisionE::Huge
|
|
static constexpr const char* fmt = "017500";
|
|
};
|
|
|
|
enum class PrecisionE { None, Zero, Small, Huge };
|
|
struct AllPrecisions : EnumValuesAsTuple<AllPrecisions, PrecisionE, 4> {
|
|
static constexpr const char* Names[] = {"PrecNone", "PrecZero", "PrecSmall", "PrecHuge"};
|
|
};
|
|
|
|
template <PrecisionE E>
|
|
struct Precision {};
|
|
|
|
template <>
|
|
struct Precision<PrecisionE::None> {
|
|
static constexpr const char* fmt = "";
|
|
};
|
|
|
|
template <>
|
|
struct Precision<PrecisionE::Zero> {
|
|
static constexpr const char* fmt = ".0";
|
|
};
|
|
|
|
template <>
|
|
struct Precision<PrecisionE::Small> {
|
|
static constexpr const char* fmt = ".10";
|
|
};
|
|
|
|
template <>
|
|
struct Precision<PrecisionE::Huge> {
|
|
// The maximum precision for a minimal sub normal long double is ±0x1p-16494.
|
|
// This value is always larger than that value forcing the trailing zero path
|
|
// to be executed.
|
|
static constexpr const char* fmt = ".17000";
|
|
};
|
|
|
|
template <class L, class DT, class T, class V, class A, class P>
|
|
struct FloatingPoint {
|
|
using F = typename Type<T::value>::type;
|
|
|
|
void run(benchmark::State& state) const {
|
|
std::array<F, 1000> data{Value<V::value>::template make_data<F>()};
|
|
std::array<char, 20'000> output;
|
|
std::string fmt{std::string("{:") + Alignment<A::value>::fmt + Precision<P::value>::fmt +
|
|
Localization<L::value>::fmt + DisplayType<DT::value>::fmt + "}"};
|
|
|
|
while (state.KeepRunningBatch(1000))
|
|
for (F value : data)
|
|
benchmark::DoNotOptimize(std::format_to(output.begin(), fmt, value));
|
|
}
|
|
|
|
std::string name() const {
|
|
return "FloatingPoint" + L::name() + DT::name() + T::name() + V::name() + A::name() + P::name();
|
|
}
|
|
};
|
|
|
|
int main(int argc, char** argv) {
|
|
benchmark::Initialize(&argc, argv);
|
|
if (benchmark::ReportUnrecognizedArguments(argc, argv))
|
|
return 1;
|
|
|
|
makeCartesianProductBenchmark<FloatingPoint, AllLocalizations, AllDisplayTypes, AllTypes, AllValues, AllAlignments,
|
|
AllPrecisions>();
|
|
|
|
benchmark::RunSpecifiedBenchmarks();
|
|
}
|