diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 02b2bc91bbd..ffaeaf4f9bd 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -105,6 +105,7 @@ add_benchmark(adjacent_find src/adjacent_find.cpp) add_benchmark(any_swap src/any_swap.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 00000000000..7da1eb87fd0 --- /dev/null +++ b/benchmarks/src/charconv_floats.cpp @@ -0,0 +1,114 @@ +// 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; + +void verify(const bool b) { + if (!b) { + puts("FAIL"); + exit(EXIT_FAILURE); + } +} + +enum class RoundTrip { Sci, Fix, Gen, Hex, Lossy }; + +consteval 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: + exit(EXIT_FAILURE); + } +} + +template +void test_to_chars(benchmark::State& state) { + constexpr size_t n = 2'000'000; // how many floating-point values to test + vector vec; + vec.reserve(n); + + { + mt19937_64 mt64; + while (vec.size() < n) { + 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 + } + vec.push_back(bit_cast(val)); + } + } + + char buf[2'000]; // more than enough + + { + auto it = vec.begin(); + for (auto _ : state) { + auto result = to_chars(buf, end(buf), *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, end(buf), 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); + } + } +} + +BENCHMARK(test_to_chars)->Name("STL_float_plain_shortest"); +BENCHMARK(test_to_chars)->Name("STL_double_plain_shortest"); +BENCHMARK(test_to_chars)->Name("STL_float_scientific_shortest"); +BENCHMARK(test_to_chars)->Name("STL_double_scientific_shortest"); +BENCHMARK(test_to_chars)->Name("STL_float_fixed_shortest"); +BENCHMARK(test_to_chars)->Name("STL_double_fixed_shortest"); +BENCHMARK(test_to_chars)->Name("STL_float_general_shortest"); +BENCHMARK(test_to_chars)->Name("STL_double_general_shortest"); +BENCHMARK(test_to_chars)->Name("STL_float_hex_shortest"); +BENCHMARK(test_to_chars)->Name("STL_double_hex_shortest"); +BENCHMARK(test_to_chars)->Name("STL_float_scientific_8"); +BENCHMARK(test_to_chars)->Name("STL_double_scientific_16"); +BENCHMARK(test_to_chars)->Name("STL_float_fixed_6_lossy"); +BENCHMARK(test_to_chars)->Name("STL_double_fixed_6_lossy"); +BENCHMARK(test_to_chars)->Name("STL_float_general_9"); +BENCHMARK(test_to_chars)->Name("STL_double_general_17"); +BENCHMARK(test_to_chars)->Name("STL_float_hex_6"); +BENCHMARK(test_to_chars)->Name("STL_double_hex_13"); + +BENCHMARK_MAIN();