Inline GpuUInt128 Mul64 helper
This commit is contained in:
1 parent
9c08cf48a7
commit
1809ba95b3
8 files changed
+108
-114
No files matched your search
@@ -456,47 +456,46 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
|
||||
return yHigh;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Mul64(in ReadOnlyGpuUInt128 other) => Mul64Internal(other.High, other.Low);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Mul64(GpuUInt128 other) => Mul64Internal(other.High, other.Low);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Mul64Internal(ulong otherHigh, ulong otherLow)
|
||||
{
|
||||
// Multiply this.Low (assumed 64-bit value) by full 128-bit other
|
||||
ulong operand = Low;
|
||||
Low = operand * otherLow;
|
||||
ulong highProduct = operand * otherHigh;
|
||||
High = highProduct + MulHigh(operand, otherLow);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// Kept by-value to coexist with the ref readonly overload below while still signaling read-only intent.
|
||||
public void MulMod(ReadOnlyGpuUInt128 value, in ReadOnlyGpuUInt128 modulus)
|
||||
{
|
||||
GpuUInt128 multiplicand = this;
|
||||
GpuUInt128 multiplier = value.ToMutable();
|
||||
|
||||
High = 0UL;
|
||||
Low = 0UL;
|
||||
GpuUInt128 result = new();
|
||||
|
||||
while (!multiplier.IsZero)
|
||||
{
|
||||
if ((multiplier.Low & 1UL) != 0UL)
|
||||
{
|
||||
AddMod(multiplicand, modulus);
|
||||
result.Add(multiplicand);
|
||||
if (result.CompareTo(modulus) >= 0)
|
||||
{
|
||||
result.Sub(modulus);
|
||||
}
|
||||
}
|
||||
|
||||
multiplicand.ShiftLeft(1);
|
||||
// The allocating shift path remains faster than the in-place variants in the GpuUInt128MulMod benchmarks.
|
||||
multiplicand = multiplicand << 1;
|
||||
if (multiplicand.CompareTo(modulus) >= 0)
|
||||
{
|
||||
multiplicand.Sub(modulus);
|
||||
}
|
||||
|
||||
multiplier.ShiftRight(1);
|
||||
if (multiplier.High == 0UL)
|
||||
{
|
||||
multiplier.Low >>= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong newLow = (multiplier.Low >> 1) | (multiplier.High << 63);
|
||||
multiplier.High >>= 1;
|
||||
multiplier.Low = newLow;
|
||||
}
|
||||
}
|
||||
|
||||
High = result.High;
|
||||
Low = result.Low;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -743,6 +742,15 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
|
||||
Low = lowPart;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Mul64(ref GpuUInt128 value, ulong otherHigh, ulong otherLow)
|
||||
{
|
||||
ulong operand = value.Low;
|
||||
value.Low = operand * otherLow;
|
||||
ulong highProduct = operand * otherHigh;
|
||||
value.High = highProduct + MulHigh(operand, otherLow);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static GpuUInt128 DivideExact(in ReadOnlyGpuUInt128 dividend, in ReadOnlyGpuUInt128 divisor)
|
||||
{
|
||||
@@ -868,54 +876,6 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
|
||||
// (e.g., Montgomery n', R2) and provide device-friendly accessors.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modular multiplication using <see cref="BigInteger"/> reduction. This
|
||||
/// method is intended for validation only and should not be used inside
|
||||
/// GPU kernels.
|
||||
/// </summary>
|
||||
internal void MulModBigInteger(in ReadOnlyGpuUInt128 other, in ReadOnlyGpuUInt128 modulus)
|
||||
{
|
||||
var left = (BigInteger)(UInt128)this;
|
||||
var right = (BigInteger)(UInt128)other;
|
||||
var mod = (BigInteger)(UInt128)modulus;
|
||||
var reduced = (UInt128)((left * right) % mod);
|
||||
High = (ulong)(reduced >> 64);
|
||||
Low = (ulong)reduced;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Experimental limb-based reduction. The current implementation performs
|
||||
/// repeated subtractions and becomes extremely slow for large remainders.
|
||||
/// Kept for future optimization work.
|
||||
/// </summary>
|
||||
internal void MulModByLimb(in ReadOnlyGpuUInt128 other, in ReadOnlyGpuUInt128 modulus)
|
||||
{
|
||||
// TODO: Relocate this limb-based reducer to the benchmark project once the production
|
||||
// pipeline switches to the faster allocating legacy path demonstrated in the benchmarks.
|
||||
MultiplyFull(this, other, out var p3, out var p2, out var p1, out var p0);
|
||||
|
||||
High = p3;
|
||||
Low = p2;
|
||||
while (CompareTo(modulus) >= 0)
|
||||
{
|
||||
Sub(modulus);
|
||||
}
|
||||
|
||||
ulong limb = p1;
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
ShiftLeft(64);
|
||||
Low = limb;
|
||||
while (CompareTo(modulus) >= 0)
|
||||
{
|
||||
Sub(modulus);
|
||||
}
|
||||
|
||||
limb = p0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void MulMod(ulong value, in ReadOnlyGpuUInt128 modulus)
|
||||
{
|
||||
|
||||
@@ -71,7 +71,7 @@ internal static class IncrementalKernels
|
||||
// TODO: Is this expected that the value of kStart is modified?
|
||||
GpuUInt128 k = kStart + (GpuUInt128)idx;
|
||||
GpuUInt128 q = twoP;
|
||||
q.Mul64(k);
|
||||
GpuUInt128.Mul64(ref q, k.High, k.Low);
|
||||
q.Add(GpuUInt128.One);
|
||||
ReadOnlyGpuUInt128 readOnlyQ = q.AsReadOnly();
|
||||
|
||||
@@ -174,7 +174,7 @@ internal static class IncrementalKernels
|
||||
|
||||
GpuUInt128 k = kStart + (GpuUInt128)idx;
|
||||
GpuUInt128 q = twoP;
|
||||
q.Mul64(k);
|
||||
GpuUInt128.Mul64(ref q, k.High, k.Low);
|
||||
q.Add(GpuUInt128.One);
|
||||
ReadOnlyGpuUInt128 readOnlyQ = q.AsReadOnly();
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ internal static class Pow2ModKernels
|
||||
// TODO: kStart is modified after this. Is this expected?
|
||||
kStart.Add(idx);
|
||||
GpuUInt128 q = twoP;
|
||||
q.Mul64(kStart);
|
||||
GpuUInt128.Mul64(ref q, kStart.High, kStart.Low);
|
||||
q.Add(GpuUInt128.One);
|
||||
ReadOnlyGpuUInt128 readOnlyQ = q.AsReadOnly();
|
||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||
@@ -158,7 +158,7 @@ internal static class Pow2ModKernels
|
||||
|
||||
GpuUInt128 k = kStart + (GpuUInt128)idx;
|
||||
GpuUInt128 q = twoP;
|
||||
q.Mul64(k);
|
||||
GpuUInt128.Mul64(ref q, k.High, k.Low);
|
||||
q.Add(GpuUInt128.One);
|
||||
ReadOnlyGpuUInt128 readOnlyQ = q.AsReadOnly();
|
||||
// Small-cycles in-kernel early rejection from device table
|
||||
|
||||
Reference in new issue
Block a user