Add Vector.Sum<T> for netstandard2.0#273
Conversation
The Vector.Sum<T> API has been available in the .NET runtime since .NET 6.0 but was never included in this package. Implements Sum via Dot(value, Vector<T>.One) so that the .NET Framework JIT can intrinsify the call through Dot. Fixes dotnet#272 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@dotnet-policy-service agree company="Microsoft" |
|
The api surface for this package is effectively frozen and not being versioned anymore. Users can polyfill the api using extension members if really desired and can otherwise define their own utility function trivially |
tannergooding
left a comment
There was a problem hiding this comment.
This does not meet the contribution bar: https://github.com/dotnet/maintenance-packages?tab=readme-ov-file#contribution-bar
We only consider fixes that unblock critical issues
- New features and APIs are not accepted
- Infrastructure changes and other non-functional changes are considered
|
Thanks @tannergooding for the explanation here and the additional context offline. Closing this, my motivation was that adding The officially supported path is to multi-target and let the modern TFM bind directly to the inbox type, which is the normal and expected scenario for maximizing performance. On my side, moving the library to C# 14 also let me drop all the |
Summary
Adds
Vector.Sum<T>(Vector<T>)to theSystem.Numerics.Vectorspackage for netstandard2.0 and .NET Framework consumers.This API has been available in the .NET runtime since .NET 6.0 but was never included in this package. Other comparable static methods (Dot, Abs, Min, Max, SquareRoot, etc.) are all present.
Fixes #272
Implementation
Sumis implemented asDot(value, Vector<T>.One)rather than a standalone method with its ownScalarAddloop:Why Dot?
The .NET Framework JIT has a hardcoded list of
Vectormethods it recognizes as intrinsics.Dotis on that list; a newSummethod is not. Adding[Intrinsic]to a new method doesn't help — the Framework JIT ignores it for methods it doesn't know about.A standalone
Sumusing theScalarAdd/typeof(T)pattern (same asDotProduct) would run the full managed code path on .NET Framework: a generic loop where each iteration does a 10-waytypeof(T)chain. Benchmarks show this is 11-17x slower than routing throughDot.By expressing
SumasDot(value, One), we piggyback onDot's existing intrinsification. The multiply-by-one is essentially free at the hardware level.Runtime behavior
On .NET 6+, the package's
_._placeholder means this code is never used — the runtime's intrinsifiedVector.Sumtakes over via type forwarding. The Dot-based implementation only matters on .NET Framework, where it provides SIMD-accelerated performance.We verified this: calling
Vector.Sumthrough a netstandard2.0 library on .NET 6/8 produces identical performance to calling the runtime'sVector.Sumdirectly (0.85 ns vs 0.85 ns, ratio 1.00). The type forwarding works with zero overhead.Benchmarks
All methods go through a netstandard2.0 class library compiled once, then consumed by each runtime — simulating the real scenario for library authors.
What's being benchmarked
Each row corresponds to a different Sum strategy, all compiled into a netstandard2.0 library:
Results
#ifpolyfill (manual loop)Key observations:
Dotpath.Dot(0.33 ns) because the runtime'sVector.Sumuses a different instruction sequence (shuffle+add vs dpps). This is a JIT codegen detail, not a package issue.#ifpolyfill is locked at ~2.1 ns everywhere — a netstandard2.0 library compiles the#elsemanual loop, which never improves regardless of the consumer's runtime. The package fix is 2.4x faster on .NET 6/8.Dotinstead of a standalone implementation.