After the 8-digit SWAR block loop, consume a remaining 4-7 digit run in one
read4_to_u32 + parse_four_digits_unrolled step instead of byte-by-byte (reusing
the existing 4-digit helpers). The parsed result is identical; this is purely a
faster way to consume the same digits.
Gated to clang: on gcc the extra 4-digit check regresses inputs whose remainder
is < 4 digits (e.g. the 17-digit fraction of uniform [0,1] -> -3% on 'random'),
because the check becomes pure overhead there; clang does not show that.
m8g.metal-24xl (Graviton4), -O3 -march=native, simple_fastfloat_benchmark,
from_chars->double, clang 18, base vs patch back-to-back (2 samples):
canada.txt +11.7%, mesh.txt +7.4%, random ~flat. No regression.
parse_number_string scans the integer part one byte at a time in a while loop,
while the fraction already uses the 8-digit SWAR loop. Most integer parts are
1-5 digits, so the loop back-edge dominates. Peel the first five iterations into
nested ifs, falling through to the original while for longer runs. Semantics are
identical (i = 10*i + digit, advancing p); no behavior change.
AWS m8g.metal-24xl (Graviton4), -O3 -march=native, simple_fastfloat_benchmark,
from_chars->double. base vs patch measured back-to-back, mean of 2 runs:
canada: gcc +3.1%, clang +2.8%
mesh: gcc +5.4%, clang +5.1%
random: ~flat (1-digit integer part)
No regression; gcc and clang agree.
Alternatives benchmarked and rejected: reusing loop_parse_if_eight_digits for the
integer part regressed 5-8% (integer parts are too short for 8-digit SWAR setup);
a counted for(k<5) loop matched on gcc but clang optimized it worse (canada -0.9%).
The explicit peel is the only form solidly positive on both compilers.
Pre-clear the lint_and_format_check CI gate. clang-format-18 (CI pins 17; LLVM base
style is identical for these constructs). Behavior/benchmarks unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
After the 8-digit block loop, consume a remaining 4-7 digit run in one SWAR step
(reusing fast_float's existing read4_to_u32 / is_made_of_four_digits_fast /
parse_four_digits_unrolled) instead of byte-by-byte. GCC path only: on Clang the
follow-up's presence bloated the 2x-unroll codegen and regressed random -6.2%.
ARM Graviton4 (canonical fast_float MB/s vs EXP-052):
GCC: canada +2.6% (948.1 from 924.0, i/f 248.7->229.7), random/mesh flat
Clang: unchanged (EXP-052 path preserved)
Correctness: 14/14 pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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 <noreply@anthropic.com>
Peel the first 5 iterations of the integer-part digit loop into nested ifs,
eliminating the loop back-edge for the common 1-5 digit integer case. Identical
semantics (i = 10*i + digit). Biggest win on inputs with multi-digit integer
parts (mesh 3D coordinates).
ARM Graviton4 (canonical MB/s, vs upstream 7790aa6 baseline):
GCC: random +0.05%, canada +4.0%, mesh +34.3% (c/f 55.7->41.4)
Clang: random +4.9%, canada +2.8%, mesh +5.1%
Correctness: 14/14 core+supplemental pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replaces uses of std::min with ternary operators in ascii_number.h, digit_comparison.h, and float_common.h to remove the dependency on the <algorithm> header in those files.
With bzlmod, native rules like cc_library are no longer implicitly available
and must be explicitly loaded from rules_cc. Add the rules_cc dependency to
MODULE.bazel and the corresponding load statement to BUILD.bazel.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>