Skip to content

Commit 5f88083

Browse files
<charconv>: floating benchmark (#5700)
Co-authored-by: Stephan T. Lavavej <[email protected]>
1 parent 7f05724 commit 5f88083

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-0
lines changed

benchmarks/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ add_benchmark(adjacent_find src/adjacent_find.cpp)
105105
add_benchmark(any_swap src/any_swap.cpp)
106106
add_benchmark(bitset_from_string src/bitset_from_string.cpp)
107107
add_benchmark(bitset_to_string src/bitset_to_string.cpp)
108+
add_benchmark(charconv_floats src/charconv_floats.cpp)
108109
add_benchmark(efficient_nonlocking_print src/efficient_nonlocking_print.cpp)
109110
add_benchmark(filesystem src/filesystem.cpp)
110111
add_benchmark(fill src/fill.cpp)

benchmarks/src/charconv_floats.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
#include <benchmark/benchmark.h>
5+
#include <bit>
6+
#include <charconv>
7+
#include <cstddef>
8+
#include <cstdint>
9+
#include <cstdio>
10+
#include <cstdlib>
11+
#include <random>
12+
#include <system_error>
13+
#include <type_traits>
14+
#include <vector>
15+
16+
using namespace std;
17+
18+
void verify(const bool b) {
19+
if (!b) {
20+
puts("FAIL");
21+
exit(EXIT_FAILURE);
22+
}
23+
}
24+
25+
enum class RoundTrip { Sci, Fix, Gen, Hex, Lossy };
26+
27+
consteval chars_format chars_format_from_RoundTrip(const RoundTrip rt) {
28+
switch (rt) {
29+
case RoundTrip::Sci:
30+
return chars_format::scientific;
31+
case RoundTrip::Fix:
32+
return chars_format::fixed;
33+
case RoundTrip::Gen:
34+
return chars_format::general;
35+
case RoundTrip::Hex:
36+
return chars_format::hex;
37+
case RoundTrip::Lossy:
38+
default:
39+
exit(EXIT_FAILURE);
40+
}
41+
}
42+
43+
template <RoundTrip Rt, typename Floating, auto... Args>
44+
void test_to_chars(benchmark::State& state) {
45+
constexpr size_t n = 2'000'000; // how many floating-point values to test
46+
vector<Floating> vec;
47+
vec.reserve(n);
48+
49+
{
50+
mt19937_64 mt64;
51+
while (vec.size() < n) {
52+
using Integral = conditional_t<sizeof(Floating) == 4, uint32_t, uint64_t>;
53+
const Integral val = static_cast<Integral>(mt64());
54+
constexpr Integral inf_nan = sizeof(Floating) == 4 ? 0x7F800000U : 0x7FF0000000000000ULL;
55+
if ((val & inf_nan) == inf_nan) {
56+
continue; // skip INF/NAN
57+
}
58+
vec.push_back(bit_cast<Floating>(val));
59+
}
60+
}
61+
62+
char buf[2'000]; // more than enough
63+
64+
{
65+
auto it = vec.begin();
66+
for (auto _ : state) {
67+
auto result = to_chars(buf, end(buf), *it, Args...);
68+
69+
benchmark::DoNotOptimize(result.ptr);
70+
benchmark::DoNotOptimize(buf);
71+
72+
++it;
73+
if (it == vec.end()) {
74+
it = vec.begin();
75+
}
76+
}
77+
}
78+
79+
for (const auto& elem : vec) {
80+
const auto result = to_chars(buf, end(buf), elem, Args...);
81+
verify(result.ec == errc{});
82+
83+
if constexpr (Rt == RoundTrip::Lossy) {
84+
// skip lossy conversions
85+
} else {
86+
Floating round_trip;
87+
const auto from_result = from_chars(buf, result.ptr, round_trip, chars_format_from_RoundTrip(Rt));
88+
verify(from_result.ec == errc{});
89+
verify(from_result.ptr == result.ptr);
90+
verify(round_trip == elem);
91+
}
92+
}
93+
}
94+
95+
BENCHMARK(test_to_chars<RoundTrip::Gen, float>)->Name("STL_float_plain_shortest");
96+
BENCHMARK(test_to_chars<RoundTrip::Gen, double>)->Name("STL_double_plain_shortest");
97+
BENCHMARK(test_to_chars<RoundTrip::Sci, float, chars_format::scientific>)->Name("STL_float_scientific_shortest");
98+
BENCHMARK(test_to_chars<RoundTrip::Sci, double, chars_format::scientific>)->Name("STL_double_scientific_shortest");
99+
BENCHMARK(test_to_chars<RoundTrip::Fix, float, chars_format::fixed>)->Name("STL_float_fixed_shortest");
100+
BENCHMARK(test_to_chars<RoundTrip::Fix, double, chars_format::fixed>)->Name("STL_double_fixed_shortest");
101+
BENCHMARK(test_to_chars<RoundTrip::Gen, float, chars_format::general>)->Name("STL_float_general_shortest");
102+
BENCHMARK(test_to_chars<RoundTrip::Gen, double, chars_format::general>)->Name("STL_double_general_shortest");
103+
BENCHMARK(test_to_chars<RoundTrip::Hex, float, chars_format::hex>)->Name("STL_float_hex_shortest");
104+
BENCHMARK(test_to_chars<RoundTrip::Hex, double, chars_format::hex>)->Name("STL_double_hex_shortest");
105+
BENCHMARK(test_to_chars<RoundTrip::Sci, float, chars_format::scientific, 8>)->Name("STL_float_scientific_8");
106+
BENCHMARK(test_to_chars<RoundTrip::Sci, double, chars_format::scientific, 16>)->Name("STL_double_scientific_16");
107+
BENCHMARK(test_to_chars<RoundTrip::Lossy, float, chars_format::fixed, 6>)->Name("STL_float_fixed_6_lossy");
108+
BENCHMARK(test_to_chars<RoundTrip::Lossy, double, chars_format::fixed, 6>)->Name("STL_double_fixed_6_lossy");
109+
BENCHMARK(test_to_chars<RoundTrip::Gen, float, chars_format::general, 9>)->Name("STL_float_general_9");
110+
BENCHMARK(test_to_chars<RoundTrip::Gen, double, chars_format::general, 17>)->Name("STL_double_general_17");
111+
BENCHMARK(test_to_chars<RoundTrip::Hex, float, chars_format::hex, 6>)->Name("STL_float_hex_6");
112+
BENCHMARK(test_to_chars<RoundTrip::Hex, double, chars_format::hex, 13>)->Name("STL_double_hex_13");
113+
114+
BENCHMARK_MAIN();

0 commit comments

Comments
 (0)