170 lines
5.1 KiB
C#
170 lines
5.1 KiB
C#
using System.Runtime.CompilerServices;
|
|
using ILGPU;
|
|
using ILGPU.Algorithms;
|
|
using ILGPU.Runtime;
|
|
|
|
namespace PerfectNumbers.Core.Gpu;
|
|
|
|
internal enum HeuristicGpuDivisorTableKind : byte
|
|
{
|
|
GroupA = 0,
|
|
GroupBEnding1 = 1,
|
|
GroupBEnding7 = 7,
|
|
GroupBEnding9 = 9,
|
|
Combined = 255,
|
|
}
|
|
|
|
internal static class PrimeTesterKernels
|
|
{
|
|
// GPU kernel: small-prime sieve only. Returns 1 if passes sieve (probable prime), 0 otherwise.
|
|
public static void SmallPrimeSieveKernel(
|
|
Index1D index,
|
|
ArrayView<ulong> numbers,
|
|
ArrayView<uint> smallPrimes,
|
|
ArrayView<uint> smallPrimesLastOne,
|
|
ArrayView<uint> smallPrimesLastSeven,
|
|
ArrayView<uint> smallPrimesLastThree,
|
|
ArrayView<uint> smallPrimesLastNine,
|
|
ArrayView<ulong> smallPrimesPow2,
|
|
ArrayView<ulong> smallPrimesPow2LastOne,
|
|
ArrayView<ulong> smallPrimesPow2LastSeven,
|
|
ArrayView<ulong> smallPrimesPow2LastThree,
|
|
ArrayView<ulong> smallPrimesPow2LastNine,
|
|
ArrayView<byte> results)
|
|
{
|
|
ulong n = numbers[index];
|
|
byte result = 1;
|
|
|
|
ArrayView<uint> primes = smallPrimes;
|
|
ArrayView<ulong> primeSquares = smallPrimesPow2;
|
|
ulong lastDigit = n % 10UL;
|
|
switch (lastDigit)
|
|
{
|
|
case 1UL:
|
|
primes = smallPrimesLastOne;
|
|
primeSquares = smallPrimesPow2LastOne;
|
|
break;
|
|
case 3UL:
|
|
primes = smallPrimesLastThree;
|
|
primeSquares = smallPrimesPow2LastThree;
|
|
break;
|
|
case 7UL:
|
|
primes = smallPrimesLastSeven;
|
|
primeSquares = smallPrimesPow2LastSeven;
|
|
break;
|
|
case 9UL:
|
|
primes = smallPrimesLastNine;
|
|
primeSquares = smallPrimesPow2LastNine;
|
|
break;
|
|
}
|
|
|
|
int length = (int)primes.Length;
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
ulong primeSquare = primeSquares[i];
|
|
if (primeSquare > n)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ulong prime = primes[i];
|
|
if (prime == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (n % prime == 0UL)
|
|
{
|
|
result = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
results[index] = result;
|
|
}
|
|
|
|
public static void HeuristicTrialDivisionKernel(
|
|
Index1D index,
|
|
ArrayView<int> resultFlag,
|
|
ulong n,
|
|
ulong maxDivisorSquare,
|
|
HeuristicGpuDivisorTableKind tableKind,
|
|
HeuristicGpuDivisorTables tables)
|
|
{
|
|
byte nMod10 = (byte)(n % 10UL);
|
|
ArrayView1D<ulong, Stride1D.Dense> divisors = tables.SelectDivisors(tableKind, nMod10);
|
|
ArrayView1D<ulong, Stride1D.Dense> divisorSquares = tables.SelectDivisorSquares(tableKind, nMod10);
|
|
if (divisors.Length == 0 || divisorSquares.Length == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int divisorLength = (int)divisors.Length;
|
|
int squareLength = (int)divisorSquares.Length;
|
|
int threadIndex = index;
|
|
if (threadIndex >= divisorLength || threadIndex >= squareLength)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ulong divisorSquare = divisorSquares[threadIndex];
|
|
if (divisorSquare == 0UL || divisorSquare > maxDivisorSquare)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ulong divisor = divisors[threadIndex];
|
|
if (divisor == 0UL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (n % divisor == 0UL)
|
|
{
|
|
resultFlag[0] = 1;
|
|
}
|
|
}
|
|
|
|
public static void SharesFactorKernel(Index1D index, ArrayView<ulong> numbers, ArrayView<byte> results)
|
|
{
|
|
ulong n = numbers[index];
|
|
ulong m = 63UL - (ulong)XMath.LeadingZeroCount(n);
|
|
ulong gcd = BinaryGcdGpu(n, m);
|
|
results[index] = gcd == 1UL ? (byte)0 : (byte)1;
|
|
}
|
|
|
|
private static ulong BinaryGcdGpu(ulong u, ulong v)
|
|
{
|
|
// TODO: Replace this inline GPU binary GCD with the kernel extracted from
|
|
// GpuUInt128BinaryGcdBenchmarks via GpuKernelPool so device callers reuse the
|
|
// fully unrolled ladder instead of this branchy fallback.
|
|
bool uIsZero = u == 0UL;
|
|
bool vIsZero = v == 0UL;
|
|
bool eitherZero = uIsZero | vIsZero;
|
|
ulong zeroResult = uIsZero ? v : u;
|
|
|
|
ulong combined = u | v | 1UL;
|
|
int shift = XMath.TrailingZeroCount(combined);
|
|
|
|
ulong normalizedU = u >> XMath.TrailingZeroCount(u | 1UL);
|
|
ulong normalizedV = v >> XMath.TrailingZeroCount(v | 1UL);
|
|
|
|
ulong currentU = normalizedU;
|
|
ulong currentV = normalizedV;
|
|
bool loopCondition = currentV != 0UL;
|
|
|
|
while (loopCondition)
|
|
{
|
|
currentV >>= XMath.TrailingZeroCount(currentV);
|
|
ulong minValue = XMath.Min(currentU, currentV);
|
|
ulong maxValue = XMath.Max(currentU, currentV);
|
|
currentU = minValue;
|
|
currentV = maxValue - minValue;
|
|
loopCondition = currentV != 0UL;
|
|
}
|
|
|
|
ulong gcd = currentU << shift;
|
|
return eitherZero ? zeroResult : gcd;
|
|
}
|
|
}
|