Repository navigation
Normalize SIMD pipelines using TensorPrimitives_ - #3161
Conversation
There was a problem hiding this comment.
Pull request overview
This PR normalizes several SIMD-heavy pipelines around a new internal TensorPrimitives_ compatibility API (mirroring System.Numerics.Tensors.TensorPrimitives) and removes older, parallel SIMD implementations in favor of a single runtime-shaped dispatch model. It also adds targeted tests and benchmarks to validate correctness across scalar/SIMD width boundaries and to track performance impacts.
Changes:
- Introduce and adopt
TensorPrimitives_-style span operations for common add/multiply/divide/negate/max workloads across codecs and processing helpers. - Refactor/normalize affine
Vector4conversion traversal and pixel blender traversal using operator-based SIMD dispatch, and remove legacy JPEG converter SIMD class hierarchy. - Add tests/benchmarks covering SIMD boundary behavior for PNG filters, ICC LUT normalization, pixel blending, convolution kernels, and Vector4 affine transforms.
Reviewed changes
Copilot reviewed 109 out of 111 changed files in this pull request and generated 1 comment.
Show a summary per file
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 109 out of 111 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
tests/ImageSharp.Benchmarks/Codecs/Png/PngFilterEncode.cs:87
AverageFilter.Encodeexpectsuint bytesPerPixel, but this benchmark passesBytesPerPixelas anint, which will not compile once theEncodesignature is updated. Cast the constant touintto match the API (consistent with other call sites in the PR).

Prerequisites
Description
This change introduces the internal
TensorPrimitives_compatibility type and uses the runtime's genericoperator-based SIMD model to normalize ImageSharp's span operations. The compatibility API follows
System.Numerics.Tensors.TensorPrimitivesso it can be replaced by the runtime implementation when ImageSharpmoves to .NET 10.
The migration removes the replaced legacy SIMD implementations rather than retaining parallel pipelines. It also
uses portable
Vector128,Vector256, andVector512APIs where the target framework exposes them, withplatform-specific intrinsics isolated behind compatibility helpers only where .NET 8 has no equivalent API.
The normalized implementation follows runtime main's widest-supported dispatch for every supported element type:
AVX-512 is selected when one complete
Vector512<T>fits, and shorter inputs fall through to a width that fits.SIMD layouts, masks, shuffles, overlap handling, scalar equivalence, and width-selection decisions are documented inline.
Performance
The tables below compare the implementation removed by this PR ("Before") with the retained normalized
implementation ("After"). Lower is better.
MemoryDiagnoserwas enabled for every row containing an allocation result.0 B / 0 Bmeans BenchmarkDotNetmeasured zero managed bytes allocated per operation for both the before and after implementations.
Tensor primitive migrations
JPEG packing pipelines
Additional migrations found during the library-wide audit
Byte-add production callers
The PNG rows compare the exact AVX2 loop removed from
UpFilter.Decodewith the productionTensorPrimitives_.Add<byte>call in the same benchmark run. The runtime-shaped implementation has asmall-buffer dispatch cost, crosses over by 2,048 payload bytes, and benefits increasingly from AVX-512 on
larger scanlines.
Binary size
Release
net8.0artifacts were built frommainand this PR using the same .NET SDK 10.0.302 installation andproject settings.
mainSixLabors.ImageSharp.dll)SixLabors.ImageSharp.pdb)The package reduction is smaller than the assembly reduction because the removed generated IL is highly
repetitive and therefore compresses efficiently inside the NuGet package.
NativeAOT binary-size impact
mainSizoscope attributes the remaining increase predominantly to statically specialized SIMD/operator code. Further material sharing would require runtime dispatch, reduced inlining, or eager initialization, potentially harming performance.
Both executables completed the representative processing and codec workload successfully with identical output.