mirror of
https://github.com/fastfloat/fast_float.git
synced 2026-09-14 22:52:47 +08:00
Add truncated-fraction benchmark mode
This commit is contained in:
parent
f6df0f2917
commit
27b89da50c
@ -3,6 +3,7 @@
|
||||
#endif
|
||||
#include "counters/event_counter.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
@ -226,7 +227,202 @@ void fileload(std::string filename) {
|
||||
process(lines, volume);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr size_t truncated_fraction_integer_digits =
|
||||
fast_float::binary_format<double>::max_digits() + 1;
|
||||
constexpr size_t truncated_fraction_max_digits =
|
||||
fast_float::binary_format<double>::max_digits();
|
||||
constexpr size_t truncated_fraction_digits = 4 * 1024 * 1024;
|
||||
constexpr size_t truncated_fraction_batches = 9;
|
||||
constexpr size_t truncated_fraction_zero_batches = 17;
|
||||
constexpr size_t truncated_fraction_direct_iterations = 2048;
|
||||
constexpr size_t truncated_fraction_direct_zero_iterations = 8192;
|
||||
constexpr size_t truncated_fraction_from_chars_iterations = 256;
|
||||
constexpr size_t truncated_fraction_from_chars_zero_iterations = 512;
|
||||
constexpr double truncated_fraction_expected_value = 0x0.607b00a417628p-1022;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define FASTFLOAT_BENCH_NOINLINE __declspec(noinline)
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#define FASTFLOAT_BENCH_NOINLINE __attribute__((noinline))
|
||||
#else
|
||||
#define FASTFLOAT_BENCH_NOINLINE
|
||||
#endif
|
||||
|
||||
struct truncated_fraction_input {
|
||||
std::string text{};
|
||||
fast_float::parsed_number_string parsed{};
|
||||
};
|
||||
|
||||
[[noreturn]] void truncated_fraction_fail(char const *message) {
|
||||
std::fputs(message, stderr);
|
||||
std::fputc('\n', stderr);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void truncated_fraction_usage() {
|
||||
std::fputs("usage: realbenchmark --truncated-fraction "
|
||||
"{parse_mantissa|from_chars} {nonzero|zero}\n",
|
||||
stderr);
|
||||
}
|
||||
|
||||
std::string make_truncated_fraction_input(char final_integer_digit) {
|
||||
std::string result = "8385788696668661046";
|
||||
result.append(truncated_fraction_integer_digits - result.size() - 1, '0');
|
||||
result.push_back(final_integer_digit);
|
||||
result.push_back('.');
|
||||
result.append(truncated_fraction_digits, '0');
|
||||
result += "e-1078";
|
||||
return result;
|
||||
}
|
||||
|
||||
void initialize_truncated_fraction_input(truncated_fraction_input &input,
|
||||
char final_integer_digit) {
|
||||
input.text = make_truncated_fraction_input(final_integer_digit);
|
||||
fast_float::parse_options options;
|
||||
input.parsed = fast_float::parse_number_string<false>(
|
||||
input.text.data(), input.text.data() + input.text.size(), options, true);
|
||||
if (!input.parsed.valid || !input.parsed.too_many_digits ||
|
||||
input.parsed.integer.len() != truncated_fraction_integer_digits ||
|
||||
input.parsed.fraction.len() != truncated_fraction_digits) {
|
||||
truncated_fraction_fail("unexpected parsed input");
|
||||
}
|
||||
|
||||
double parsed_value = 0;
|
||||
auto const parsed = fast_float::from_chars(
|
||||
input.text.data(), input.text.data() + input.text.size(), parsed_value);
|
||||
if (parsed.ec != std::errc() ||
|
||||
parsed.ptr != input.text.data() + input.text.size() ||
|
||||
parsed_value != truncated_fraction_expected_value) {
|
||||
truncated_fraction_fail("unexpected conversion result");
|
||||
}
|
||||
}
|
||||
|
||||
FASTFLOAT_BENCH_NOINLINE uint64_t
|
||||
parse_mantissa_once(truncated_fraction_input const &input) {
|
||||
fast_float::parsed_number_string number = input.parsed;
|
||||
fast_float::bigint result;
|
||||
size_t digits = 0;
|
||||
fast_float::parse_mantissa(result, number, truncated_fraction_max_digits,
|
||||
digits);
|
||||
bool truncated = false;
|
||||
return result.hi64(truncated) ^ (uint64_t(digits) << 1) ^ uint64_t(truncated);
|
||||
}
|
||||
|
||||
FASTFLOAT_BENCH_NOINLINE uint64_t
|
||||
from_chars_once(truncated_fraction_input const &input) {
|
||||
double result = 0;
|
||||
auto const parsed = fast_float::from_chars(
|
||||
input.text.data(), input.text.data() + input.text.size(), result);
|
||||
if (parsed.ec != std::errc() ||
|
||||
parsed.ptr != input.text.data() + input.text.size()) {
|
||||
truncated_fraction_fail("unexpected conversion result");
|
||||
}
|
||||
|
||||
uint64_t bits = 0;
|
||||
static_assert(sizeof(bits) == sizeof(result), "unexpected double size");
|
||||
std::memcpy(&bits, &result, sizeof(bits));
|
||||
return bits;
|
||||
}
|
||||
|
||||
inline void do_not_optimize(uint64_t value) {
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
asm volatile("" : : "r"(value) : "memory");
|
||||
#else
|
||||
volatile uint64_t sink = value;
|
||||
(void)sink;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
double measure_truncated_fraction(
|
||||
std::array<truncated_fraction_input, 2> const &inputs, size_t iterations,
|
||||
size_t batches, Function function) {
|
||||
std::array<double, truncated_fraction_zero_batches> samples{};
|
||||
for (size_t batch = 0; batch < batches; ++batch) {
|
||||
uint64_t sink = 0;
|
||||
auto const start = std::chrono::steady_clock::now();
|
||||
for (size_t index = 0; index < iterations; ++index) {
|
||||
sink += function(inputs[index & 1]);
|
||||
}
|
||||
auto const finish = std::chrono::steady_clock::now();
|
||||
do_not_optimize(sink);
|
||||
samples[batch] =
|
||||
double(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(finish - start)
|
||||
.count()) /
|
||||
double(iterations);
|
||||
}
|
||||
std::sort(samples.begin(), samples.begin() + batches);
|
||||
return samples[batches / 2];
|
||||
}
|
||||
|
||||
void print_truncated_fraction_measurement(double nanoseconds_per_operation) {
|
||||
std::printf("{\"metric\":\"ns/op\",\"value\":%.17g}\n",
|
||||
nanoseconds_per_operation);
|
||||
}
|
||||
|
||||
int run_truncated_fraction_benchmark(char const *operation,
|
||||
char const *integer_suffix) {
|
||||
bool direct = false;
|
||||
if (std::strcmp(operation, "parse_mantissa") == 0) {
|
||||
direct = true;
|
||||
} else if (std::strcmp(operation, "from_chars") != 0) {
|
||||
truncated_fraction_usage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
bool zero_suffix = false;
|
||||
if (std::strcmp(integer_suffix, "zero") == 0) {
|
||||
zero_suffix = true;
|
||||
} else if (std::strcmp(integer_suffix, "nonzero") != 0) {
|
||||
truncated_fraction_usage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::array<truncated_fraction_input, 2> inputs{};
|
||||
initialize_truncated_fraction_input(inputs[0], zero_suffix ? '0' : '1');
|
||||
initialize_truncated_fraction_input(inputs[1], zero_suffix ? '0' : '2');
|
||||
|
||||
// The zero-suffix route retains the long fractional scan. Use larger batches
|
||||
// and more independent samples there so that its unchanged path has a precise
|
||||
// check.
|
||||
size_t const iterations =
|
||||
zero_suffix ? (direct ? truncated_fraction_direct_zero_iterations
|
||||
: truncated_fraction_from_chars_zero_iterations)
|
||||
: (direct ? truncated_fraction_direct_iterations
|
||||
: truncated_fraction_from_chars_iterations);
|
||||
size_t const batches = zero_suffix ? truncated_fraction_zero_batches
|
||||
: truncated_fraction_batches;
|
||||
|
||||
if (direct) {
|
||||
print_truncated_fraction_measurement(measure_truncated_fraction(
|
||||
inputs, iterations, batches, [](truncated_fraction_input const &input) {
|
||||
return parse_mantissa_once(input);
|
||||
}));
|
||||
} else {
|
||||
print_truncated_fraction_measurement(measure_truncated_fraction(
|
||||
inputs, iterations, batches, [](truncated_fraction_input const &input) {
|
||||
return from_chars_once(input);
|
||||
}));
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#undef FASTFLOAT_BENCH_NOINLINE
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc > 1 && std::strcmp(argv[1], "--truncated-fraction") == 0) {
|
||||
if (argc != 4) {
|
||||
truncated_fraction_usage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return run_truncated_fraction_benchmark(argv[2], argv[3]);
|
||||
}
|
||||
|
||||
if (collector.has_events()) {
|
||||
std::cout << "# Using hardware counters" << std::endl;
|
||||
} else {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user