Avoid reducing unused Vector2 and Vector3 elements - #133527
Conversation
|
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. |
|
Tagging subscribers to this area: @dotnet/area-system-numerics |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The new “ignore upper elements” tests are misleading because AsVector2/AsVector3 drop the upper Vector128 lane, so the test inputs don’t actually validate the intended behavior.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Lite
Findings: 2
New issues introduced by this change (4)
| Severity | Finding |
|---|---|
src/libraries/System.Numerics.Vectors/tests/Vector2Tests.cs — This test’s upper parameter doesn’t actually influence any “upper elements” of Vector2:… |
|
src/libraries/System.Numerics.Vectors/tests/Vector3Tests.cs — This test’s upper parameter doesn’t actually affect any stored “upper element” for Vector3:… |
|
src/libraries/System.Private.CoreLib/src/System/Numerics/Vector2.cs — internal static Vector2 Sum(Vector128<float> value) returns a Vector2 (not a scalar sum), which… |
|
src/libraries/System.Private.CoreLib/src/System/Numerics/Vector3.cs — internal static Vector3 Sum(Vector128<float> value) returns a Vector3 (not a scalar sum), which… |
What changed in this PR
This PR updates System.Numerics.Vector2/Vector3 reduction-style operations (Sum/Dot/Length/Distance/Normalize/Reflect) to avoid doing 4-lane reductions that effectively incorporate an extra zero lane, keeping intermediate computations in Vector128<float> while reducing only the meaningful elements.
Changes:
- Replaces
Vector128.*reductions forVector2/Vector3with custom lane-shuffle reductions that only reduce the meaningful elements. - Updates
NormalizeandReflectto use the new reduction helpers while keeping intermediates in SIMD form. - Expands tests to cover signed-zero behavior and non-finite inputs, and adds targeted reduction-related assertions.
| File | Description |
|---|---|
| src/libraries/System.Private.CoreLib/src/System/Numerics/Vector3.cs | Reworks reduction-based APIs to reduce only X/Y/Z and avoid the implicit extra lane. |
| src/libraries/System.Private.CoreLib/src/System/Numerics/Vector2.cs | Reworks reduction-based APIs to reduce only X/Y and avoid the implicit extra lane. |
| src/libraries/System.Numerics.Vectors/tests/Vector3Tests.cs | Adds additional reduction-focused coverage (signed zero, non-finite inputs, reflection/normalization). |
| src/libraries/System.Numerics.Vectors/tests/Vector2Tests.cs | Adds additional reduction-focused coverage (signed zero, reflection/normalization). |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
814696c to
dc75f3d
Compare
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Vector2.SumAndBroadcast does not actually broadcast a single computed sum (it can produce lane-dependent NaN payloads) and should be adjusted to replicate one lane’s sum consistently.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Lite
Findings: 1
New issues introduced by this change (1)
| Severity | Finding |
|---|---|
src/libraries/System.Private.CoreLib/src/System/Numerics/Vector2.cs — SumAndBroadcast currently returns (x + y, y + x) (via AsVector2()), which is not a true… |
Issues resolved since last review (4)
| Severity | Finding |
|---|---|
src/libraries/System.Private.CoreLib/src/System/Numerics/Vector3.cs — internal static Vector3 Sum(Vector128<float> value) returns a Vector3 (not a scalar sum), which… View resolved comment |
|
src/libraries/System.Private.CoreLib/src/System/Numerics/Vector2.cs — internal static Vector2 Sum(Vector128<float> value) returns a Vector2 (not a scalar sum), which… View resolved comment |
|
src/libraries/System.Numerics.Vectors/tests/Vector3Tests.cs — This test’s upper parameter doesn’t actually affect any stored “upper element” for Vector3:… View resolved comment |
|
src/libraries/System.Numerics.Vectors/tests/Vector2Tests.cs — This test’s upper parameter doesn’t actually influence any “upper elements” of Vector2:… View resolved comment |


Reduce only the meaningful
Vector2andVector3elements, avoiding zero-extension and extra arithmetic from four-element reductions. Keep intermediates in SIMD form, with scalar reductions for dot products, lengths, and distances, and vector results for normalization and reflection. The implementations use portableVector128operations, without architecture-specific paths.This also preserves negative zero when all summed elements are negative zero, rather than adding an irrelevant positive zero. Adds coverage for signed zero, grouping, nonfinite inputs, and unused upper elements.
Addresses the
Vector2/Vector3reduction portion of #133297.Vector4, JIT simplifications, and loop alignment are out of scope.Release x64 codegen for the local
VectorBenchloops, compared with the original upstream implementation:LengthSquared2LengthSquared3Normalize3Reflect3These are whole-method sizes including alignment; the
LengthSquared2loop itself shrinks from 46 to 30 bytes.The portable
Vector3broadcast costs five instructions / 25 reduction bytes versus four / 18 for an investigated SSE specialization. In local Ryzen 9 7950XVectorBenchloops over 1,000 vectors,Normalize3was within 1% faster andReflect3was 11-12% slower than that specialization in both runtime orders. This comparison is against the rejected specialization, not upstream; portability is preferred over separate target-specific reduction paths. ARM and WASM performance has not been measured.Note
This description was drafted with GitHub Copilot.