Revert "Merge pull request #251 from mlemanczyk/codex/refactor-prime-checking-on-gpu-n5t2pm"

This reverts commit 936ce239db, reversing
changes made to e14b77e504.
This commit is contained in:
mlemanczyk committed 2025-11-02 11:22:14 +01:00
1 parent 936ce239db
commit 2281d9535e
23 files changed
+880 -1187

No files matched your search

@@ -1,142 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using ILGPU;
using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu;
internal static class DivisorGeneratorGpuCache
{
private sealed class AcceleratorReferenceComparer : IEqualityComparer<Accelerator>
{
public static AcceleratorReferenceComparer Instance { get; } = new();
public bool Equals(Accelerator? x, Accelerator? y)
{
return ReferenceEquals(x, y);
}
public int GetHashCode(Accelerator obj)
{
return RuntimeHelpers.GetHashCode(obj);
}
}
private sealed class DivisorGeneratorGpuTables : IDisposable
{
internal DivisorGeneratorGpuTables(Accelerator accelerator)
{
SmallPrimes = AllocateAndCopyUInt(accelerator, DivisorGenerator.SmallPrimes);
SmallPrimesPow2 = AllocateAndCopyULong(accelerator, DivisorGenerator.SmallPrimesPow2);
SmallPrimesLastOne = AllocateAndCopyUInt(accelerator, DivisorGenerator.SmallPrimesLastOne);
SmallPrimesPow2LastOne = AllocateAndCopyULong(accelerator, DivisorGenerator.SmallPrimesPow2LastOne);
SmallPrimesLastSeven = AllocateAndCopyUInt(accelerator, DivisorGenerator.SmallPrimesLastSeven);
SmallPrimesPow2LastSeven = AllocateAndCopyULong(accelerator, DivisorGenerator.SmallPrimesPow2LastSeven);
SmallPrimesLastThree = AllocateAndCopyUInt(accelerator, DivisorGenerator.SmallPrimesLastThree);
SmallPrimesPow2LastThree = AllocateAndCopyULong(accelerator, DivisorGenerator.SmallPrimesPow2LastThree);
SmallPrimesLastNine = AllocateAndCopyUInt(accelerator, DivisorGenerator.SmallPrimesLastNine);
SmallPrimesPow2LastNine = AllocateAndCopyULong(accelerator, DivisorGenerator.SmallPrimesPow2LastNine);
SmallPrimesLastOneWithoutLastThree = AllocateAndCopyUInt(accelerator, DivisorGenerator.SmallPrimesLastOneWithoutLastThree);
SmallPrimesPow2LastOneWithoutLastThree = AllocateAndCopyULong(accelerator, DivisorGenerator.SmallPrimesPow2LastOneWithoutLastThree);
SmallPrimesLastSevenWithoutLastThree = AllocateAndCopyUInt(accelerator, DivisorGenerator.SmallPrimesLastSevenWithoutLastThree);
SmallPrimesPow2LastSevenWithoutLastThree = AllocateAndCopyULong(accelerator, DivisorGenerator.SmallPrimesPow2LastSevenWithoutLastThree);
SmallPrimesLastNineWithoutLastThree = AllocateAndCopyUInt(accelerator, DivisorGenerator.SmallPrimesLastNineWithoutLastThree);
SmallPrimesPow2LastNineWithoutLastThree = AllocateAndCopyULong(accelerator, DivisorGenerator.SmallPrimesPow2LastNineWithoutLastThree);
}
internal MemoryBuffer1D<uint, Stride1D.Dense> SmallPrimes { get; }
internal MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimesPow2 { get; }
internal MemoryBuffer1D<uint, Stride1D.Dense> SmallPrimesLastOne { get; }
internal MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimesPow2LastOne { get; }
internal MemoryBuffer1D<uint, Stride1D.Dense> SmallPrimesLastSeven { get; }
internal MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimesPow2LastSeven { get; }
internal MemoryBuffer1D<uint, Stride1D.Dense> SmallPrimesLastThree { get; }
internal MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimesPow2LastThree { get; }
internal MemoryBuffer1D<uint, Stride1D.Dense> SmallPrimesLastNine { get; }
internal MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimesPow2LastNine { get; }
internal MemoryBuffer1D<uint, Stride1D.Dense> SmallPrimesLastOneWithoutLastThree { get; }
internal MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimesPow2LastOneWithoutLastThree { get; }
internal MemoryBuffer1D<uint, Stride1D.Dense> SmallPrimesLastSevenWithoutLastThree { get; }
internal MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimesPow2LastSevenWithoutLastThree { get; }
internal MemoryBuffer1D<uint, Stride1D.Dense> SmallPrimesLastNineWithoutLastThree { get; }
internal MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimesPow2LastNineWithoutLastThree { get; }
public void Dispose()
{
SmallPrimes.Dispose();
SmallPrimesPow2.Dispose();
SmallPrimesLastOne.Dispose();
SmallPrimesPow2LastOne.Dispose();
SmallPrimesLastSeven.Dispose();
SmallPrimesPow2LastSeven.Dispose();
SmallPrimesLastThree.Dispose();
SmallPrimesPow2LastThree.Dispose();
SmallPrimesLastNine.Dispose();
SmallPrimesPow2LastNine.Dispose();
SmallPrimesLastOneWithoutLastThree.Dispose();
SmallPrimesPow2LastOneWithoutLastThree.Dispose();
SmallPrimesLastSevenWithoutLastThree.Dispose();
SmallPrimesPow2LastSevenWithoutLastThree.Dispose();
SmallPrimesLastNineWithoutLastThree.Dispose();
SmallPrimesPow2LastNineWithoutLastThree.Dispose();
}
private static MemoryBuffer1D<uint, Stride1D.Dense> AllocateAndCopyUInt(Accelerator accelerator, uint[] source)
{
var buffer = accelerator.Allocate1D<uint>(source.Length);
if (source.Length > 0)
{
buffer.View.CopyFromCPU(source);
}
return buffer;
}
private static MemoryBuffer1D<ulong, Stride1D.Dense> AllocateAndCopyULong(Accelerator accelerator, ulong[] source)
{
var buffer = accelerator.Allocate1D<ulong>(source.Length);
if (source.Length > 0)
{
buffer.View.CopyFromCPU(source);
}
return buffer;
}
}
private static readonly ConcurrentDictionary<Accelerator, DivisorGeneratorGpuTables> Cache = new(AcceleratorReferenceComparer.Instance);
public static void WarmUpByDivisorTables(Accelerator accelerator)
{
if (accelerator is null || accelerator.AcceleratorType == AcceleratorType.CPU)
{
return;
}
Cache.GetOrAdd(accelerator, acc => new DivisorGeneratorGpuTables(acc));
}
public static void Clear(Accelerator accelerator)
{
if (accelerator is null)
{
return;
}
if (Cache.TryRemove(accelerator, out var tables))
{
tables.Dispose();
}
}
public static void DisposeAll()
{
foreach (var entry in Cache)
{
entry.Value.Dispose();
}
Cache.Clear();
}
}
+80 -57
View File
@@ -1,4 +1,3 @@
using System;
using System.Collections.Concurrent;
using ILGPU;
using ILGPU.Runtime;
@@ -7,19 +6,24 @@ namespace PerfectNumbers.Core.Gpu;
public static class GpuContextPool
{
// The pool intentionally skips pre-loading ProcessEightBitWindows kernels so accelerator initialization
// stays stable; GpuKernelPool JIT-compiles them on first use alongside the small-cycle uploads.
// Default device preference for generic GPU kernels (prime scans, NTT, etc.)
internal sealed class PooledContext
{
public Context Context { get; }
public Accelerator Accelerator { get; }
private static readonly bool PoolingEnabled = true;
// The pool intentionally skips pre-loading ProcessEightBitWindows kernels so accelerator initialization
// stays stable; GpuKernelPool JIT-compiles them on first use alongside the small-cycle uploads.
// Default device preference for generic GPU kernels (prime scans, NTT, etc.)
public static bool ForceCpu { get; set; } = false;
public PooledContext()
internal sealed class PooledContext
{
public Context Context { get; }
public Accelerator Accelerator { get; }
public bool IsCpu { get; }
public object ExecutionLock { get; } = new();
public PooledContext(bool preferCpu)
{
Context = Context.CreateDefault();
Accelerator = Context.GetPreferredDevice(false).CreateAccelerator(Context);
DivisorGeneratorGpuCache.WarmUpByDivisorTables(Accelerator);
Accelerator = Context.GetPreferredDevice(preferCpu).CreateAccelerator(Context);
IsCpu = Accelerator.AcceleratorType == AcceleratorType.CPU;
// NOTE: Avoid loading/compiling any kernel here to prevent implicit
// CL stream/queue creation during accelerator construction.
// Some OpenCL drivers are fragile when a queue is created immediately
@@ -28,12 +32,12 @@ public static class GpuContextPool
public void Dispose()
{
// Ensure all cached GPU buffers for this accelerator are released.
NttGpuMath.ClearCaches(Accelerator);
// Release PrimeTester GPU state for this accelerator (kernel/device primes)
// Clear any per-accelerator cached resources to avoid releasing
// after the accelerator is destroyed.
DivisorGeneratorGpuCache.Clear(Accelerator);
PrimeTester.ClearGpuCaches(Accelerator);
Accelerator.Dispose();
@@ -41,77 +45,96 @@ public static class GpuContextPool
}
}
private static readonly ConcurrentQueue<PooledContext> CpuPool = new();
private static readonly ConcurrentQueue<PooledContext> GpuPool = new();
private static int WarmedGpuContextCount;
// Allows callers to choose CPU/GPU per use-case, decoupled from ForceCpu.
public static GpuContextLease Rent()
{
if (GpuPool.TryDequeue(out var gpu))
return RentPreferred(ForceCpu);
}
// Allows callers to choose CPU/GPU per use-case, decoupled from ForceCpu.
public static GpuContextLease RentPreferred(bool preferCpu)
{
if (PoolingEnabled)
{
return new GpuContextLease(gpu);
if (preferCpu && CpuPool.TryDequeue(out var cpu))
{
return new GpuContextLease(cpu);
}
if (!preferCpu && GpuPool.TryDequeue(out var gpu))
{
return new GpuContextLease(gpu);
}
}
// Create a new accelerator when the pool does not have one available.
return new GpuContextLease(new PooledContext());
}
public static void WarmUpPool(int threadCount)
{
if (threadCount <= WarmedGpuContextCount)
{
return;
}
int toCreate = threadCount - WarmedGpuContextCount;
var contexts = new PooledContext[toCreate];
for (int i = 0; i < toCreate; i++)
{
contexts[i] = new PooledContext();
}
for (int i = 0; i < toCreate; i++)
{
GpuPool.Enqueue(contexts[i]);
}
WarmedGpuContextCount = threadCount;
return new GpuContextLease(new PooledContext(preferCpu));
}
public static void DisposeAll()
{
if (!PoolingEnabled)
{
return;
}
while (CpuPool.TryDequeue(out var cpu))
{
cpu.Dispose();
}
while (GpuPool.TryDequeue(out var gpu))
{
gpu.Dispose();
}
DivisorGeneratorGpuCache.DisposeAll();
PrimeTester.DisposeGpuContexts();
WarmedGpuContextCount = 0;
}
private static void Return(PooledContext ctx)
{
ctx.Accelerator.Synchronize();
GpuPool.Enqueue(ctx);
}
public readonly struct GpuContextLease
{
private readonly PooledContext _ctx;
internal GpuContextLease(PooledContext ctx)
if (PoolingEnabled)
{
_ctx = ctx;
ctx.Accelerator.Synchronize();
if (ctx.IsCpu)
{
CpuPool.Enqueue(ctx);
}
else
{
GpuPool.Enqueue(ctx);
}
}
public readonly Context Context => _ctx.Context;
public readonly Accelerator Accelerator => _ctx.Accelerator;
else
{
ctx.Dispose();
}
}
public struct GpuContextLease
{
private readonly PooledContext _ctx;
internal GpuContextLease(PooledContext ctx)
{
_ctx = ctx;
}
public Context Context => _ctx.Context;
public Accelerator Accelerator => _ctx.Accelerator;
public object ExecutionLock => _ctx.ExecutionLock;
public void Dispose()
{
public readonly void Dispose()
{
Return(_ctx);
}
}
}
}
}
+1 -1
View File
@@ -280,7 +280,7 @@ public class GpuKernelPool
public static GpuKernelLease GetKernel(bool useGpuOrder)
{
var limiter = GpuPrimeWorkLimiter.Acquire();
var gpu = Rent();
var gpu = RentPreferred(preferCpu: !useGpuOrder);
var accelerator = gpu.Accelerator;
var kernels = GetKernels(accelerator);
return GpuKernelLease.Rent(limiter, gpu, kernels);
@@ -1,146 +0,0 @@
using System.Runtime.CompilerServices;
using ILGPU;
using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu;
internal readonly struct HeuristicGpuDivisorTables
{
public static readonly HeuristicGpuDivisorTables Shared = default;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding1;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding3;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding7;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding9;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding1;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding3;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding7;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding9;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupADivisors;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupADivisorSquares;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding1;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding1;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding7;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding7;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding9;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding9;
public HeuristicGpuDivisorTables(
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding1,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding3,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding7,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding9,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding1,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding3,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding7,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding9,
ArrayView1D<ulong, Stride1D.Dense> groupADivisors,
ArrayView1D<ulong, Stride1D.Dense> groupADivisorSquares,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding1,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding1,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding7,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding7,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding9,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding9)
{
CombinedDivisorsEnding1 = combinedDivisorsEnding1;
CombinedDivisorsEnding3 = combinedDivisorsEnding3;
CombinedDivisorsEnding7 = combinedDivisorsEnding7;
CombinedDivisorsEnding9 = combinedDivisorsEnding9;
CombinedDivisorSquaresEnding1 = combinedDivisorSquaresEnding1;
CombinedDivisorSquaresEnding3 = combinedDivisorSquaresEnding3;
CombinedDivisorSquaresEnding7 = combinedDivisorSquaresEnding7;
CombinedDivisorSquaresEnding9 = combinedDivisorSquaresEnding9;
GroupADivisors = groupADivisors;
GroupADivisorSquares = groupADivisorSquares;
GroupBDivisorsEnding1 = groupBDivisorsEnding1;
GroupBDivisorSquaresEnding1 = groupBDivisorSquaresEnding1;
GroupBDivisorsEnding7 = groupBDivisorsEnding7;
GroupBDivisorSquaresEnding7 = groupBDivisorSquaresEnding7;
GroupBDivisorsEnding9 = groupBDivisorsEnding9;
GroupBDivisorSquaresEnding9 = groupBDivisorSquaresEnding9;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectCombined(byte ending)
{
return ending switch
{
1 => CombinedDivisorsEnding1,
3 => CombinedDivisorsEnding3,
7 => CombinedDivisorsEnding7,
9 => CombinedDivisorsEnding9,
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectCombinedSquares(byte ending)
{
return ending switch
{
1 => CombinedDivisorSquaresEnding1,
3 => CombinedDivisorSquaresEnding3,
7 => CombinedDivisorSquaresEnding7,
9 => CombinedDivisorSquaresEnding9,
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectGroupB(byte ending)
{
return ending switch
{
1 => GroupBDivisorsEnding1,
7 => GroupBDivisorsEnding7,
9 => GroupBDivisorsEnding9,
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectGroupBSquares(byte ending)
{
return ending switch
{
1 => GroupBDivisorSquaresEnding1,
7 => GroupBDivisorSquaresEnding7,
9 => GroupBDivisorSquaresEnding9,
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectDivisors(HeuristicGpuDivisorTableKind kind, byte ending)
{
return kind switch
{
HeuristicGpuDivisorTableKind.GroupA => GroupADivisors,
HeuristicGpuDivisorTableKind.GroupBEnding1 => GroupBDivisorsEnding1,
HeuristicGpuDivisorTableKind.GroupBEnding7 => GroupBDivisorsEnding7,
HeuristicGpuDivisorTableKind.GroupBEnding9 => GroupBDivisorsEnding9,
HeuristicGpuDivisorTableKind.Combined => SelectCombined(ending),
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectDivisorSquares(HeuristicGpuDivisorTableKind kind, byte ending)
{
return kind switch
{
HeuristicGpuDivisorTableKind.GroupA => GroupADivisorSquares,
HeuristicGpuDivisorTableKind.GroupBEnding1 => GroupBDivisorSquaresEnding1,
HeuristicGpuDivisorTableKind.GroupBEnding7 => GroupBDivisorSquaresEnding7,
HeuristicGpuDivisorTableKind.GroupBEnding9 => GroupBDivisorSquaresEnding9,
HeuristicGpuDivisorTableKind.Combined => SelectCombinedSquares(ending),
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
}
internal static void InitializeShared(in HeuristicGpuDivisorTables tables)
{
Unsafe.AsRef(in Shared) = tables;
}
}
@@ -1,19 +1,9 @@
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.
@@ -61,13 +51,13 @@ internal static class PrimeTesterKernels
int length = (int)primes.Length;
for (int i = 0; i < length; i++)
{
ulong prime = primes[i];
ulong primeSquare = primeSquares[i];
if (primeSquare > n)
{
break;
}
ulong prime = primes[i];
if (n % prime == 0UL)
{
result = 0;
@@ -78,31 +68,19 @@ internal static class PrimeTesterKernels
results[index] = result;
}
public static void HeuristicTrialDivisionKernel(
Index1D index,
ArrayView<int> resultFlag,
ulong n,
ulong maxDivisorSquare,
HeuristicGpuDivisorTableKind tableKind,
HeuristicGpuDivisorTables tables)
public static void HeuristicTrialDivisionKernel(Index1D index, ArrayView<ulong> divisors, ulong n, ArrayView<int> resultFlag)
{
byte nMod10 = (byte)(n % 10UL);
ArrayView1D<ulong, Stride1D.Dense> divisors = tables.SelectDivisors(tableKind, nMod10);
ArrayView1D<ulong, Stride1D.Dense> divisorSquares = tables.SelectDivisorSquares(tableKind, nMod10);
ulong divisor = divisors[index];
if (divisor <= 1UL)
{
return;
}
int divisorLength = (int)divisorSquares.Length;
int threadIndex = index;
bool skipThread = threadIndex >= divisorLength;
ulong divisorSquare = skipThread ? 0UL : divisorSquares[threadIndex];
skipThread = skipThread | (divisorSquare > maxDivisorSquare);
ulong divisor = skipThread ? 1UL : divisors[threadIndex];
ulong remainder = skipThread ? 1UL : n % divisor;
bool hasFactor = !skipThread & (remainder == 0UL);
if (hasFactor)
{
resultFlag[0] = 1;
}
ulong residue = n % divisor;
if (residue == 0UL)
{
resultFlag[0] = 1;
}
}
public static void SharesFactorKernel(Index1D index, ArrayView<ulong> numbers, ArrayView<byte> results)
@@ -492,7 +492,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner)
{
_owner = owner;
_lease = GpuContextPool.Rent();
_lease = GpuContextPool.RentPreferred(preferCpu: false);
_accelerator = _lease.Accelerator;
_capacity = Math.Max(1, owner._gpuBatchSize);
}
@@ -657,7 +657,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
return lease;
}
return GpuContextPool.Rent();
return GpuContextPool.RentPreferred(preferCpu: false);
}
private void ReturnAccelerator(GpuContextPool.GpuContextLease lease) => _acceleratorPool.Add(lease);
@@ -37,7 +37,7 @@ public sealed class MersenneNumberDivisorGpuTester
public bool IsDivisible(ulong exponent, in ReadOnlyGpuUInt128 divisor)
{
var gpu = GpuContextPool.Rent();
var gpu = GpuContextPool.RentPreferred(preferCpu: false);
var accelerator = gpu.Accelerator;
var kernel = GetKernel(accelerator);
var resultBuffer = _resultBuffers.GetOrAdd(accelerator, acc => acc.Allocate1D<byte>(1));
@@ -38,6 +38,16 @@ public class MersenneNumberLucasLehmerGpuTester
// Configurable LL slice size to keep kernels short. Default 32.
public int SliceSize = 32;
public bool IsMersennePrime(ulong exponent)
{
// Default to global kernel preference for backward compatibility.
bool runOnGpu = !GpuContextPool.ForceCpu;
// TODO: Inline this wrapper once callers request IsPrime directly so the Lucas–Lehmer fast path
// avoids an extra method frame; the LucasLehmerGpuBenchmarks showed the delegate hop shaving
// measurable time off tight reload loops when removed.
return IsPrime(exponent, runOnGpu);
}
public bool IsPrime(ulong exponent, bool runOnGpu)
{
// Early rejections aligned with incremental/order sieves, but safe for small p:
@@ -68,7 +78,7 @@ public class MersenneNumberLucasLehmerGpuTester
}
else
{
var gpu = GpuContextPool.Rent();
var gpu = GpuContextPool.RentPreferred(preferCpu: !runOnGpu);
var accelerator = gpu.Accelerator;
var kernel = GetKernel(accelerator);
var modulus = new GpuUInt128(((UInt128)1 << (int)exponent) - 1UL); // TODO: Cache these Mersenne moduli per exponent so LL GPU runs skip rebuilding them every launch.
@@ -100,7 +110,7 @@ public class MersenneNumberLucasLehmerGpuTester
}
}
var gpu = GpuContextPool.Rent();
var gpu = GpuContextPool.RentPreferred(preferCpu: false);
var accelerator = gpu.Accelerator;
var kernel = GetBatchKernel(accelerator);
@@ -152,7 +162,7 @@ public class MersenneNumberLucasLehmerGpuTester
private bool IsMersennePrimeNtt(ulong exponent, GpuUInt128 nttMod, GpuUInt128 primitiveRoot, bool runOnGpu)
{
var gpu = GpuContextPool.Rent();
var gpu = GpuContextPool.RentPreferred(preferCpu: !runOnGpu);
var accelerator = gpu.Accelerator;
var addKernel = GetAddSmallKernel(accelerator);
var subKernel = GetSubSmallKernel(accelerator);
@@ -132,7 +132,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
return lease;
}
return GpuContextPool.Rent();
return GpuContextPool.RentPreferred(preferCpu: !_useGpuOrder);
}
private void ReturnAccelerator(GpuContextPool.GpuContextLease lease) => _acceleratorPool.Add(lease);