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.
This commit is contained in:
Daniel Lemire 2026-10-04 09:34:39 -04:00
parent 9498cc361e
commit fe5e97eeac
2 changed files with 10 additions and 1 deletions

View File

@ -308,13 +308,16 @@ template <typename UC> struct parsed_number_string_t {
int64_t exponent{0}; int64_t exponent{0};
uint64_t mantissa{0}; uint64_t mantissa{0};
UC const *lastmatch{nullptr}; 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 negative{false};
bool valid{false}; bool valid{false};
bool too_many_digits{false}; bool too_many_digits{false};
// contains the range of the significant digits // contains the range of the significant digits
span<UC const> integer{}; // non-nullable span<UC const> integer{}; // non-nullable
span<UC const> fraction{}; // nullable span<UC const> fraction{}; // nullable
parse_error error{parse_error::no_error};
}; };
using byte_span = span<char const>; using byte_span = span<char const>;

View File

@ -84,6 +84,12 @@ char const *round_name(int d) {
} }
} }
// 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 FASTFLOAT_STR(x) #x
#define SHOW_DEFINE(x) printf("%s='%s'\n", #x, FASTFLOAT_STR(x)) #define SHOW_DEFINE(x) printf("%s='%s'\n", #x, FASTFLOAT_STR(x))