[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