Address feedback on GPU pooling and heuristic factoring
This commit is contained in:
1 parent
567b92792c
commit
8c662c5c85
6 files changed
+92
-139
No files matched your search
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in new issue
Block a user