Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions benchmarks/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
192 changes: 192 additions & 0 deletions benchmarks/src/charconv_floats.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <benchmark/benchmark.h>
#include <charconv>
#include <random>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <system_error>
#include <type_traits>
#include <vector>

using namespace std;

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 <RoundTrip Rt, typename Floating, typename... Args>
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<Floating> vec;

vec.reserve(n);
while (vec.size() < n) {
using Integral = conditional_t<sizeof(Floating) == 4, uint32_t, uint64_t>;
const Integral val = static_cast<Integral>(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<RoundTrip::Gen, float>;
constexpr auto STL_double_plain_shortest = test_to_chars<RoundTrip::Gen, double>;

void STL_float_scientific_shortest(benchmark::State& state) {
test_to_chars<RoundTrip::Sci, float>(state, chars_format::scientific);
}

void STL_double_scientific_shortest(benchmark::State& state) {
test_to_chars<RoundTrip::Sci, double>(state, chars_format::scientific);
}

void STL_float_fixed_shortest(benchmark::State& state) {
test_to_chars<RoundTrip::Fix, float>(state, chars_format::fixed);
}

void STL_double_fixed_shortest(benchmark::State& state) {
test_to_chars<RoundTrip::Fix, double>(state, chars_format::fixed);
}

void STL_float_general_shortest(benchmark::State& state) {
test_to_chars<RoundTrip::Gen, float>(state, chars_format::general);
}

void STL_double_general_shortest(benchmark::State& state) {
test_to_chars<RoundTrip::Gen, double>(state, chars_format::general);
}

void STL_float_hex_shortest(benchmark::State& state) {
test_to_chars<RoundTrip::Hex, float>(state, chars_format::hex);
}

void STL_double_hex_shortest(benchmark::State& state) {
test_to_chars<RoundTrip::Hex, double>(state, chars_format::hex);
}

void STL_float_scientific_8(benchmark::State& state) {
test_to_chars<RoundTrip::Sci, float>(state, chars_format::scientific, 8);
}

void STL_double_scientific_16(benchmark::State& state) {
test_to_chars<RoundTrip::Sci, double>(state, chars_format::scientific, 16);
}

void STL_float_fixed_6_lossy(benchmark::State& state) {
test_to_chars<RoundTrip::Lossy, float>(state, chars_format::fixed, 6);
}

void STL_double_fixed_6_lossy(benchmark::State& state) {
test_to_chars<RoundTrip::Lossy, double>(state, chars_format::fixed, 6);
}

void STL_float_general_9(benchmark::State& state) {
test_to_chars<RoundTrip::Gen, float>(state, chars_format::general, 9);
}

void STL_double_general_17(benchmark::State& state) {
test_to_chars<RoundTrip::Gen, double>(state, chars_format::general, 17);
}

void STL_float_hex_6(benchmark::State& state) {
test_to_chars<RoundTrip::Hex, float>(state, chars_format::hex, 6);
}

void STL_double_hex_13(benchmark::State& state) {
test_to_chars<RoundTrip::Hex, double>(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();