FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Optimize validate_utf8_with_counts (icelake ASCII-skip, drop non_ascii, NEON counting) by lemire · Pull Request #2 · BenjaminBucher/simdutf · GitHub

Optimize validate_utf8_with_counts (icelake ASCII-skip, drop non_ascii, NEON counting) - #2

Merged
BenjaminBucher merged 5 commits into
BenjaminBucher:simdunicode-portfrom
lemire:opt974
Aug 6, 2026
Merged

Optimize validate_utf8_with_counts (icelake ASCII-skip, drop non_ascii, NEON counting)#2
BenjaminBucher merged 5 commits into
BenjaminBucher:simdunicode-portfrom
lemire:opt974

Conversation

lemire commented Jul 24, 2026

Copy link
Copy Markdown

Five commits on top of simdunicode-port optimizing validate_utf8_with_counts.

  • icelake: skip the popcount-based counting on pure-ASCII 64-byte blocks
    (using the checker's ASCII return value), and drop the unused non_ascii_count
    from utf8_result and all backends — neither utf16_length() (input −
    continuations + four_byte) nor the code-point count (input − continuations)
    needs it. ~1.6x on ASCII/mixed on Emerald Rapids; counting on ASCII/mixed now
    matches the plain-validation floor.
  • arm64: route to the generic NEON kernel instead of scalar
    (~17–23x on ASCII/mixed, 2–3.5x on multibyte vs the old scalar fallback).
  • arm64 NEON counting: first count via horizontal byte-sum, then via
    SimdUnicode's deferred cross-lane reduction (int8x16 lane accumulators updated
    with cheap vertical adds; reduce only every 124 chunks / on error / at end).
    Fewer instructions and half the horizontal reductions than the byte-sum
    version. Graviton 2 +40% on multibyte, Apple M4 +17%; tie on ASCII/mixed.
  • generic: check_next_input now returns the ASCII bool, matching the
    icelake avx512 checker; the NEON counter uses it to gate accumulation without
    a redundant is_ascii() per block.

Net diff on the scalar/generic/icelake side is a simplification (fewer fields,
fewer popcounts).

Tested: the validate_utf8_with_counts brute-force equality-to-scalar test
passes on x86 (Emerald Rapids / icelake + haswell + westmere), arm64 (Apple M4),
and Graviton 2 (Neoverse N1); the full ctest suite is green on x86 and arm64.

lemire added 5 commits July 23, 2026 14:54
…ii_count

- icelake validate_utf8_with_counts: skip continuation/4-byte popcounts on
  pure-ASCII 64-byte blocks (use avx512_utf8_checker::check_next_input's
  ASCII return), matching SimdUnicode's ASCII fast path. ~1.6x on ASCII/mixed.
- Remove non_ascii_count from utf8_result and all backends: it was computed in
  the hot loop but consumed by nothing (utf16_length needs only input,
  continuations, four_byte; code points need input - continuations).
- Fewer popcounts per non-ASCII block across scalar/generic/icelake.
arm64 already routes validate_utf8/validate_utf8_with_errors through the
generic utf8_validation kernel; do the same for validate_utf8_with_counts.
Measured on Apple M4 Max (64 MB inputs, best-of-N):
  ascii  4.2 -> 97.5 GB/s (23x), mixed 4.2 -> 74 GB/s (18x),
  chinese 3.0 -> 7.3 GB/s (2.4x), emoji 3.6 -> 7.1 GB/s (2x).
Brute-force equality-to-scalar test passes.
…ask+popcount

NEON has no movemask; building a bitmask and popcounting it is costly. Reduce
the continuation / four-byte comparison masks directly with a horizontal byte
add (sum_bytes), one instruction per counter. x86 keeps movemask+popcount via
the #else branch (byte-identical to before, verified unchanged on big4).
Apple M4 Max, validate_utf8_with_counts, 64 MB, best-of-N:
  chinese 7.3 -> 10.6 GB/s (1.45x), emoji 7.1 -> 10.5 GB/s (1.48x);
  ascii/mixed unchanged (counting already skipped on ASCII blocks).
Replace the per-chunk horizontal reduction (sum_bytes/vaddvq) with int8x16
lane accumulators updated by cheap vertical adds; reduce across lanes only
every 124 chunks, on error, and at end. Helps weak cores (Graviton 2) where
cross-lane sums are slow. Arm64-guarded; x86 path unchanged.
Make the generic utf8_checker::check_next_input return whether the block was
pure ASCII (matching the icelake avx512 checker). The NEON counting path uses
it to gate accumulation without a redundant is_ascii() call per block. Existing
validate_utf8 callers ignore the return value.

lemire commented Jul 24, 2026

Copy link
Copy Markdown
Author

@BenjaminBucher This is on top of your working branch for simdutf#974

Copy link
Copy Markdown
Owner

Removing non_ascii_counts cleans up a lot of stuff again, I agree that it's nicer.

Interesting that NEON doesn't have a movemask instruction.
The arm implementation looks correct, and the counter struct seems very elegant to me (though I don't feel qualified in my review).

BenjaminBucher merged commit e02e7ac into BenjaminBucher:simdunicode-port Aug 6, 2026
5 of 22 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants


Back | FazBrowse Home | New Git URL