From ee849467021be4fbef05e90913528cb100127bc1 Mon Sep 17 00:00:00 2001 From: fcostaoliveira Date: Mon, 1 Jun 2026 00:56:52 +0100 Subject: [PATCH] EXP-052: 2x unroll of char loop_parse_if_eight_digits (ported from ffc EXP-044) Clang/AArch64-gated 16-digit-per-iteration unroll of the fraction SWAR loop; eliminates the back-edge for typical 17-digit [0,1] mantissas. GCC keeps the auto-unrolled simple loop. ARM Graviton4 (canonical fast_float MB/s vs EXP-050): Clang: random +2.8% (1365.7 from 1328.8), mesh +1.7%, canada +0.5% GCC: unchanged (#else path) Correctness: 14/14 pass. Co-Authored-By: Claude Opus 4.8 --- include/fast_float/ascii_number.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index 257b43b..6c79509 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -259,6 +259,34 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void loop_parse_if_eight_digits(char const *&p, char const *const pend, uint64_t &i) { // optimizes better than parse_if_eight_digits_unrolled() for UC = char. +#if defined(__aarch64__) && defined(__clang__) + // 2x unroll (ported from ffc EXP-044): on Clang/AArch64, consuming 16 digits + // per iteration eliminates the loop back-edge for typical fractions (e.g. the + // 17-digit mantissas of uniform [0,1] inputs) and keeps the SWAR constants + // resident. GCC auto-unrolls the simple loop well, so it keeps the original. + while ((pend - p) >= 16) { + uint64_t val1 = read8_to_u64(p); + if (!is_made_of_eight_digits_fast(val1)) { + break; + } + uint64_t val2 = read8_to_u64(p + 8); + if (!is_made_of_eight_digits_fast(val2)) { + i = i * 100000000 + parse_eight_digits_unrolled(val1); + p += 8; + return; // val2 is not a full digit block; caller's byte loop handles rest + } + i = (i * 100000000 + parse_eight_digits_unrolled(val1)) * 100000000 + + parse_eight_digits_unrolled(val2); // in rare cases overflows, that's ok + p += 16; + } + if ((pend - p) >= 8) { + uint64_t val = read8_to_u64(p); + if (is_made_of_eight_digits_fast(val)) { + i = i * 100000000 + parse_eight_digits_unrolled(val); + p += 8; + } + } +#else while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(read8_to_u64(p))) { i = i * 100000000 + @@ -266,6 +294,7 @@ loop_parse_if_eight_digits(char const *&p, char const *const pend, p)); // in rare cases, this will overflow, but that's ok p += 8; } +#endif } enum class parse_error {