Skip to content

perf: use SIMD for ASCII scan, lowercase, and UTF transcoding - #86

Open
anonrig wants to merge 4 commits into
mainfrom
cursor/simd-performance-008b
Open

perf: use SIMD for ASCII scan, lowercase, and UTF transcoding#86
anonrig wants to merge 4 commits into
mainfrom
cursor/simd-performance-008b

Conversation

@anonrig

@anonrig anonrig commented Aug 17, 2026

Copy link
Copy Markdown
Member

This speeds up the IDNA hot paths with portable SIMD (SSE2 on x86_64, NEON on aarch64) and a SWAR fallback, while keeping the instruction count down on the short-domain path that dominates to_ascii.

Instruction-count reductions

  • One pass on the ASCII carve-out. to_ascii used to scan for ASCII, copy, then scan again to lowercase. It now copies once and runs ascii_lowercase_is_ascii: in-place A–Z → a–z plus a high-bit test, the same approach as ada-url’s to_lower_ascii.
  • 32-byte inner loops for lowercase / is_ascii, with a single movemask/max at the end of the vector section.
  • Shared loads for UTF-8 widen and UTF-32 pack (check and convert use the same register).
  • Accumulate utf8_length_from_utf32 in a vector and reduce once, instead of a horizontal sum every 4 code points.
  • Removed SIMD that cost more than it saved on typical label lengths: forbidden-domain stays a 4-way table unroll (ada-url style), and the label-dot scan is a scalar pointer walk again.

SIMD that remains

  • is_ascii (UTF-8): 32-byte OR + SWAR tail
  • ascii_map / ASCII to_ascii: vector lowercase + high-bit test
  • UTF-8 ↔ UTF-32 ASCII widen/pack and leading-byte counts

is_ascii is no longer constexpr. The old declaration could not be evaluated at compile time from other TUs, and the vector paths cannot be constexpr.

Tests

Coverage for SIMD block sizes and tails (15/16/17/32/33 bytes, unaligned views, non-ASCII at first/middle/last), ascii_map widths, forbidden-domain punctuation, and mixed UTF-8/UTF-32 transcoding. Release, pedantic -Werror, and ASan were run locally. The NEON TU was also cross-compiled with aarch64-linux-gnu-g++ and the same warning flags as CI.

CI

  • Alpine latest-stable is 3.23 (apk-tools v3). setup-alpine still bootstraps with a static apk 2.14 binary, so apk add failed on qemu ppc64le with database: file format is invalid. The Alpine workflow now pins v3.22 (last apk v2) and sets fail-fast: false.
  • Alpine aarch64 GCC 14 rejects vreinterpretq_u8_s8(vcgtq_s8(...)) because vcgtq_s8 already returns uint8x16_t (ACLE). The NEON leading-byte count now uses that return type directly.
Open in Web Open in Cursor 

Apply SSE2/NEON (SWAR fallback) to the IDNA hot paths: is_ascii,
ascii_map, forbidden-domain checks, label-dot scan, and UTF-8/UTF-32
ASCII widen/narrow. Keep scalar tails for short strings and big-endian
targets such as s390x.

Co-authored-by: Yagiz Nizipli <anonrig@users.noreply.github.com>
@anonrig
anonrig marked this pull request as ready for review August 17, 2026 18:51
@cursor

cursor Bot commented Aug 17, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

cursoragent and others added 3 commits August 17, 2026 19:07
The common to_ascii path was two full scans (is_ascii, then ascii_map).
Lowercase in place and test the high bit in one pass, matching ada-url's
to_lower_ascii. Drop SIMD that increased instruction count on short
domains: forbidden-code-point table stays a 4-way unroll, label-dot
scan is scalar again, and UTF widen/pack share a single load.

Process 32 bytes per lowercase/is_ascii iteration and accumulate
utf8_length_from_utf32 instead of horizontally summing each block.

Co-authored-by: Yagiz Nizipli <anonrig@users.noreply.github.com>
latest-stable is now 3.23 (apk-tools v3). jirutka/setup-alpine still
bootstraps the chroot with a static apk 2.14 binary, so apk add fails
with "database: file format is invalid" on qemu ppc64le. Pin to v3.22
(last apk v2) and disable fail-fast so one arch does not cancel the rest.

Co-authored-by: Yagiz Nizipli <anonrig@users.noreply.github.com>
Alpine aarch64 GCC 14 rejects vreinterpretq_u8_s8(vcgtq_s8(...)):
vcgtq_s8 already returns uint8x16_t, so the reinterpret is a
vector-type conversion error under -Wfatal-errors.

Co-authored-by: Yagiz Nizipli <anonrig@users.noreply.github.com>
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