Merge pull request #17 from mlemanczyk/codex/analyze-evenperfectbitscanner-for-optimizations
Optimize GPU residue scanning and divisor path
This commit is contained in:
7 files changed
+149
-46
No files matched your search
@@ -73,14 +73,14 @@ public class ProgramTests
|
|||||||
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var divisorField = typeof(Program).GetField("_divisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var divisorField = typeof(Program).GetField("_divisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var candidatesField = typeof(Program).GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var candidatesField = typeof(MersenneNumberDivisorGpuTester).GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var residueField = typeof(Program).GetField("PResidue", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var residueField = typeof(Program).GetField("PResidue", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||||
|
|
||||||
useDivisorField.SetValue(null, true);
|
useDivisorField.SetValue(null, true);
|
||||||
divisorField.SetValue(null, 0UL);
|
divisorField.SetValue(null, 0UL);
|
||||||
testerField.SetValue(null, null);
|
testerField.SetValue(null, new MersenneNumberDivisorGpuTester());
|
||||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||||
residueField.SetValue(null, new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
residueField.SetValue(null, new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||||
candidatesField.SetValue(null, new (ulong, uint)[] { (7UL, 3U), (23UL, 11U) });
|
candidatesField.SetValue(null, new (ulong, uint)[] { (7UL, 3U), (23UL, 11U) });
|
||||||
@@ -90,6 +90,7 @@ public class ProgramTests
|
|||||||
{
|
{
|
||||||
Program.IsEvenPerfectCandidate(11UL, 32).Should().BeFalse();
|
Program.IsEvenPerfectCandidate(11UL, 32).Should().BeFalse();
|
||||||
Program.IsEvenPerfectCandidate(5UL, 32).Should().BeTrue();
|
Program.IsEvenPerfectCandidate(5UL, 32).Should().BeTrue();
|
||||||
|
Program.IsEvenPerfectCandidate(127UL, 32).Should().BeTrue();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ public class PrimeTesterIsPrimeTests
|
|||||||
[InlineData(2UL, true)]
|
[InlineData(2UL, true)]
|
||||||
[InlineData(3UL, true)]
|
[InlineData(3UL, true)]
|
||||||
[InlineData(4UL, false)]
|
[InlineData(4UL, false)]
|
||||||
|
[InlineData(5UL, true)]
|
||||||
[InlineData(7UL, true)]
|
[InlineData(7UL, true)]
|
||||||
[InlineData(97UL, true)]
|
[InlineData(97UL, true)]
|
||||||
[InlineData(121UL, false)]
|
[InlineData(121UL, false)]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
using static PerfectNumbers.Core.Gpu.GpuContextPool;
|
using static PerfectNumbers.Core.Gpu.GpuContextPool;
|
||||||
@@ -29,7 +30,7 @@ public sealed class KernelContainer
|
|||||||
public Action<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
public Action<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
||||||
ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>>? Incremental;
|
ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>>? Incremental;
|
||||||
public Action<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
public Action<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
||||||
ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>>? Pow2Mod;
|
ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2Mod;
|
||||||
public Action<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
public Action<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
||||||
ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<uint, Stride1D.Dense>>? IncrementalOrder;
|
ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<uint, Stride1D.Dense>>? IncrementalOrder;
|
||||||
public Action<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
public Action<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
||||||
@@ -37,6 +38,8 @@ public sealed class KernelContainer
|
|||||||
|
|
||||||
// Optional device buffer with small divisor cycles (<= 4M). Index = divisor, value = cycle length.
|
// Optional device buffer with small divisor cycles (<= 4M). Index = divisor, value = cycle length.
|
||||||
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallCycles;
|
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallCycles;
|
||||||
|
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimes;
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2;
|
||||||
|
|
||||||
public static T InitOnce<T>(ref T? slot, Func<T> factory) where T : class
|
public static T InitOnce<T>(ref T? slot, Func<T> factory) where T : class
|
||||||
{
|
{
|
||||||
@@ -77,14 +80,14 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly Action<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>> Pow2ModKernel
|
public readonly Action<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> Pow2ModKernel
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var accel = Accelerator;
|
var accel = Accelerator;
|
||||||
lock (_gpu!.Value.KernelInitLock)
|
lock (_gpu!.Value.KernelInitLock)
|
||||||
{
|
{
|
||||||
return KernelContainer.InitOnce(ref _kernels.Pow2Mod, () => accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>>(Pow2ModKernelScan));
|
return KernelContainer.InitOnce(ref _kernels.Pow2Mod, () => accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernelScan));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,33 +277,30 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void Pow2ModKernelScan(Index1D index, ulong exponent, GpuUInt128 twoP, GpuUInt128 kStart, byte lastIsSeven, ulong _,
|
private static void Pow2ModKernelScan(Index1D index, ulong exponent, GpuUInt128 twoP, GpuUInt128 kStart, byte lastIsSeven, ulong _,
|
||||||
ulong q0m10, ulong q0m8, ulong q0m3, ulong q0m5, ArrayView<ulong> orders,
|
ResidueAutomatonArgs ra, ArrayView<ulong> orders,
|
||||||
ArrayView1D<uint, Stride1D.Dense> smallCycles)
|
ArrayView1D<uint, Stride1D.Dense> smallCycles, ArrayView1D<uint, Stride1D.Dense> smallPrimes,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> smallPrimesPow2)
|
||||||
{
|
{
|
||||||
ulong idx = (ulong)index.X;
|
ulong idx = (ulong)index.X;
|
||||||
ulong step10 = ((exponent % 10UL) << 1) % 10UL;
|
ulong r10 = ra.Q0M10 + (ra.Step10 * idx) % 10UL; r10 -= (r10 >= 10UL) ? 10UL : 0UL;
|
||||||
ulong r10 = q0m10 + (step10 * idx) % 10UL; r10 -= (r10 >= 10UL) ? 10UL : 0UL;
|
|
||||||
bool shouldCheck = r10 != 5UL;
|
bool shouldCheck = r10 != 5UL;
|
||||||
if (shouldCheck)
|
if (shouldCheck)
|
||||||
{
|
{
|
||||||
ulong step8 = ((exponent & 7UL) << 1) & 7UL;
|
ulong r8 = (ra.Q0M8 + ((ra.Step8 * idx) & 7UL)) & 7UL;
|
||||||
ulong r8 = (q0m8 + ((step8 * idx) & 7UL)) & 7UL;
|
|
||||||
if (r8 != 1UL && r8 != 7UL)
|
if (r8 != 1UL && r8 != 7UL)
|
||||||
{
|
{
|
||||||
shouldCheck = false;
|
shouldCheck = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ulong step3 = ((exponent % 3UL) << 1) % 3UL;
|
ulong r3 = ra.Q0M3 + (ra.Step3 * (idx % 3UL)); r3 -= (r3 >= 3UL) ? 3UL : 0UL;
|
||||||
ulong r3 = q0m3 + (step3 * (idx % 3UL)); r3 -= (r3 >= 3UL) ? 3UL : 0UL;
|
|
||||||
if (r3 == 0UL)
|
if (r3 == 0UL)
|
||||||
{
|
{
|
||||||
shouldCheck = false;
|
shouldCheck = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ulong step5Loc = ((exponent % 5UL) << 1) % 5UL;
|
ulong r5 = ra.Q0M5 + (ra.Step5 * (idx % 5UL)); if (r5 >= 5UL) r5 -= 5UL;
|
||||||
ulong r5 = q0m5 + (step5Loc * (idx % 5UL)); if (r5 >= 5UL) r5 -= 5UL;
|
|
||||||
if (r5 == 0UL)
|
if (r5 == 0UL)
|
||||||
{
|
{
|
||||||
shouldCheck = false;
|
shouldCheck = false;
|
||||||
@@ -316,7 +316,6 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
|||||||
|
|
||||||
GpuUInt128 k = kStart + (GpuUInt128)idx;
|
GpuUInt128 k = kStart + (GpuUInt128)idx;
|
||||||
GpuUInt128 q = twoP.Mul64(k) + GpuUInt128.One;
|
GpuUInt128 q = twoP.Mul64(k) + GpuUInt128.One;
|
||||||
// Small-cycles in-kernel early rejection from device table
|
|
||||||
if (q.High == 0UL && q.Low <= (ulong)(smallCycles.Length - 1))
|
if (q.High == 0UL && q.Low <= (ulong)(smallCycles.Length - 1))
|
||||||
{
|
{
|
||||||
uint cyc = smallCycles[(int)q.Low];
|
uint cyc = smallCycles[(int)q.Low];
|
||||||
@@ -332,9 +331,54 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int primesLen = (int)smallPrimes.Length;
|
||||||
|
for (int i = 0; i < primesLen; i++)
|
||||||
|
{
|
||||||
|
ulong square = smallPrimesPow2[i];
|
||||||
|
if (new GpuUInt128(0UL, square) > q)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ulong prime = smallPrimes[i];
|
||||||
|
if (Mod128By64(q, prime) == 0UL)
|
||||||
|
{
|
||||||
|
orders[index] = 0UL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
orders[index] = exponent;
|
orders[index] = exponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static ulong Mod128By64(GpuUInt128 value, ulong modulus)
|
||||||
|
{
|
||||||
|
ulong rem = 0UL;
|
||||||
|
ulong part = value.High;
|
||||||
|
for (int i = 0; i < 64; i++)
|
||||||
|
{
|
||||||
|
rem = (rem << 1) | (part >> 63);
|
||||||
|
if (rem >= modulus)
|
||||||
|
{
|
||||||
|
rem -= modulus;
|
||||||
|
}
|
||||||
|
part <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
part = value.Low;
|
||||||
|
for (int i = 0; i < 64; i++)
|
||||||
|
{
|
||||||
|
rem = (rem << 1) | (part >> 63);
|
||||||
|
if (rem >= modulus)
|
||||||
|
{
|
||||||
|
rem -= modulus;
|
||||||
|
}
|
||||||
|
part <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rem;
|
||||||
|
}
|
||||||
|
|
||||||
// Device-friendly small cycle length for 2 mod divisor
|
// Device-friendly small cycle length for 2 mod divisor
|
||||||
private static ulong CalculateCycleLengthSmall(ulong divisor)
|
private static ulong CalculateCycleLengthSmall(ulong divisor)
|
||||||
{
|
{
|
||||||
@@ -527,6 +571,33 @@ public class GpuKernelPool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static (ArrayView1D<uint, Stride1D.Dense> primes, ArrayView1D<ulong, Stride1D.Dense> primesPow2) EnsureSmallPrimesOnDevice(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
var kernels = GetKernels(accelerator);
|
||||||
|
if (kernels.SmallPrimes is { } p && kernels.SmallPrimesPow2 is { } p2)
|
||||||
|
{
|
||||||
|
return (p.View, p2.View);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (kernels)
|
||||||
|
{
|
||||||
|
if (kernels.SmallPrimes is { } existing && kernels.SmallPrimesPow2 is { } existingPow2)
|
||||||
|
{
|
||||||
|
return (existing.View, existingPow2.View);
|
||||||
|
}
|
||||||
|
|
||||||
|
var hostPrimes = PrimesGenerator.SmallPrimes;
|
||||||
|
var hostPow2 = PrimesGenerator.SmallPrimesPow2;
|
||||||
|
var devicePrimes = accelerator.Allocate1D<uint>(hostPrimes.Length);
|
||||||
|
devicePrimes.View.CopyFromCPU(hostPrimes);
|
||||||
|
var devicePow2 = accelerator.Allocate1D<ulong>(hostPow2.Length);
|
||||||
|
devicePow2.View.CopyFromCPU(hostPow2);
|
||||||
|
kernels.SmallPrimes = devicePrimes;
|
||||||
|
kernels.SmallPrimesPow2 = devicePow2;
|
||||||
|
return (devicePrimes.View, devicePow2.View);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static GpuKernelLease GetKernel(bool useGpuOrder)
|
public static GpuKernelLease GetKernel(bool useGpuOrder)
|
||||||
{
|
{
|
||||||
var limiter = GpuPrimeWorkLimiter.Acquire();
|
var limiter = GpuPrimeWorkLimiter.Acquire();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace PerfectNumbers.Core.Gpu;
|
|||||||
public sealed class MersenneNumberDivisorGpuTester
|
public sealed class MersenneNumberDivisorGpuTester
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ulong, ArrayView<byte>>> _kernelCache = new();
|
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ulong, ArrayView<byte>>> _kernelCache = new();
|
||||||
|
private readonly ConcurrentDictionary<Accelerator, MemoryBuffer1D<byte, Stride1D.Dense>> _resultBuffers = new();
|
||||||
|
|
||||||
private Action<Index1D, ulong, ulong, ArrayView<byte>> GetKernel(Accelerator accelerator) =>
|
private Action<Index1D, ulong, ulong, ArrayView<byte>> GetKernel(Accelerator accelerator) =>
|
||||||
_kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, ulong, ArrayView<byte>>(Kernel));
|
_kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, ulong, ArrayView<byte>>(Kernel));
|
||||||
@@ -37,11 +38,10 @@ public sealed class MersenneNumberDivisorGpuTester
|
|||||||
var gpu = GpuContextPool.RentPreferred(preferCpu: false);
|
var gpu = GpuContextPool.RentPreferred(preferCpu: false);
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = gpu.Accelerator;
|
||||||
var kernel = GetKernel(accelerator);
|
var kernel = GetKernel(accelerator);
|
||||||
var resultBuffer = accelerator.Allocate1D<byte>(1);
|
var resultBuffer = _resultBuffers.GetOrAdd(accelerator, acc => acc.Allocate1D<byte>(1));
|
||||||
kernel(1, exponent, divisor, resultBuffer.View);
|
kernel(1, exponent, divisor, resultBuffer.View);
|
||||||
accelerator.Synchronize();
|
accelerator.Synchronize();
|
||||||
bool divisible = resultBuffer.GetAsArray1D()[0] != 0;
|
bool divisible = resultBuffer.GetAsArray1D()[0] != 0;
|
||||||
resultBuffer.Dispose();
|
|
||||||
gpu.Dispose();
|
gpu.Dispose();
|
||||||
return divisible;
|
return divisible;
|
||||||
}
|
}
|
||||||
@@ -93,6 +93,10 @@ public sealed class MersenneNumberDivisorGpuTester
|
|||||||
}
|
}
|
||||||
|
|
||||||
d = kMul2 * p + 1UL;
|
d = kMul2 * p + 1UL;
|
||||||
|
if (p < 64UL && d == ((1UL << (int)p) - 1UL))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// kkMul2 now becomes cycle. We're reusing existing variable for the best performance
|
// kkMul2 now becomes cycle. We're reusing existing variable for the best performance
|
||||||
kMul2 = divisorCycles.GetCycle(d);
|
kMul2 = divisorCycles.GetCycle(d);
|
||||||
if (p % kMul2 != 0UL)
|
if (p % kMul2 != 0UL)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
@@ -18,9 +19,20 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
|
|||||||
ulong divMul = (ulong)((((UInt128)1 << 64) - UInt128.One) / exponent) + 1UL;
|
ulong divMul = (ulong)((((UInt128)1 << 64) - UInt128.One) / exponent) + 1UL;
|
||||||
byte last = lastIsSeven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
|
byte last = lastIsSeven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
|
||||||
|
|
||||||
var kernel = (_kernelType == GpuKernelType.Pow2Mod)
|
var pow2Kernel = gpuLease.Pow2ModKernel;
|
||||||
? gpuLease.Pow2ModKernel
|
var incKernel = gpuLease.IncrementalKernel;
|
||||||
: gpuLease.IncrementalKernel;
|
ulong step10 = ((exponent % 10UL) << 1) % 10UL;
|
||||||
|
ulong step8 = ((exponent & 7UL) << 1) & 7UL;
|
||||||
|
ulong step3 = ((exponent % 3UL) << 1) % 3UL;
|
||||||
|
ulong step5 = ((exponent % 5UL) << 1) % 5UL;
|
||||||
|
GpuUInt128 twoPGpu = (GpuUInt128)twoP;
|
||||||
|
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
|
||||||
|
ArrayView1D<uint, Stride1D.Dense> smallPrimesView = default;
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> smallPrimesPow2View = default;
|
||||||
|
if (_kernelType == GpuKernelType.Pow2Mod)
|
||||||
|
{
|
||||||
|
(smallPrimesView, smallPrimesPow2View) = GpuKernelPool.EnsureSmallPrimesOnDevice(accelerator);
|
||||||
|
}
|
||||||
|
|
||||||
var orderBuffer = accelerator.Allocate1D<ulong>(batchSize);
|
var orderBuffer = accelerator.Allocate1D<ulong>(batchSize);
|
||||||
// Avoid giant stack allocations that can trigger StackOverflow when batchSize is large.
|
// Avoid giant stack allocations that can trigger StackOverflow when batchSize is large.
|
||||||
@@ -39,27 +51,39 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
|
|||||||
Span<ulong> orders = orderArray.AsSpan(0, currentSize);
|
Span<ulong> orders = orderArray.AsSpan(0, currentSize);
|
||||||
// Precompute residue automaton bases for this batch
|
// Precompute residue automaton bases for this batch
|
||||||
UInt128 q0 = twoP * kStart + UInt128.One;
|
UInt128 q0 = twoP * kStart + UInt128.One;
|
||||||
ulong q0m10 = q0.Mod10();
|
if (_kernelType == GpuKernelType.Pow2Mod)
|
||||||
ulong q0m8 = q0.Mod8();
|
{
|
||||||
ulong q0m3 = q0.Mod3();
|
q0.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
|
||||||
ulong q0m5 = q0.Mod5();
|
var ra = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
|
||||||
ulong step10 = ((exponent % 10UL) << 1) % 10UL;
|
pow2Kernel(currentSize, exponent, twoPGpu, (GpuUInt128)kStart, last, divMul,
|
||||||
ulong step8 = ((exponent & 7UL) << 1) & 7UL;
|
ra, orderBuffer.View, smallCyclesView, smallPrimesView, smallPrimesPow2View);
|
||||||
ulong step3 = ((exponent % 3UL) << 1) % 3UL;
|
}
|
||||||
ulong step5 = ((exponent % 5UL) << 1) % 5UL;
|
else
|
||||||
|
{
|
||||||
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
|
q0.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
|
||||||
kernel(currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, divMul,
|
incKernel(currentSize, exponent, twoPGpu, (GpuUInt128)kStart, last, divMul,
|
||||||
q0m10, q0m8, q0m3, q0m5, orderBuffer.View, smallCyclesView);
|
q0m10, q0m8, q0m3, q0m5, orderBuffer.View, smallCyclesView);
|
||||||
|
}
|
||||||
|
|
||||||
accelerator.Synchronize();
|
accelerator.Synchronize();
|
||||||
orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize);
|
orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize);
|
||||||
q = twoP * kStart + UInt128.One;
|
if (_kernelType == GpuKernelType.Pow2Mod)
|
||||||
for (i = 0; i < currentSize && Volatile.Read(ref isPrime); i++, q += twoP)
|
{
|
||||||
|
for (i = 0; i < currentSize; i++)
|
||||||
{
|
{
|
||||||
if (orders[i] != 0UL)
|
if (orders[i] != 0UL)
|
||||||
{
|
{
|
||||||
if (q.IsPrimeCandidate())
|
Volatile.Write(ref isPrime, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
q = twoP * kStart + UInt128.One;
|
||||||
|
for (i = 0; i < currentSize && Volatile.Read(ref isPrime); i++, q += twoP)
|
||||||
|
{
|
||||||
|
if (orders[i] != 0UL && q.IsPrimeCandidate())
|
||||||
{
|
{
|
||||||
Volatile.Write(ref isPrime, false);
|
Volatile.Write(ref isPrime, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,18 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
|||||||
{
|
{
|
||||||
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
|
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
|
||||||
var accelerator = gpuLease.Accelerator;
|
var accelerator = gpuLease.Accelerator;
|
||||||
// Ensure device has small cycles table for in-kernel lookup
|
// Ensure device has small cycles and primes tables for in-kernel lookup
|
||||||
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
|
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
|
||||||
|
var (smallPrimesView, smallPrimesPow2View) = GpuKernelPool.EnsureSmallPrimesOnDevice(accelerator);
|
||||||
int batchSize = GpuConstants.ScanBatchSize;
|
int batchSize = GpuConstants.ScanBatchSize;
|
||||||
UInt128 kStart = 1UL;
|
UInt128 kStart = 1UL;
|
||||||
byte last = lastIsSeven ? (byte)1 : (byte)0;
|
byte last = lastIsSeven ? (byte)1 : (byte)0;
|
||||||
var kernel = gpuLease.Pow2ModKernel;
|
var kernel = gpuLease.Pow2ModKernel;
|
||||||
|
ulong step10 = ((exponent % 10UL) << 1) % 10UL;
|
||||||
|
ulong step8 = ((exponent & 7UL) << 1) & 7UL;
|
||||||
|
ulong step3 = ((exponent % 3UL) << 1) % 3UL;
|
||||||
|
ulong step5 = ((exponent % 5UL) << 1) % 5UL;
|
||||||
|
GpuUInt128 twoPGpu = (GpuUInt128)twoP;
|
||||||
|
|
||||||
var orderBuffer = accelerator.Allocate1D<ulong>(batchSize);
|
var orderBuffer = accelerator.Allocate1D<ulong>(batchSize);
|
||||||
ulong[] orderArray = ArrayPool<ulong>.Shared.Rent(batchSize);
|
ulong[] orderArray = ArrayPool<ulong>.Shared.Rent(batchSize);
|
||||||
@@ -47,25 +53,21 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
|||||||
|
|
||||||
q = twoP * kStart + UInt128.One;
|
q = twoP * kStart + UInt128.One;
|
||||||
q.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
|
q.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
|
||||||
ulong step10 = ((exponent % 10UL) << 1) % 10UL;
|
var ra = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
|
||||||
ulong step8 = ((exponent & 7UL) << 1) & 7UL;
|
|
||||||
ulong step3 = ((exponent % 3UL) << 1) % 3UL;
|
|
||||||
ulong step5 = ((exponent % 5UL) << 1) % 5UL;
|
|
||||||
|
|
||||||
kernel(currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, 0UL,
|
kernel(currentSize, exponent, twoPGpu, (GpuUInt128)kStart, last, 0UL,
|
||||||
q0m10, q0m8, q0m3, q0m5, orderBuffer.View, smallCyclesView);
|
ra, orderBuffer.View, smallCyclesView, smallPrimesView, smallPrimesPow2View);
|
||||||
|
|
||||||
accelerator.Synchronize();
|
accelerator.Synchronize();
|
||||||
orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize);
|
orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize);
|
||||||
accelerator.Synchronize();
|
|
||||||
if (!Volatile.Read(ref isPrime))
|
if (!Volatile.Read(ref isPrime))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < currentSize; i++, q += twoP)
|
for (i = 0; i < currentSize; i++)
|
||||||
{
|
{
|
||||||
if (orders[i] != 0UL && q.IsPrimeCandidate())
|
if (orders[i] != 0UL)
|
||||||
{
|
{
|
||||||
Volatile.Write(ref isPrime, false);
|
Volatile.Write(ref isPrime, false);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public sealed class PrimeTester(bool useInternal = false)
|
|||||||
{
|
{
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
else if (n % 5 == 0)
|
else if (n > 5UL && (n % 5UL) == 0UL)
|
||||||
{
|
{
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user