Skip to content

Vectorize Base64 DecodeFromUtf8InPlace#131333

Merged
tannergooding merged 1 commit into
dotnet:mainfrom
tannergooding:tannergooding-base64-inplace-perf-regression
Jul 25, 2026
Merged

Vectorize Base64 DecodeFromUtf8InPlace#131333
tannergooding merged 1 commit into
dotnet:mainfrom
tannergooding:tannergooding-base64-inplace-perf-regression

Conversation

@tannergooding

Copy link
Copy Markdown
Member

Fixes #105707.

Base64.DecodeFromUtf8InPlace (and the Base64Url in-place decode) ran a pure scalar loop, unlike DecodeFrom which already had AVX-512 / AVX2 / AdvSimd / SSSE3 fast paths. That scalar loop is why the 200 MB Base64DecodeInPlace benchmark 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 / Vector128Decode helpers for the in-place path, mirroring DecodeFrom exactly. 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-vector src, 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:

Config Time vs scalar
Scalar (DOTNET_EnableHWIntrinsic=0, prior behavior) 63.9 ms 1.0x
Vector128 (DOTNET_EnableAVX2=0) 24.4 ms 2.6x
AVX2/AVX-512 10.9 ms 5.9x

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.Tests pass (52,929) with the debug AssertRead/AssertWrite bounds checks active, including under DOTNET_EnableAVX2=0 and DOTNET_EnableHWIntrinsic=0. The existing EncodeAndDecodeInPlace test 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.

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>
Copilot AI review requested due to automatic review settings July 24, 2026 16:53
@github-actions github-actions Bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jul 24, 2026
@azure-pipelines

Copy link
Copy Markdown
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.

@tannergooding tannergooding added area-System.Buffers and removed area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI labels Jul 24, 2026
@tannergooding

Copy link
Copy Markdown
Member Author

This is libraries, not runtime related.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-buffers
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 reuses Avx512Decode / Avx2Decode / AdvSimdDecode / Vector128Decode.
  • Update sourceIndex / destIndex based 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

@tannergooding
tannergooding enabled auto-merge (squash) July 25, 2026 02:06
@tannergooding
tannergooding merged commit f599e09 into dotnet:main Jul 25, 2026
152 checks passed
@tannergooding
tannergooding deleted the tannergooding-base64-inplace-perf-regression branch July 25, 2026 02:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Perf] Windows/x64: Regression in System.Buffers.Text.Tests.Base64EncodeDecodeInPlaceTests

3 participants