Remove unsafe code from System.Numerics.BigIntegerCalculator.ShiftRot#127847
Remove unsafe code from System.Numerics.BigIntegerCalculator.ShiftRot#127847EgorBo wants to merge 2 commits intodotnet:mainfrom
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Tagging subscribers to this area: @dotnet/area-system-numerics |
There was a problem hiding this comment.
Pull request overview
This PR refactors System.Runtime.Numerics' internal BigIntegerCalculator shift/rotate implementation to remove direct LoadUnsafe/StoreUnsafe usage from the SIMD paths, which sit underneath BigInteger shift and rotate operations.
Changes:
- Reworks
LeftShiftSelfto use sliced spans plusVector128/256/512.CreateandCopyToinstead of ref+offset-based vector loads/stores. - Applies the same span-based vector load/store pattern to
RightShiftSelf. - Changes loop bookkeeping from integer offsets to shrinking
Span<nuint>windows while keeping the existing carry/scalar fallback structure.
|
Note AI-generated benchmark. @EgorBot -arm -amd using System;
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args);
public class Bench
{
private BigInteger _value;
[Params(128, 1024, 8192)]
public int Bits { get; set; }
[GlobalSetup]
public void Setup()
{
var rng = new Random(42);
byte[] bytes = new byte[Bits / 8];
rng.NextBytes(bytes);
bytes[^1] &= 0x7F; // ensure positive
_value = new BigInteger(bytes);
}
[Benchmark]
public BigInteger LeftShift() => _value << 7;
[Benchmark]
public BigInteger RightShift() => _value >> 7;
} |
|
@EgorBot -arm -amd using System;
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args);
public class Bench
{
private BigInteger _value;
[Params(128, 1024, 8192)]
public int Bits { get; set; }
[GlobalSetup]
public void Setup()
{
var rng = new Random(42);
byte[] bytes = new byte[Bits / 8];
rng.NextBytes(bytes);
bytes[^1] &= 0x7F; // ensure positive
_value = new BigInteger(bytes);
}
[Benchmark]
public BigInteger LeftShift() => _value << 7;
[Benchmark]
public BigInteger RightShift() => _value >> 7;
} |
|
|
||
| ref nuint start = ref MemoryMarshal.GetReference(bits); | ||
| int offset = bits.Length; | ||
| Span<nuint> data = bits; |
There was a problem hiding this comment.
Is using a different local needed to get the good codegen (vs. just using bits)?
I guess it's the same as for the method below with remaining.
| Vector128<nuint> current = Vector128.Create(remaining) >> shift; | ||
| Vector128<nuint> carries = Vector128.Create(remaining.Slice(1)) << back; | ||
| Vector128<nuint> current = Vector128.Create((ReadOnlySpan<nuint>)remaining) >> shift; | ||
| Vector128<nuint> carries = Vector128.Create((ReadOnlySpan<nuint>)remaining.Slice(1)) << back; |
There was a problem hiding this comment.
Why are all these extra casts needed?
|
|
||
| nuint carry2 = 0; | ||
| for (int i = 0; i < offset; i++) | ||
| for (int i = 0; i < data.Length; i++) |
There was a problem hiding this comment.
Can we similarly delete the else branch in this method too as was done for RightShiftSelf?
Note
This PR is AI-generated.
+5b asm diffs