Skip to content

perf: SIMD percent-decode and form-urlencoded encode/decode - #1231

Closed
anonrig wants to merge 2 commits into
mainfrom
cursor/simd-percent-encode-8260
Closed

perf: SIMD percent-decode and form-urlencoded encode/decode#1231
anonrig wants to merge 2 commits into
mainfrom
cursor/simd-percent-encode-8260

Conversation

@anonrig

@anonrig anonrig commented Aug 21, 2026

Copy link
Copy Markdown
Member

Percent-encode/decode and application/x-www-form-urlencoded were still scalar after the absolute-URL fast path grew nibble-table SIMD (#1216). Issue #1120 called this out. Prior attempts missed:

This keeps the inlined 8-byte percent_encode_index and converts the remaining hot tails.

What changed

  • Decode: 16-byte SSE2/NEON scans for % (and + in form-urlencoded). Dense %XX runs decode five triplets per 16-byte load (SSSE3 pshufb + maddubs, or NEON tbl).
  • Encode tail: consteval nibble tables from each official 32-byte percent-encode set (static_assert that every table matches bit_at). SIMD classify is noinline and only runs when the remaining suffix is at least 48 bytes, so SetHash / UserInfo / the official percent_encode examples stay on scalar bit_at.
  • Search params: to_string() appends encoded keys/values in one pass, mapping space to + instead of percent-encode then replace.

Local results

ctest --output-on-failure --test-dir build: 345/345 passed (g++ 13.3, Debug).

Release, g++ 13.3, same inputs vs main at 18ca958 (wall-clock, noisy):

Path main this PR
percent_decode of 80×%3A ~216 ns ~98 ns
form_urlencoded_decode (prefix + dense %XX + +) ~199 ns ~117 ns
percent_encode tiny (hello world) ~32 ns ~29 ns
percent_encode ~200-byte fragment ~340 ns ~222 ns
url_search_params::to_string (dense keys/values) ~3.5 µs ~1.6 µs

No public signature or object-layout change. New form_urlencoded_encode / form_urlencoded_encode_append are internal (ada::unicode).

Closes #1120.

Open in Web Open in Cursor 

Percent-encode tails and dense %XX decode were still scalar after the
absolute-URL fast path grew nibble-table SIMD. Scan 16-byte windows with
SSSE3/NEON (pshufb/tbl classifiers, five %XX groups per load) while
keeping percent_encode_index inlined so short setters stay cheap.
Search-params to_string now encodes directly with '+' for space.

Closes #1120.

Co-authored-by: Yagiz Nizipli <anonrig@users.noreply.github.com>
@codecov

codecov Bot commented Aug 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.76404% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.57%. Comparing base (18ca958) to head (9fcf35d).

Files with missing lines Patch % Lines
src/unicode.cpp 90.80% 1 Missing and 15 partials ⚠️
include/ada/url_search_params-inl.h 0.00% 0 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1231      +/-   ##
==========================================
+ Coverage   63.00%   63.57%   +0.56%     
==========================================
  Files          38       38              
  Lines        7628     7741     +113     
  Branches     3496     3516      +20     
==========================================
+ Hits         4806     4921     +115     
- Misses        749      750       +1     
+ Partials     2073     2070       -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

CodSpeed on #1218/#1230 showed 16-byte encode classify regresses
SetHash and the official percent_encode examples. Gate the nibble-table
walk on a 48-byte remainder and keep it noinline so setter-sized
percent_encode stays a tight scalar tail.

Co-authored-by: Yagiz Nizipli <anonrig@users.noreply.github.com>
@codspeed-hq

codspeed-hq Bot commented Aug 21, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 5.48%

❌ 12 regressed benchmarks
✅ 33 untouched benchmarks
⏩ 4 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
SetHash 1.7 µs 2.1 µs -15.58%
Decode 3.6 µs 3.9 µs -9.38%
Fragment 3.3 µs 3.5 µs -4.91%
SetHostname 4.5 µs 4.7 µs -4.33%
SetProtocol 2.6 µs 2.7 µs -4.32%
SetHref 11 µs 11.5 µs -4.16%
C0Control 3.3 µs 3.5 µs -4.11%
Query 3.3 µs 3.5 µs -4.07%
SpecialQuery 3.3 µs 3.5 µs -4.07%
SetPort 2.9 µs 3 µs -3.83%
SetPassword 1.8 µs 1.9 µs -3.23%
SetPasswordInsertAndClear 1.9 µs 2 µs -3.03%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing cursor/simd-percent-encode-8260 (9fcf35d) with main (18ca958)

Open in CodSpeed

Footnotes

  1. 4 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@anonrig anonrig closed this Aug 21, 2026
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.

SIMD-accelerated percent_encode / percent_decode

2 participants