Reorder parsed_number_string_t fields to avoid padding (fixes #418) Placing the parse_error field next to the booleans shrinks the struct from 72 to 64 bytes on 64-bit systems, recovering a large performance regression observed with MSVC. Add a static_assert to guard the size.
diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index e814f12..4d68eaa 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h
@@ -308,13 +308,16 @@ int64_t exponent{0}; uint64_t mantissa{0}; UC const *lastmatch{nullptr}; + // The field order matters: placing 'error' next to the booleans avoids + // padding, keeping the struct at 64 bytes on 64-bit systems instead of 72. + // See https://github.com/fastfloat/fast_float/issues/418 + parse_error error{parse_error::no_error}; bool negative{false}; bool valid{false}; bool too_many_digits{false}; // contains the range of the significant digits span<UC const> integer{}; // non-nullable span<UC const> fraction{}; // nullable - parse_error error{parse_error::no_error}; }; using byte_span = span<char const>;
diff --git a/tests/basictest.cpp b/tests/basictest.cpp index 9b45822..cde42bc 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp
@@ -84,6 +84,12 @@ } } +// Guard against padding regressions that hurt performance. +// See https://github.com/fastfloat/fast_float/issues/418 +static_assert(sizeof(void *) != 8 || + sizeof(fast_float::parsed_number_string_t<char>) <= 64, + "parsed_number_string_t should fit in 64 bytes"); + #define FASTFLOAT_STR(x) #x #define SHOW_DEFINE(x) printf("%s='%s'\n", #x, FASTFLOAT_STR(x))