From 5f342c37c3fc3a605cde2cb7afdedb7c1e9cf97a Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 28 Aug 2025 09:28:37 +0300 Subject: [PATCH 1/3] Port benchmark from #1243 comment Co-authored-by: StephanTLavavej --- benchmarks/CMakeLists.txt | 1 + benchmarks/src/charconv_floats.cpp | 194 +++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 benchmarks/src/charconv_floats.cpp diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 5f7afec336..e9283fd520 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -104,6 +104,7 @@ add_benchmark(adjacent_difference src/adjacent_difference.cpp) add_benchmark(adjacent_find src/adjacent_find.cpp) add_benchmark(bitset_from_string src/bitset_from_string.cpp) add_benchmark(bitset_to_string src/bitset_to_string.cpp) +add_benchmark(charconv_floats src/charconv_floats.cpp) add_benchmark(efficient_nonlocking_print src/efficient_nonlocking_print.cpp) add_benchmark(filesystem src/filesystem.cpp) add_benchmark(fill src/fill.cpp) diff --git a/benchmarks/src/charconv_floats.cpp b/benchmarks/src/charconv_floats.cpp new file mode 100644 index 0000000000..f8f6b4b747 --- /dev/null +++ b/benchmarks/src/charconv_floats.cpp @@ -0,0 +1,194 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace std::chrono; + +void verify(const bool b) { + if (!b) { + puts("FAIL"); + exit(EXIT_FAILURE); + } +} + +enum class RoundTrip { Sci, Fix, Gen, Hex, Lossy }; + +constexpr chars_format chars_format_from_RoundTrip(const RoundTrip rt) { + switch (rt) { + case RoundTrip::Sci: + return chars_format::scientific; + case RoundTrip::Fix: + return chars_format::fixed; + case RoundTrip::Gen: + return chars_format::general; + case RoundTrip::Hex: + return chars_format::hex; + case RoundTrip::Lossy: + default: + puts("FAIL"); + exit(EXIT_FAILURE); + } +} + +template +void test_to_chars(benchmark::State& state, const Args&... args) { + constexpr size_t n = 2'000'000; // how many floating-point values to test + + constexpr size_t BufSize = 2'000; // more than enough + + mt19937_64 mt64; + + vector vec; + + vec.reserve(n); + for (size_t i = 0; i != n; ++i) { + using Integral = conditional_t; + const Integral val = static_cast(mt64()); + constexpr Integral inf_nan = sizeof(Floating) == 4 ? 0x7F800000U : 0x7FF0000000000000ULL; + if ((val & inf_nan) == inf_nan) { + continue; // skip INF/NAN + } + Floating flt; + static_assert(sizeof(flt) == sizeof(val)); + memcpy(&flt, &val, sizeof(flt)); + vec.push_back(flt); + } + + char buf[BufSize]; + + auto it = vec.begin(); + for (auto _ : state) { + auto result = to_chars(buf, buf + BufSize, *it, args...); + + benchmark::DoNotOptimize(result.ptr); + benchmark::DoNotOptimize(buf); + + ++it; + if (it == vec.end()) { + it = vec.begin(); + } + } + + for (const auto& elem : vec) { + const auto result = to_chars(buf, buf + BufSize, elem, args...); + verify(result.ec == errc{}); + + if constexpr (Rt == RoundTrip::Lossy) { + // skip lossy conversions + } else { + Floating round_trip; + const auto from_result = from_chars(buf, result.ptr, round_trip, chars_format_from_RoundTrip(Rt)); + verify(from_result.ec == errc{}); + verify(from_result.ptr == result.ptr); + verify(round_trip == elem); + } + } +} + +constexpr auto STL_float_plain_shortest = test_to_chars; +constexpr auto STL_double_plain_shortest = test_to_chars; + +void STL_float_scientific_shortest(benchmark::State& state) { + test_to_chars(state, chars_format::scientific); +} + +void STL_double_scientific_shortest(benchmark::State& state) { + test_to_chars(state, chars_format::scientific); +} + +void STL_float_fixed_shortest(benchmark::State& state) { + test_to_chars(state, chars_format::fixed); +} + +void STL_double_fixed_shortest(benchmark::State& state) { + test_to_chars(state, chars_format::fixed); +} + +void STL_float_general_shortest(benchmark::State& state) { + test_to_chars(state, chars_format::general); +} + +void STL_double_general_shortest(benchmark::State& state) { + test_to_chars(state, chars_format::general); +} + +void STL_float_hex_shortest(benchmark::State& state) { + test_to_chars(state, chars_format::hex); +} + +void STL_double_hex_shortest(benchmark::State& state) { + test_to_chars(state, chars_format::hex); +} + +void STL_float_scientific_8(benchmark::State& state) { + test_to_chars(state, chars_format::scientific, 8); +} + +void STL_double_scientific_16(benchmark::State& state) { + test_to_chars(state, chars_format::scientific, 16); +} + +void STL_float_fixed_6_lossy(benchmark::State& state) { + test_to_chars(state, chars_format::fixed, 6); +} + +void STL_double_fixed_6_lossy(benchmark::State& state) { + test_to_chars(state, chars_format::fixed, 6); +} + +void STL_float_general_9(benchmark::State& state) { + test_to_chars(state, chars_format::general, 9); +} + +void STL_double_general_17(benchmark::State& state) { + test_to_chars(state, chars_format::general, 17); +} + +void STL_float_hex_6(benchmark::State& state) { + test_to_chars(state, chars_format::hex, 6); +} + +void STL_double_hex_13(benchmark::State& state) { + test_to_chars(state, chars_format::hex, 13); +} + +BENCHMARK(STL_float_plain_shortest); +BENCHMARK(STL_double_plain_shortest); + +BENCHMARK(STL_float_scientific_shortest); +BENCHMARK(STL_double_scientific_shortest); + +BENCHMARK(STL_float_fixed_shortest); +BENCHMARK(STL_double_fixed_shortest); + +BENCHMARK(STL_float_general_shortest); +BENCHMARK(STL_double_general_shortest); + +BENCHMARK(STL_float_hex_shortest); +BENCHMARK(STL_double_hex_shortest); + +BENCHMARK(STL_float_scientific_8); +BENCHMARK(STL_double_scientific_16); + +BENCHMARK(STL_float_fixed_6_lossy); +BENCHMARK(STL_double_fixed_6_lossy); + +BENCHMARK(STL_float_general_9); +BENCHMARK(STL_double_general_17); + +BENCHMARK(STL_float_hex_6); +BENCHMARK(STL_double_hex_13); + +BENCHMARK_MAIN(); From 3e3cbba3d83c56db33ac99e679f4fce56f7794a7 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 28 Aug 2025 10:05:57 +0300 Subject: [PATCH 2/3] use size, as we skip sometimes --- benchmarks/src/charconv_floats.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/src/charconv_floats.cpp b/benchmarks/src/charconv_floats.cpp index f8f6b4b747..36fd3140bc 100644 --- a/benchmarks/src/charconv_floats.cpp +++ b/benchmarks/src/charconv_floats.cpp @@ -53,7 +53,7 @@ void test_to_chars(benchmark::State& state, const Args&... args) { vector vec; vec.reserve(n); - for (size_t i = 0; i != n; ++i) { + while (vec.size() < n) { using Integral = conditional_t; const Integral val = static_cast(mt64()); constexpr Integral inf_nan = sizeof(Floating) == 4 ? 0x7F800000U : 0x7FF0000000000000ULL; From c150dcbfff3a170009aa9e585f6856f2fe745bf4 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 28 Aug 2025 14:47:02 +0300 Subject: [PATCH 3/3] no chrono --- benchmarks/src/charconv_floats.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/benchmarks/src/charconv_floats.cpp b/benchmarks/src/charconv_floats.cpp index 36fd3140bc..731c8d5748 100644 --- a/benchmarks/src/charconv_floats.cpp +++ b/benchmarks/src/charconv_floats.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -14,7 +13,6 @@ #include using namespace std; -using namespace std::chrono; void verify(const bool b) { if (!b) {