[libcxx-commits] [libcxx] f338f41 - [libc++][format] Adds integral formatter benchmarks.
Mark de Wever via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Jul 12 10:18:02 PDT 2022
Author: Mark de Wever
Date: 2022-07-12T19:17:57+02:00
New Revision: f338f416baf09aaed59b1b3bc693cab5d5cdf7ab
URL: https://github.com/llvm/llvm-project/commit/f338f416baf09aaed59b1b3bc693cab5d5cdf7ab
DIFF: https://github.com/llvm/llvm-project/commit/f338f416baf09aaed59b1b3bc693cab5d5cdf7ab.diff
LOG: [libc++][format] Adds integral formatter benchmarks.
This is a preparation to look at possible performance improvements.
Reviewed By: #libc, ldionne
Differential Revision: https://reviews.llvm.org/D129421
Added:
libcxx/benchmarks/formatter_int.bench.cpp
Modified:
libcxx/benchmarks/CMakeLists.txt
Removed:
################################################################################
diff --git a/libcxx/benchmarks/CMakeLists.txt b/libcxx/benchmarks/CMakeLists.txt
index 93da48b0b97e8..9bb45cdabd674 100644
--- a/libcxx/benchmarks/CMakeLists.txt
+++ b/libcxx/benchmarks/CMakeLists.txt
@@ -202,6 +202,7 @@ if (LIBCXX_ENABLE_INCOMPLETE_FEATURES)
format.bench.cpp
formatted_size.bench.cpp
formatter_float.bench.cpp
+ formatter_int.bench.cpp
std_format_spec_string_unicode.bench.cpp)
endif()
diff --git a/libcxx/benchmarks/formatter_int.bench.cpp b/libcxx/benchmarks/formatter_int.bench.cpp
new file mode 100644
index 0000000000000..a42d96491a2b3
--- /dev/null
+++ b/libcxx/benchmarks/formatter_int.bench.cpp
@@ -0,0 +1,208 @@
+//===----------------------------------------------------------------------===//
+// 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 <array>
+#include <format>
+#include <random>
+
+#include "benchmark/benchmark.h"
+#include "CartesianBenchmarks.h"
+
+// Tests the full range of the value.
+template <class T>
+static std::array<T, 1000>
+generate(std::uniform_int_distribution<T> distribution = std::uniform_int_distribution<T>{
+ std::numeric_limits<T>::min(), std::numeric_limits<T>::max()}) {
+ std::mt19937 generator;
+ std::array<T, 1000> result;
+ std::generate_n(result.begin(), result.size(), [&] { return distribution(generator); });
+ return result;
+}
+
+template <class T>
+static void BM_Basic(benchmark::State& state) {
+ std::array data{generate<T>()};
+ std::array<char, 100> output;
+
+ while (state.KeepRunningBatch(data.size()))
+ for (auto value : data)
+ benchmark::DoNotOptimize(std::format_to(output.begin(), "{}", value));
+}
+BENCHMARK_TEMPLATE(BM_Basic, uint32_t);
+BENCHMARK_TEMPLATE(BM_Basic, int32_t);
+BENCHMARK_TEMPLATE(BM_Basic, uint64_t);
+BENCHMARK_TEMPLATE(BM_Basic, int64_t);
+
+// Ideally the low values of a 128-bit value are all dispatched to a 64-bit routine.
+template <class T>
+static void BM_BasicLow(benchmark::State& state) {
+ using U = std::conditional_t<std::is_signed_v<T>, int64_t, uint64_t>;
+ std::array data{
+ generate<T>(std::uniform_int_distribution<T>{std::numeric_limits<U>::min(), std::numeric_limits<U>::max()})};
+ std::array<char, 100> output;
+
+ while (state.KeepRunningBatch(data.size()))
+ for (auto value : data)
+ benchmark::DoNotOptimize(std::format_to(output.begin(), "{}", value));
+}
+BENCHMARK_TEMPLATE(BM_BasicLow, __uint128_t);
+BENCHMARK_TEMPLATE(BM_BasicLow, __int128_t);
+
+BENCHMARK_TEMPLATE(BM_Basic, __uint128_t);
+BENCHMARK_TEMPLATE(BM_Basic, __int128_t);
+
+// *** 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";
+};
+
+// *** Base ***
+enum class BaseE {
+ Binary,
+ Octal,
+ Decimal,
+ Hex,
+ HexUpper,
+};
+struct AllBases : EnumValuesAsTuple<AllBases, BaseE, 5> {
+ static constexpr const char* Names[] = {"BaseBin", "BaseOct", "BaseDec", "BaseHex", "BaseHexUpper"};
+};
+
+template <BaseE E>
+struct Base {};
+
+template <>
+struct Base<BaseE::Binary> {
+ static constexpr const char* fmt = "b";
+};
+
+template <>
+struct Base<BaseE::Octal> {
+ static constexpr const char* fmt = "o";
+};
+
+template <>
+struct Base<BaseE::Decimal> {
+ static constexpr const char* fmt = "d";
+};
+
+template <>
+struct Base<BaseE::Hex> {
+ static constexpr const char* fmt = "x";
+};
+
+template <>
+struct Base<BaseE::HexUpper> {
+ static constexpr const char* fmt = "X";
+};
+
+// *** Types ***
+enum class TypeE { Int64, Uint64 };
+struct AllTypes : EnumValuesAsTuple<AllTypes, TypeE, 2> {
+ static constexpr const char* Names[] = {"Int64", "Uint64"};
+};
+
+template <TypeE E>
+struct Type {};
+
+template <>
+struct Type<TypeE::Int64> {
+ using type = int64_t;
+
+ static std::array<type, 1000> make_data() { return generate<type>(); }
+};
+
+template <>
+struct Type<TypeE::Uint64> {
+ using type = uint64_t;
+
+ static std::array<type, 1000> make_data() { return generate<type>(); }
+};
+
+// *** 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> {
+ static constexpr const char* fmt = "0<512";
+};
+
+template <>
+struct Alignment<AlignmentE::Center> {
+ static constexpr const char* fmt = "0^512";
+};
+
+template <>
+struct Alignment<AlignmentE::Right> {
+ static constexpr const char* fmt = "0>512";
+};
+
+template <>
+struct Alignment<AlignmentE::ZeroPadding> {
+ static constexpr const char* fmt = "0512";
+};
+
+template <class L, class B, class T, class A>
+struct Integral {
+ void run(benchmark::State& state) const {
+ std::array data{Type<T::value>::make_data()};
+ std::array<char, 512> output;
+
+ while (state.KeepRunningBatch(data.size()))
+ for (auto value : data)
+ benchmark::DoNotOptimize(std::format_to(output.begin(), std::string_view{fmt.data(), fmt.size()}, value));
+ }
+
+ std::string name() const { return "Integral" + L::name() + B::name() + A::name() + T::name(); }
+
+ static constexpr std::string make_fmt() {
+ return std::string("{:") + Alignment<A::value>::fmt + Localization<L::value>::fmt + Base<B::value>::fmt + "}";
+ }
+
+ static constexpr auto fmt = []() {
+ constexpr size_t s = make_fmt().size();
+ std::array<char, s> r;
+ std::ranges::copy(make_fmt(), r.begin());
+ return r;
+ }();
+};
+
+int main(int argc, char** argv) {
+ benchmark::Initialize(&argc, argv);
+ if (benchmark::ReportUnrecognizedArguments(argc, argv))
+ return 1;
+
+ makeCartesianProductBenchmark<Integral, AllLocalizations, AllBases, AllTypes, AllAlignments>();
+
+ benchmark::RunSpecifiedBenchmarks();
+}
More information about the libcxx-commits
mailing list