Vectorize Base64 DecodeFromUtf8InPlace#131333
Merged
tannergooding merged 1 commit intoJul 25, 2026
Merged
Conversation
Reuse the existing SIMD decode helpers for the in-place path, which previously ran a pure scalar loop. Safe in place because the write cursor trails the read cursor by 25%, so each vector store ends at or before the next load. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Member
Author
|
This is libraries, not runtime related. |
Contributor
|
Tagging subscribers to this area: @dotnet/area-system-buffers |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the CoreLib Base64 in-place UTF-8 decoding implementation to use the existing SIMD-accelerated decode helpers (AVX-512 / AVX2 / AdvSimd / Vector128) that are already used by the non-in-place DecodeFrom path, improving throughput for large in-place decodes while preserving the existing scalar tail / padding validation behavior.
Changes:
- Add a SIMD fast path to
Base64Helper.DecodeFromUtf8InPlace(guarded by#if NET) that reusesAvx512Decode/Avx2Decode/AdvSimdDecode/Vector128Decode. - Update
sourceIndex/destIndexbased on the SIMD progress so the existing scalar loop and final-block handling continue correctly.
Show a summary per file
| File | Description |
|---|---|
| src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Helper/Base64DecoderHelper.cs | Adds an in-place SIMD decode loop mirroring the existing DecodeFrom vectorized pipeline, then continues with the existing scalar remainder / padding validation. |
Copilot's findings
- Files reviewed: 1/1 changed files
- Comments generated: 0
Open
3 tasks
lewing
approved these changes
Jul 25, 2026
tannergooding
enabled auto-merge (squash)
July 25, 2026 02:06
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
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #105707.
Base64.DecodeFromUtf8InPlace(and the Base64Url in-place decode) ran a pure scalar loop, unlikeDecodeFromwhich already had AVX-512 / AVX2 / AdvSimd / SSSE3 fast paths. That scalar loop is why the 200 MBBase64DecodeInPlacebenchmark was ~395 ms, and why it was sensitive enough to layout that #105262 -- a correctness fix that only touched the once-per-call tail, leaving the hot loop's codegen byte-for-byte identical -- surfaced as a ~12% regression from alignment/layout alone.This reuses the existing
Avx512Decode/Avx2Decode/AdvSimdDecode/Vector128Decodehelpers for the in-place path, mirroringDecodeFromexactly. The decode is safe in place because the write cursor trails the read cursor by 25% (3 bytes written per 4 read), so each vector store -- including its zero-padded overshoot -- ends at or before the next vector load and never clobbers source that hasn't been read yet. The final overshoot of whichever path runs last lands strictly before the post-vectorsrc, so the narrower SIMD phases and the scalar remainder read unclobbered data.Measured locally on win-x64 (AVX-512 capable), 200 MB in-place decode, release runtime:
DOTNET_EnableHWIntrinsic=0, prior behavior)DOTNET_EnableAVX2=0)So this doesn't just recover the 12% regression -- it makes the in-place path ~2.6-5.9x faster depending on hardware.
Testing: all
System.Memory.Testspass (52,929) with the debugAssertRead/AssertWritebounds checks active, including underDOTNET_EnableAVX2=0andDOTNET_EnableHWIntrinsic=0. The existingEncodeAndDecodeInPlacetest roundtrips every length 1-256, which exercises all in-place SIMD widths with byte-exact overlap validation. No JIT changes.Note
This PR was authored with the assistance of GitHub Copilot.