Address feedback on GPU pooling and heuristic factoring

This commit is contained in:
Marek Lemańczyk authored and mlemanczyk committed 2025-11-02 11:22:49 +01:00
1 parent 5339c3c304
commit 76dfe108a9
6 files changed
+92 -139

No files matched your search

+33 -59
View File
@@ -7,7 +7,6 @@ namespace PerfectNumbers.Core.Gpu;
public static class GpuContextPool public static class GpuContextPool
{ {
private static readonly bool PoolingEnabled = true;
// The pool intentionally skips pre-loading ProcessEightBitWindows kernels so accelerator initialization // 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. // 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.) // Default device preference for generic GPU kernels (prime scans, NTT, etc.)
@@ -48,7 +47,6 @@ public static class GpuContextPool
private static readonly ConcurrentQueue<PooledContext> CpuPool = new(); private static readonly ConcurrentQueue<PooledContext> CpuPool = new();
private static readonly ConcurrentQueue<PooledContext> GpuPool = new(); private static readonly ConcurrentQueue<PooledContext> GpuPool = new();
private static readonly object WarmUpLock = new();
private static int WarmedGpuContextCount; private static int WarmedGpuContextCount;
public static GpuContextLease Rent() public static GpuContextLease Rent()
@@ -59,17 +57,14 @@ public static class GpuContextPool
// Allows callers to choose CPU/GPU per use-case, decoupled from ForceCpu. // Allows callers to choose CPU/GPU per use-case, decoupled from ForceCpu.
public static GpuContextLease RentPreferred(bool preferCpu) public static GpuContextLease RentPreferred(bool preferCpu)
{ {
if (PoolingEnabled) if (preferCpu && CpuPool.TryDequeue(out var cpu))
{ {
if (preferCpu && CpuPool.TryDequeue(out var cpu)) return new GpuContextLease(cpu);
{ }
return new GpuContextLease(cpu);
}
if (!preferCpu && GpuPool.TryDequeue(out var gpu)) if (!preferCpu && GpuPool.TryDequeue(out var gpu))
{ {
return new GpuContextLease(gpu); return new GpuContextLease(gpu);
}
} }
// Create a new accelerator when the pool does not have one available. // Create a new accelerator when the pool does not have one available.
@@ -78,53 +73,40 @@ public static class GpuContextPool
public static void WarmUpPool(int threadCount) public static void WarmUpPool(int threadCount)
{ {
if (!PoolingEnabled || ForceCpu) if (ForceCpu)
{ {
return; return;
} }
if (threadCount <= 0) int target = threadCount >> 2;
{
return;
}
int target = threadCount / 4;
if (target == 0) if (target == 0)
{ {
target = 1; target = threadCount;
} }
lock (WarmUpLock) if (target <= WarmedGpuContextCount)
{ {
if (target <= WarmedGpuContextCount) return;
{
return;
}
int toCreate = target - WarmedGpuContextCount;
var contexts = new PooledContext[toCreate];
for (int i = 0; i < toCreate; i++)
{
contexts[i] = new PooledContext(preferCpu: false);
}
for (int i = 0; i < toCreate; i++)
{
GpuPool.Enqueue(contexts[i]);
}
WarmedGpuContextCount = target;
} }
int toCreate = target - WarmedGpuContextCount;
var contexts = new PooledContext[toCreate];
for (int i = 0; i < toCreate; i++)
{
contexts[i] = new PooledContext(preferCpu: false);
}
for (int i = 0; i < toCreate; i++)
{
GpuPool.Enqueue(contexts[i]);
}
WarmedGpuContextCount = target;
} }
public static void DisposeAll() public static void DisposeAll()
{ {
if (!PoolingEnabled)
{
return;
}
while (CpuPool.TryDequeue(out var cpu)) while (CpuPool.TryDequeue(out var cpu))
{ {
cpu.Dispose(); cpu.Dispose();
@@ -141,24 +123,16 @@ public static class GpuContextPool
private static void Return(PooledContext ctx) private static void Return(PooledContext ctx)
{ {
if (PoolingEnabled) ctx.Accelerator.Synchronize();
if (ctx.IsCpu)
{ {
ctx.Accelerator.Synchronize(); CpuPool.Enqueue(ctx);
if (ctx.IsCpu)
{
CpuPool.Enqueue(ctx);
}
else
{
GpuPool.Enqueue(ctx);
}
} }
else
else {
{ GpuPool.Enqueue(ctx);
ctx.Dispose(); }
} }
}
public struct GpuContextLease public struct GpuContextLease
{ {
@@ -68,11 +68,6 @@ internal static class PrimeTesterKernels
} }
ulong prime = primes[i]; ulong prime = primes[i];
if (prime == 0)
{
break;
}
if (n % prime == 0UL) if (n % prime == 0UL)
{ {
result = 0; result = 0;
@@ -94,32 +89,23 @@ internal static class PrimeTesterKernels
byte nMod10 = (byte)(n % 10UL); byte nMod10 = (byte)(n % 10UL);
ArrayView1D<ulong, Stride1D.Dense> divisors = tables.SelectDivisors(tableKind, nMod10); ArrayView1D<ulong, Stride1D.Dense> divisors = tables.SelectDivisors(tableKind, nMod10);
ArrayView1D<ulong, Stride1D.Dense> divisorSquares = tables.SelectDivisorSquares(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 divisorLength = (int)divisorSquares.Length;
int squareLength = (int)divisorSquares.Length;
int threadIndex = index; int threadIndex = index;
if (threadIndex >= divisorLength || threadIndex >= squareLength) if (threadIndex >= divisorLength)
{ {
return; return;
} }
ulong divisorSquare = divisorSquares[threadIndex]; ulong divisorSquare = divisorSquares[threadIndex];
if (divisorSquare == 0UL || divisorSquare > maxDivisorSquare) if (divisorSquare > maxDivisorSquare)
{ {
return; return;
} }
ulong divisor = divisors[threadIndex]; ulong divisor = divisors[threadIndex];
divisor = n % divisor;
if (divisor == 0UL) if (divisor == 0UL)
{
return;
}
if (n % divisor == 0UL)
{ {
resultFlag[0] = 1; resultFlag[0] = 1;
} }
@@ -235,21 +235,18 @@ public sealed class HeuristicCombinedPrimeTester
bool compositeDetected = false; bool compositeDetected = false;
int divisorLength = GetCombinedDivisors(nMod10).Length; int divisorLength = GetCombinedDivisors(nMod10).Length;
if (divisorLength > 0) int compositeFlag = 0;
{ flagView1D.CopyFromCPU(ref compositeFlag, 1);
int compositeFlag = 0; kernel(
flagView1D.CopyFromCPU(ref compositeFlag, 1); divisorLength,
kernel( flagView,
divisorLength, n,
flagView, maxDivisorSquare,
n, HeuristicGpuDivisorTableKind.Combined,
maxDivisorSquare, gpu.HeuristicGpuTables);
HeuristicGpuDivisorTableKind.Combined, accelerator.Synchronize();
gpu.HeuristicGpuTables); flagView1D.CopyToCPU(ref compositeFlag, 1);
accelerator.Synchronize(); compositeDetected = compositeFlag != 0;
flagView1D.CopyToCPU(ref compositeFlag, 1);
compositeDetected = compositeFlag != 0;
}
gpu.Dispose(); gpu.Dispose();
limiter.Dispose(); limiter.Dispose();
+11 -18
View File
@@ -252,20 +252,17 @@ public sealed class HeuristicPrimeTester
int groupALength = HeuristicPrimeSieves.GroupADivisors.Length; int groupALength = HeuristicPrimeSieves.GroupADivisors.Length;
if (groupALength > 0) flagView1D.CopyFromCPU(ref compositeFlag, 1);
{ kernel(
flagView1D.CopyFromCPU(ref compositeFlag, 1); groupALength,
kernel( flagView,
groupALength, n,
flagView, maxDivisorSquare,
n, HeuristicGpuDivisorTableKind.GroupA,
maxDivisorSquare, gpu.HeuristicGpuTables);
HeuristicGpuDivisorTableKind.GroupA, accelerator.Synchronize();
gpu.HeuristicGpuTables); flagView1D.CopyToCPU(ref compositeFlag, 1);
accelerator.Synchronize(); compositeDetected = compositeFlag != 0;
flagView1D.CopyToCPU(ref compositeFlag, 1);
compositeDetected = compositeFlag != 0;
}
if (!compositeDetected) if (!compositeDetected)
{ {
@@ -287,10 +284,6 @@ public sealed class HeuristicPrimeTester
} }
int divisorLength = GetGroupBDivisors(ending).Length; int divisorLength = GetGroupBDivisors(ending).Length;
if (divisorLength == 0)
{
continue;
}
compositeFlag = 0; compositeFlag = 0;
flagView1D.CopyFromCPU(ref compositeFlag, 1); flagView1D.CopyFromCPU(ref compositeFlag, 1);
+10 -10
View File
@@ -1289,11 +1289,7 @@ internal static partial class PrimeOrderCalculator
if (primeSlotArray is not null) if (primeSlotArray is not null)
{ {
ThreadStaticPools.UlongPool.Return(primeSlotArray, clearArray: false); ThreadStaticPools.UlongPool.Return(primeSlotArray, clearArray: false);
} ThreadStaticPools.IntPool.Return(exponentSlotArray!, clearArray: false);
if (exponentSlotArray is not null)
{
ThreadStaticPools.IntPool.Return(exponentSlotArray, clearArray: false);
} }
if (compositeStack is not null) if (compositeStack is not null)
@@ -1340,12 +1336,20 @@ internal static partial class PrimeOrderCalculator
} }
ulong primeSquare = squares[i]; ulong primeSquare = squares[i];
// primeSquare will never == 0 in production code
// if (primeSquare != 0 && primeSquare > remainingLocal)
if (primeSquare > remainingLocal) if (primeSquare > remainingLocal)
{ {
break; break;
} }
ulong primeValue = primeCandidate; ulong primeValue = primeCandidate;
// primeCandidate will never equal 0 in production code
// if (primeCandidate == 0UL)
// {
// continue;
// }
if ((remainingLocal % primeValue) != 0UL) if ((remainingLocal % primeValue) != 0UL)
{ {
continue; continue;
@@ -1626,11 +1630,7 @@ internal static partial class PrimeOrderCalculator
if (primeArray is not null) if (primeArray is not null)
{ {
ThreadStaticPools.UlongPool.Return(primeArray, clearArray: false); ThreadStaticPools.UlongPool.Return(primeArray, clearArray: false);
} ThreadStaticPools.IntPool.Return(exponentArray!, clearArray: false);
if (exponentArray is not null)
{
ThreadStaticPools.IntPool.Return(exponentArray, clearArray: false);
} }
primeArray = newPrimeArray; primeArray = newPrimeArray;
+22 -19
View File
@@ -152,15 +152,10 @@ public sealed class PrimeTester
return; return;
} }
if (threadCount <= 0) int target = threadCount >> 2;
{
return;
}
int target = threadCount / 4;
if (target == 0) if (target == 0)
{ {
target = 1; target = threadCount;
} }
lock (GpuWarmUpLock) lock (GpuWarmUpLock)
@@ -267,12 +262,14 @@ public sealed class PrimeTester
private static void ReleaseSharedTables(SharedHeuristicGpuTables tables) private static void ReleaseSharedTables(SharedHeuristicGpuTables tables)
{ {
if (tables is null)
{
return;
}
lock (SharedTablesLock) lock (SharedTablesLock)
{ {
if (tables.Release()) tables.Release();
{
SharedTables.Remove(tables.Key);
}
} }
} }
@@ -303,6 +300,16 @@ public sealed class PrimeTester
{ {
lease.DisposeResources(); lease.DisposeResources();
} }
lock (SharedTablesLock)
{
foreach (var entry in SharedTables.Values)
{
entry.Dispose();
}
SharedTables.Clear();
}
} }
internal static void Clear(Accelerator accelerator) internal static void Clear(Accelerator accelerator)
@@ -572,16 +579,12 @@ public sealed class PrimeTester
_referenceCount++; _referenceCount++;
} }
internal bool Release() internal void Release()
{ {
_referenceCount--; if (_referenceCount > 0)
if (_referenceCount == 0)
{ {
Dispose(); _referenceCount--;
return true;
} }
return false;
} }
private static MemoryBuffer1D<ulong, Stride1D.Dense> CopySpanToDevice(Accelerator accelerator, ReadOnlySpan<ulong> span) private static MemoryBuffer1D<ulong, Stride1D.Dense> CopySpanToDevice(Accelerator accelerator, ReadOnlySpan<ulong> span)
@@ -613,7 +616,7 @@ public sealed class PrimeTester
return buffer; return buffer;
} }
private void Dispose() internal void Dispose()
{ {
DevicePrimesDefault.Dispose(); DevicePrimesDefault.Dispose();
DevicePrimesLastOne.Dispose(); DevicePrimesLastOne.Dispose();