Various improvements to make sure scratch buffers don't override each others memory

This commit is contained in:
mlemanczyk committed 2025-11-03 23:49:55 +01:00
1 parent dcda4b5cba
commit fc22c01ec8
16 files changed
+123 -199

No files matched your search

@@ -18,19 +18,19 @@ public class GpuPrimeLimiterTests
var startedSecond = new TaskCompletionSource<bool>(); var startedSecond = new TaskCompletionSource<bool>();
var sw = new Stopwatch(); var sw = new Stopwatch();
var first = GpuPrimeWorkLimiter.Acquire(); GpuPrimeWorkLimiter.Acquire();
sw.Start(); sw.Start();
var secondTask = Task.Run(() => var secondTask = Task.Run(() =>
{ {
var second = GpuPrimeWorkLimiter.Acquire(); GpuPrimeWorkLimiter.Acquire();
try try
{ {
startedSecond.TrySetResult(true); startedSecond.TrySetResult(true);
} }
finally finally
{ {
second.Dispose(); GpuPrimeWorkLimiter.Release();
} }
}); });
@@ -43,7 +43,7 @@ public class GpuPrimeLimiterTests
finally finally
{ {
// Release first after the initial wait so the second task can proceed. // Release first after the initial wait so the second task can proceed.
first.Dispose(); GpuPrimeWorkLimiter.Release();
} }
await secondTask.WaitAsync(TimeSpan.FromSeconds(1)); await secondTask.WaitAsync(TimeSpan.FromSeconds(1));
+6 -4
View File
@@ -14,11 +14,12 @@ public static class GpuContextPool
public readonly Context Context; public readonly Context Context;
public readonly Accelerator Accelerator; public readonly Accelerator Accelerator;
public PooledContext() public PooledContext(KernelContainer kernels)
{ {
Context = SharedGpuContext.Context; Context = SharedGpuContext.Context;
Accelerator = SharedGpuContext.Accelerator; Accelerator = SharedGpuContext.Accelerator;
GpuStaticTableInitializer.EnsureStaticTables(Accelerator); GpuStaticTableInitializer.EnsureStaticTables(kernels, Accelerator);
kernels.Dispose();
// NOTE: Avoid loading/compiling any kernel here to prevent implicit // NOTE: Avoid loading/compiling any kernel here to prevent implicit
// CL stream/queue creation during accelerator construction. // CL stream/queue creation during accelerator construction.
// Some OpenCL drivers are fragile when a queue is created immediately // Some OpenCL drivers are fragile when a queue is created immediately
@@ -52,7 +53,7 @@ public static class GpuContextPool
} }
// Create a new accelerator when the pool does not have one available. // Create a new accelerator when the pool does not have one available.
return new GpuContextLease(new PooledContext()); return new GpuContextLease(new PooledContext(GpuKernelPool.GetKernels()));
} }
@@ -70,7 +71,8 @@ public static class GpuContextPool
PooledContext context; PooledContext context;
for (int i = 0; i < toCreate; i++) for (int i = 0; i < toCreate; i++)
{ {
context = new PooledContext(); KernelContainer kernels = GpuKernelPool.GetKernels();
context = new PooledContext(kernels);
contexts[i] = context; contexts[i] = context;
pool.Enqueue(context); pool.Enqueue(context);
} }
+18 -21
View File
@@ -11,25 +11,24 @@ public sealed class GpuKernelLease
{ {
private static readonly ConcurrentQueue<GpuKernelLease> Pool = new(); private static readonly ConcurrentQueue<GpuKernelLease> Pool = new();
private Action? _releaseLimiter;
private GpuContextLease? _gpu; private GpuContextLease? _gpu;
private KernelContainer? _kernels; public KernelContainer Kernels;
private AcceleratorStream? _stream; private AcceleratorStream? _stream;
private GpuKernelLease() private GpuKernelLease(KernelContainer kernels)
{ {
Kernels = kernels;
} }
internal static GpuKernelLease Rent(GpuPrimeWorkLimiter.Lease limiter, GpuContextLease gpu, KernelContainer kernels) internal static GpuKernelLease Rent(GpuContextLease gpu, KernelContainer kernels)
{ {
if (!Pool.TryDequeue(out var lease)) if (!Pool.TryDequeue(out var lease))
{ {
lease = new GpuKernelLease(); lease = new GpuKernelLease(kernels);
} }
lease._releaseLimiter = limiter.Dispose;
lease._gpu = gpu; lease._gpu = gpu;
lease._kernels = kernels; lease.Kernels = kernels;
lease._stream = null; lease._stream = null;
return lease; return lease;
} }
@@ -57,7 +56,7 @@ public sealed class GpuKernelLease
get get
{ {
var accel = Accelerator; // avoid capturing 'this' in lambda var accel = Accelerator; // avoid capturing 'this' in lambda
return KernelContainer.InitOnce(ref _kernels!.Order, () => return KernelContainer.InitOnce(ref Kernels!.Order, () =>
{ {
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>(OrderKernels.OrderKernelScan); var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>(OrderKernels.OrderKernelScan);
var kernel = KernelUtil.GetKernel(loaded); var kernel = KernelUtil.GetKernel(loaded);
@@ -71,7 +70,7 @@ public sealed class GpuKernelLease
get get
{ {
var accel = Accelerator; var accel = Accelerator;
return KernelContainer.InitOnce(ref _kernels!.Pow2Mod, () => return KernelContainer.InitOnce(ref Kernels!.Pow2Mod, () =>
{ {
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernels.Pow2ModKernelScan); var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernels.Pow2ModKernelScan);
var kernel = KernelUtil.GetKernel(loaded); var kernel = KernelUtil.GetKernel(loaded);
@@ -85,7 +84,7 @@ public sealed class GpuKernelLease
get get
{ {
var accel = Accelerator; var accel = Accelerator;
return KernelContainer.InitOnce(ref _kernels!.Incremental, () => return KernelContainer.InitOnce(ref Kernels!.Incremental, () =>
{ {
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalKernelScan); var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalKernelScan);
var kernel = KernelUtil.GetKernel(loaded); var kernel = KernelUtil.GetKernel(loaded);
@@ -99,7 +98,7 @@ public sealed class GpuKernelLease
get get
{ {
var accel = Accelerator; var accel = Accelerator;
return KernelContainer.InitOnce(ref _kernels!.IncrementalOrder, () => return KernelContainer.InitOnce(ref Kernels!.IncrementalOrder, () =>
{ {
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalOrderKernelScan); var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalOrderKernelScan);
var kernel = KernelUtil.GetKernel(loaded); var kernel = KernelUtil.GetKernel(loaded);
@@ -113,7 +112,7 @@ public sealed class GpuKernelLease
get get
{ {
var accel = Accelerator; var accel = Accelerator;
return KernelContainer.InitOnce(ref _kernels!.Pow2ModOrder, () => return KernelContainer.InitOnce(ref Kernels!.Pow2ModOrder, () =>
{ {
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernels.Pow2ModOrderKernelScan); var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernels.Pow2ModOrderKernelScan);
var kernel = KernelUtil.GetKernel(loaded); var kernel = KernelUtil.GetKernel(loaded);
@@ -122,16 +121,16 @@ public sealed class GpuKernelLease
} }
} }
public Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>> SpecialMaxKernel public Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ushort, Stride1D.Dense>> SpecialMaxKernel
{ {
get get
{ {
var accel = Accelerator; var accel = Accelerator;
return KernelContainer.InitOnce(ref _kernels!.SpecialMax, () => return KernelContainer.InitOnce(ref Kernels!.SpecialMax, () =>
{ {
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel); var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ushort, Stride1D.Dense>>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel);
var kernel = KernelUtil.GetKernel(loaded); var kernel = KernelUtil.GetKernel(loaded);
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>>(); return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ushort, Stride1D.Dense>>>();
}); });
} }
} }
@@ -141,7 +140,7 @@ public sealed class GpuKernelLease
get get
{ {
var accel = Accelerator; var accel = Accelerator;
return KernelContainer.InitOnce(ref _kernels!.SmallPrimeFactor, () => return KernelContainer.InitOnce(ref Kernels!.SmallPrimeFactor, () =>
{ {
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(SmallPrimeFactorKernels.SmallPrimeFactorKernelScan); var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(SmallPrimeFactorKernels.SmallPrimeFactorKernelScan);
var kernel = KernelUtil.GetKernel(loaded); var kernel = KernelUtil.GetKernel(loaded);
@@ -158,10 +157,8 @@ public sealed class GpuKernelLease
_gpu?.Dispose(); _gpu?.Dispose();
_gpu = null; _gpu = null;
_releaseLimiter?.Invoke(); Kernels?.Dispose();
_releaseLimiter = null; GpuPrimeWorkLimiter.Release();
_kernels = null;
Pool.Enqueue(this); Pool.Enqueue(this);
} }
+42 -95
View File
@@ -38,7 +38,7 @@ public sealed class KernelContainer
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2ModOrder; ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2ModOrder;
public Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>? SmallPrimeFactor; public Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>? SmallPrimeFactor;
public Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>? SpecialMax; public Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ushort, Stride1D.Dense>>? SpecialMax;
// 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<ulong, Stride1D.Dense>? SmallCycles; public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallCycles;
@@ -50,7 +50,7 @@ public sealed class KernelContainer
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimeFactorRemainingSlot; public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimeFactorRemainingSlot;
public MemoryBuffer1D<ulong, Stride1D.Dense>? SpecialMaxFactors; public MemoryBuffer1D<ulong, Stride1D.Dense>? SpecialMaxFactors;
public MemoryBuffer1D<ulong, Stride1D.Dense>? SpecialMaxCandidates; public MemoryBuffer1D<ulong, Stride1D.Dense>? SpecialMaxCandidates;
public MemoryBuffer1D<byte, Stride1D.Dense>? SpecialMaxResult; public MemoryBuffer1D<ushort, Stride1D.Dense>? SpecialMaxResult;
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastOne; public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastOne;
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2LastOne; public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2LastOne;
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastSeven; public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastSeven;
@@ -72,6 +72,8 @@ public sealed class KernelContainer
} }
return current; return current;
} }
public void Dispose() => GpuKernelPool.Return(this);
} }
public readonly struct ResiduePrimeViews( public readonly struct ResiduePrimeViews(
@@ -86,44 +88,29 @@ public readonly struct ResiduePrimeViews(
public readonly ArrayView1D<ulong, Stride1D.Dense> LastSevenPow2 = lastSevenPow2; public readonly ArrayView1D<ulong, Stride1D.Dense> LastSevenPow2 = lastSevenPow2;
} }
public readonly struct SmallPrimeFactorTables public readonly struct SmallPrimeFactorTables(
{
public readonly MemoryBuffer1D<uint, Stride1D.Dense> Primes;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> Squares;
public readonly int Count;
public SmallPrimeFactorTables(
MemoryBuffer1D<uint, Stride1D.Dense> primes, MemoryBuffer1D<uint, Stride1D.Dense> primes,
MemoryBuffer1D<ulong, Stride1D.Dense> squares, MemoryBuffer1D<ulong, Stride1D.Dense> squares,
int count) int count)
{ {
Primes = primes; public readonly MemoryBuffer1D<uint, Stride1D.Dense> Primes = primes;
Squares = squares; public readonly MemoryBuffer1D<ulong, Stride1D.Dense> Squares = squares;
Count = count; public readonly int Count = count;
}
public ArrayView1D<uint, Stride1D.Dense> PrimesView => Primes.View; public ArrayView1D<uint, Stride1D.Dense> PrimesView => Primes.View;
public ArrayView1D<ulong, Stride1D.Dense> SquaresView => Squares.View; public ArrayView1D<ulong, Stride1D.Dense> SquaresView => Squares.View;
} }
public readonly struct SmallPrimeFactorScratch public readonly struct SmallPrimeFactorScratch(
{
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> PrimeSlots;
public readonly MemoryBuffer1D<int, Stride1D.Dense> ExponentSlots;
public readonly MemoryBuffer1D<int, Stride1D.Dense> CountSlot;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> RemainingSlot;
public SmallPrimeFactorScratch(
MemoryBuffer1D<ulong, Stride1D.Dense> primeSlots, MemoryBuffer1D<ulong, Stride1D.Dense> primeSlots,
MemoryBuffer1D<int, Stride1D.Dense> exponentSlots, MemoryBuffer1D<int, Stride1D.Dense> exponentSlots,
MemoryBuffer1D<int, Stride1D.Dense> countSlot, MemoryBuffer1D<int, Stride1D.Dense> countSlot,
MemoryBuffer1D<ulong, Stride1D.Dense> remainingSlot) MemoryBuffer1D<ulong, Stride1D.Dense> remainingSlot)
{ {
PrimeSlots = primeSlots; public readonly MemoryBuffer1D<ulong, Stride1D.Dense> PrimeSlots = primeSlots;
ExponentSlots = exponentSlots; public readonly MemoryBuffer1D<int, Stride1D.Dense> ExponentSlots = exponentSlots;
CountSlot = countSlot; public readonly MemoryBuffer1D<int, Stride1D.Dense> CountSlot = countSlot;
RemainingSlot = remainingSlot; public readonly MemoryBuffer1D<ulong, Stride1D.Dense> RemainingSlot = remainingSlot;
}
public void Clear() public void Clear()
{ {
@@ -134,74 +121,55 @@ public readonly struct SmallPrimeFactorScratch
} }
} }
public readonly struct SpecialMaxScratch public readonly struct SpecialMaxScratch(
{
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> FactorValues;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> CandidateValues;
public readonly MemoryBuffer1D<byte, Stride1D.Dense> ResultSlot;
public SpecialMaxScratch(
MemoryBuffer1D<ulong, Stride1D.Dense> factorValues, MemoryBuffer1D<ulong, Stride1D.Dense> factorValues,
MemoryBuffer1D<ulong, Stride1D.Dense> candidateValues, MemoryBuffer1D<ulong, Stride1D.Dense> candidateValues,
MemoryBuffer1D<byte, Stride1D.Dense> resultSlot) MemoryBuffer1D<ushort, Stride1D.Dense> resultSlot)
{ {
FactorValues = factorValues; public readonly MemoryBuffer1D<ulong, Stride1D.Dense> FactorValues = factorValues;
CandidateValues = candidateValues; public readonly MemoryBuffer1D<ulong, Stride1D.Dense> CandidateValues = candidateValues;
ResultSlot = resultSlot; public readonly MemoryBuffer1D<ushort, Stride1D.Dense> ResultSlot = resultSlot;
}
public ArrayView1D<ulong, Stride1D.Dense> FactorsView => FactorValues.View; public ArrayView1D<ulong, Stride1D.Dense> FactorsView => FactorValues.View;
public ArrayView1D<ulong, Stride1D.Dense> CandidatesView => CandidateValues.View; public ArrayView1D<ulong, Stride1D.Dense> CandidatesView => CandidateValues.View;
public ArrayView1D<ushort, Stride1D.Dense> ResultView => ResultSlot.View;
public ArrayView1D<byte, Stride1D.Dense> ResultView => ResultSlot.View;
} }
public class GpuKernelPool public class GpuKernelPool
{ {
private static readonly ConcurrentDictionary<Accelerator, KernelContainer> KernelCache = new(); // TODO: Replace this concurrent map with a simple accelerator-indexed lookup once kernel launchers are prewarmed during startup so we can drop the thread-safe wrapper entirely. private static readonly ConcurrentQueue<KernelContainer> KernelCache = new(); // TODO: Replace this concurrent map with a simple accelerator-indexed lookup once kernel launchers are prewarmed during startup so we can drop the thread-safe wrapper entirely.
private static KernelContainer GetKernels(Accelerator accelerator) public static KernelContainer GetKernels() => KernelCache.TryDequeue(out var container)
{ ? container
return KernelCache.GetOrAdd(accelerator, _ => new KernelContainer()); : new KernelContainer();
}
internal static void PreloadStaticTables(Accelerator accelerator) public static void Return(KernelContainer container) => KernelCache.Enqueue(container);
internal static void PreloadStaticTables(KernelContainer kernels, Accelerator accelerator)
{ {
EnsureSmallCyclesOnDevice(accelerator); EnsureSmallCyclesOnDevice(kernels, accelerator);
_ = EnsureSmallPrimesOnDevice(accelerator); EnsureSmallPrimesOnDevice(kernels, accelerator);
_ = EnsureSmallPrimeFactorTables(accelerator); EnsureSmallPrimeFactorTables(kernels, accelerator);
} }
// Ensures the small cycles table is uploaded to the device for the given accelerator. // Ensures the small cycles table is uploaded to the device for the given accelerator.
// Returns the ArrayView to pass into kernels (when kernels are extended to accept it). // Returns the ArrayView to pass into kernels (when kernels are extended to accept it).
public static ArrayView1D<ulong, Stride1D.Dense> EnsureSmallCyclesOnDevice(Accelerator accelerator) public static ArrayView1D<ulong, Stride1D.Dense> EnsureSmallCyclesOnDevice(KernelContainer kernels, Accelerator accelerator)
{ {
var kernels = GetKernels(accelerator);
if (kernels.SmallCycles is { } buffer) if (kernels.SmallCycles is { } buffer)
{ {
return buffer.View; return buffer.View;
} }
// Ensure single upload per accelerator even if multiple threads race here.
lock (kernels) // TODO: Remove this lock by pre-uploading the immutable small-cycle snapshot during initialization; once no mutation happens at runtime, the pool must expose a simple reference without synchronization.
{
if (kernels.SmallCycles is { } existing)
{
return existing.View;
}
var host = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot(); // TODO: Preload this device buffer during startup and keep it immutable so we can delete the lock above in favor of the preloaded snapshot. var host = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot(); // TODO: Preload this device buffer during startup and keep it immutable so we can delete the lock above in favor of the preloaded snapshot.
var device = accelerator.Allocate1D<ulong>(host.Length); var device = accelerator.Allocate1D<ulong>(host.Length);
device.View.CopyFromCPU(host); device.View.CopyFromCPU(host);
kernels.SmallCycles = device; kernels.SmallCycles = device;
return device.View; return device.View;
} }
}
public static ResiduePrimeViews EnsureSmallPrimesOnDevice(Accelerator accelerator) public static ResiduePrimeViews EnsureSmallPrimesOnDevice(KernelContainer kernels, Accelerator accelerator)
{ {
var kernels = GetKernels(accelerator);
if (kernels.SmallPrimesLastOne is { } lastOne && if (kernels.SmallPrimesLastOne is { } lastOne &&
kernels.SmallPrimesLastSeven is { } lastSeven && kernels.SmallPrimesLastSeven is { } lastSeven &&
kernels.SmallPrimesPow2LastOne is { } lastOnePow2 && kernels.SmallPrimesPow2LastOne is { } lastOnePow2 &&
@@ -210,8 +178,6 @@ public class GpuKernelPool
return new ResiduePrimeViews(lastOne.View, lastSeven.View, lastOnePow2.View, lastSevenPow2.View); return new ResiduePrimeViews(lastOne.View, lastSeven.View, lastOnePow2.View, lastSevenPow2.View);
} }
lock (kernels) // TODO: Inline these small-prime uploads into startup initialization alongside the small-cycle snapshot so we can drop runtime locking and keep the GPU pool free of synchronization.
{
if (kernels.SmallPrimesLastOne is { } existingLastOne && if (kernels.SmallPrimesLastOne is { } existingLastOne &&
kernels.SmallPrimesLastSeven is { } existingLastSeven && kernels.SmallPrimesLastSeven is { } existingLastSeven &&
kernels.SmallPrimesPow2LastOne is { } existingLastOnePow2 && kernels.SmallPrimesPow2LastOne is { } existingLastOnePow2 &&
@@ -241,18 +207,14 @@ public class GpuKernelPool
return new ResiduePrimeViews(deviceLastOne.View, deviceLastSeven.View, deviceLastOnePow2.View, deviceLastSevenPow2.View); return new ResiduePrimeViews(deviceLastOne.View, deviceLastSeven.View, deviceLastOnePow2.View, deviceLastSevenPow2.View);
} }
}
public static SmallPrimeFactorTables EnsureSmallPrimeFactorTables(Accelerator accelerator) public static SmallPrimeFactorTables EnsureSmallPrimeFactorTables(KernelContainer kernels, Accelerator accelerator)
{ {
var kernels = GetKernels(accelerator);
if (kernels.SmallPrimeFactorsPrimes is { } primeBuffer && kernels.SmallPrimeFactorsSquares is { } squareBuffer) if (kernels.SmallPrimeFactorsPrimes is { } primeBuffer && kernels.SmallPrimeFactorsSquares is { } squareBuffer)
{ {
return new SmallPrimeFactorTables(primeBuffer, squareBuffer, (int)primeBuffer.Length); return new SmallPrimeFactorTables(primeBuffer, squareBuffer, (int)primeBuffer.Length);
} }
lock (kernels)
{
if (kernels.SmallPrimeFactorsPrimes is { } existingPrimes && kernels.SmallPrimeFactorsSquares is { } existingSquares) if (kernels.SmallPrimeFactorsPrimes is { } existingPrimes && kernels.SmallPrimeFactorsSquares is { } existingSquares)
{ {
return new SmallPrimeFactorTables(existingPrimes, existingSquares, (int)existingPrimes.Length); return new SmallPrimeFactorTables(existingPrimes, existingSquares, (int)existingPrimes.Length);
@@ -271,12 +233,8 @@ public class GpuKernelPool
return new SmallPrimeFactorTables(devicePrimes, deviceSquares, hostPrimes.Length); return new SmallPrimeFactorTables(devicePrimes, deviceSquares, hostPrimes.Length);
} }
}
public static SmallPrimeFactorScratch EnsureSmallPrimeFactorScratch(Accelerator accelerator, int slotCount) public static SmallPrimeFactorScratch EnsureSmallPrimeFactorScratch(KernelContainer kernels, Accelerator accelerator, int slotCount)
{
var kernels = GetKernels(accelerator);
lock (kernels)
{ {
MemoryBuffer1D<ulong, Stride1D.Dense>? primeSlots = kernels.SmallPrimeFactorPrimeSlots; MemoryBuffer1D<ulong, Stride1D.Dense>? primeSlots = kernels.SmallPrimeFactorPrimeSlots;
if (primeSlots is null || primeSlots.Length < slotCount) if (primeSlots is null || primeSlots.Length < slotCount)
@@ -310,17 +268,8 @@ public class GpuKernelPool
return new SmallPrimeFactorScratch(primeSlots!, exponentSlots!, countSlot!, remainingSlot!); return new SmallPrimeFactorScratch(primeSlots!, exponentSlots!, countSlot!, remainingSlot!);
} }
}
public static SpecialMaxScratch EnsureSpecialMaxScratch(Accelerator accelerator, int factorCapacity) public static SpecialMaxScratch EnsureSpecialMaxScratch(KernelContainer kernels, Accelerator accelerator, int factorCapacity)
{
if (factorCapacity <= 0)
{
factorCapacity = 1;
}
var kernels = GetKernels(accelerator);
lock (kernels)
{ {
MemoryBuffer1D<ulong, Stride1D.Dense>? factorValues = kernels.SpecialMaxFactors; MemoryBuffer1D<ulong, Stride1D.Dense>? factorValues = kernels.SpecialMaxFactors;
if (factorValues is null || factorValues.Length < factorCapacity) if (factorValues is null || factorValues.Length < factorCapacity)
@@ -338,24 +287,22 @@ public class GpuKernelPool
kernels.SpecialMaxCandidates = candidateValues; kernels.SpecialMaxCandidates = candidateValues;
} }
MemoryBuffer1D<byte, Stride1D.Dense>? resultSlot = kernels.SpecialMaxResult; MemoryBuffer1D<ushort, Stride1D.Dense>? resultSlot = kernels.SpecialMaxResult;
if (resultSlot is null) if (resultSlot is null)
{ {
resultSlot = accelerator.Allocate1D<byte>(1); resultSlot = accelerator.Allocate1D<ushort>(1);
kernels.SpecialMaxResult = resultSlot; kernels.SpecialMaxResult = resultSlot;
} }
return new SpecialMaxScratch(factorValues!, candidateValues!, resultSlot!); return new SpecialMaxScratch(factorValues!, candidateValues!, resultSlot!);
} }
}
public static GpuKernelLease GetKernel(bool useGpuOrder) public static GpuKernelLease GetKernel(bool useGpuOrder)
{ {
var limiter = GpuPrimeWorkLimiter.Acquire(); GpuPrimeWorkLimiter.Acquire();
var gpu = Rent(); var gpu = Rent();
var accelerator = gpu.Accelerator; var kernels = GetKernels();
var kernels = GetKernels(accelerator); return GpuKernelLease.Rent(gpu, kernels);
return GpuKernelLease.Rent(limiter, gpu, kernels);
} }
/// <summary> /// <summary>
+2 -23
View File
@@ -8,14 +8,8 @@ public static class GpuPrimeWorkLimiter
private static SemaphoreSlim _semaphore = new(1, int.MaxValue); private static SemaphoreSlim _semaphore = new(1, int.MaxValue);
private static int _currentLimit = 1; private static int _currentLimit = 1;
public static Lease Acquire() public static void Acquire() => _semaphore.Wait();
{ public static void Release() => _semaphore.Release();
// TODO: Inline the pooled limiter guard from the GpuLimiterThroughputBenchmarks so prime-sieve
// GPU jobs stop allocating Lease instances on every acquisition.
var sem = _semaphore;
sem.Wait();
return new Lease(sem);
}
public static void SetLimit(int value) public static void SetLimit(int value)
{ {
@@ -36,19 +30,4 @@ public static class GpuPrimeWorkLimiter
_currentLimit = value; _currentLimit = value;
previous.Dispose(); previous.Dispose();
} }
public sealed class Lease : IDisposable
{
private readonly SemaphoreSlim _sem;
internal Lease(SemaphoreSlim sem)
{
_sem = sem;
}
public void Dispose()
{
_sem.Release();
}
}
} }
@@ -11,18 +11,13 @@ internal static class GpuStaticTableInitializer
{ {
private static readonly ConcurrentDictionary<Accelerator, byte> Initialized = new(AcceleratorReferenceComparer.Instance); private static readonly ConcurrentDictionary<Accelerator, byte> Initialized = new(AcceleratorReferenceComparer.Instance);
internal static void EnsureStaticTables(Accelerator accelerator) internal static void EnsureStaticTables(KernelContainer kernels, Accelerator accelerator)
{ {
if (accelerator is null) Initialized.GetOrAdd(accelerator, acc =>
{ {
return; PrimeTester.PrimeTesterGpuContextPool.EnsureStaticTables(kernels, acc);
} GpuKernelPool.PreloadStaticTables(kernels, acc);
PrimeOrderGpuHeuristics.PreloadStaticTables( acc);
Initialized.GetOrAdd(accelerator, static acc =>
{
PrimeTester.PrimeTesterGpuContextPool.EnsureStaticTables(acc);
GpuKernelPool.PreloadStaticTables(acc);
PrimeOrderGpuHeuristics.PreloadStaticTables(acc);
return 0; return 0;
}); });
} }
@@ -13,7 +13,7 @@ internal static partial class PrimeOrderGpuHeuristics
int factorCount, int factorCount,
MontgomeryDivisorData divisor, MontgomeryDivisorData divisor,
ArrayView1D<ulong, Stride1D.Dense> candidates, ArrayView1D<ulong, Stride1D.Dense> candidates,
ArrayView1D<byte, Stride1D.Dense> resultOut) ArrayView1D<ushort, Stride1D.Dense> resultOut)
{ {
if (index != 0) if (index != 0)
{ {
@@ -31,11 +31,11 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
ulong step3 = ((exponent % 3UL) << 1) % 3UL; ulong step3 = ((exponent % 3UL) << 1) % 3UL;
ulong step5 = ((exponent % 5UL) << 1) % 5UL; ulong step5 = ((exponent % 5UL) << 1) % 5UL;
GpuUInt128 twoPGpu = (GpuUInt128)twoP; GpuUInt128 twoPGpu = (GpuUInt128)twoP;
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator); var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(gpuLease.Kernels, accelerator);
ResiduePrimeViews primeViews = default; ResiduePrimeViews primeViews = default;
if (_kernelType == GpuKernelType.Pow2Mod) if (_kernelType == GpuKernelType.Pow2Mod)
{ {
primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(accelerator); primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(gpuLease.Kernels, accelerator);
} }
var orderBuffer = accelerator.Allocate1D<ulong>(batchSize); var orderBuffer = accelerator.Allocate1D<ulong>(batchSize);
@@ -54,13 +54,13 @@ public class MersenneNumberLucasLehmerGpuTester
return false; return false;
} }
var limiter = GpuPrimeWorkLimiter.Acquire(); GpuPrimeWorkLimiter.Acquire();
bool result; bool result;
if (exponent >= 128UL) if (exponent >= 128UL)
{ {
if (!TryGetNttParameters(exponent, out var nttMod, out var primitiveRoot)) if (!TryGetNttParameters(exponent, out var nttMod, out var primitiveRoot))
{ {
limiter.Dispose(); GpuPrimeWorkLimiter.Release();
throw new NotSupportedException("NTT parameters for the given exponent are not available."); throw new NotSupportedException("NTT parameters for the given exponent are not available.");
} }
@@ -80,7 +80,7 @@ public class MersenneNumberLucasLehmerGpuTester
gpu.Dispose(); gpu.Dispose();
} }
limiter.Dispose(); GpuPrimeWorkLimiter.Release();
return result; return result;
} }
@@ -29,6 +29,8 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
UInt128 remaining; UInt128 remaining;
int currentSize; int currentSize;
int found = 0; int found = 0;
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(gpuLease.Kernels, accelerator);
while (kStart <= maxK && Volatile.Read(ref isPrime)) while (kStart <= maxK && Volatile.Read(ref isPrime))
{ {
remaining = maxK - kStart + 1UL; remaining = maxK - kStart + 1UL;
@@ -44,7 +46,6 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
var kernelArgs = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5); var kernelArgs = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
// Ensure device has small cycles table for early rejections in order kernels // Ensure device has small cycles table for early rejections in order kernels
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
kernel(stream, currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, divMul, kernel(stream, currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, divMul,
kernelArgs, foundBuffer.View, smallCyclesView); kernelArgs, foundBuffer.View, smallCyclesView);
@@ -26,7 +26,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
// GPU residue variant: check 2^p % q == 1 for q = 2*p*k + 1. // GPU residue variant: check 2^p % q == 1 for q = 2*p*k + 1.
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime) public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
{ {
var limiter = GpuPrimeWorkLimiter.Acquire(); GpuPrimeWorkLimiter.Acquire();
var gpuLease = RentAccelerator(); var gpuLease = RentAccelerator();
var accelerator = gpuLease.Accelerator; var accelerator = gpuLease.Accelerator;
@@ -35,14 +35,15 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
var stream = accelerator.CreateStream(); var stream = accelerator.CreateStream();
var kernel = GetPow2ModKernel(accelerator); var kernel = GetPow2ModKernel(accelerator);
var resources = RentResources(accelerator, GpuConstants.ScanBatchSize); var resources = RentResources(accelerator, GpuConstants.ScanBatchSize);
var gpuKernels = GpuKernelPool.GetKernels();
var orderBuffer = resources.OrderBuffer; var orderBuffer = resources.OrderBuffer;
ulong[] orderArray = resources.OrderArray; ulong[] orderArray = resources.OrderArray;
int batchSize = resources.Capacity; int batchSize = resources.Capacity;
// Ensure device has small cycles and primes tables 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(gpuKernels, accelerator);
ResiduePrimeViews primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(accelerator); ResiduePrimeViews primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(gpuKernels, accelerator);
UInt128 kStart = 1UL; UInt128 kStart = 1UL;
UInt128 limit = maxK + UInt128.One; UInt128 limit = maxK + UInt128.One;
@@ -109,8 +110,9 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
ReturnResources(accelerator, resources); ReturnResources(accelerator, resources);
stream.Dispose(); stream.Dispose();
// Monitor.Exit(gpuLease.ExecutionLock); // Monitor.Exit(gpuLease.ExecutionLock);
gpuKernels.Dispose();
ReturnAccelerator(gpuLease); ReturnAccelerator(gpuLease);
limiter.Dispose(); GpuPrimeWorkLimiter.Release();
} }
private Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> GetPow2ModKernel(Accelerator accelerator) => private Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> GetPow2ModKernel(Accelerator accelerator) =>
@@ -1,5 +1,4 @@
using Open.Numeric.Primes; using Open.Numeric.Primes;
using System;
using ILGPU.Runtime; using ILGPU.Runtime;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
@@ -225,10 +224,10 @@ public sealed class HeuristicCombinedPrimeTester
private static bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10) private static bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10)
{ {
var limiter = GpuPrimeWorkLimiter.Acquire(); GpuPrimeWorkLimiter.Acquire();
var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(1); var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(1);
var accelerator = gpu.Accelerator; var accelerator = gpu.Accelerator;
var kernel = gpu.HeuristicTrialDivisionKernel; var kernel = PrimeTester.PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.HeuristicTrialDivisionKernel;
var flagView1D = gpu.HeuristicFlag.View; var flagView1D = gpu.HeuristicFlag.View;
var flagView = flagView1D.AsContiguous(); var flagView = flagView1D.AsContiguous();
@@ -249,7 +248,7 @@ public sealed class HeuristicCombinedPrimeTester
compositeDetected = compositeFlag != 0; compositeDetected = compositeFlag != 0;
gpu.Dispose(); gpu.Dispose();
limiter.Dispose(); GpuPrimeWorkLimiter.Release();
return compositeDetected; return compositeDetected;
} }
+3 -3
View File
@@ -240,10 +240,10 @@ public sealed class HeuristicPrimeTester
private bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10) private bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10)
{ {
var limiter = GpuPrimeWorkLimiter.Acquire(); GpuPrimeWorkLimiter.Acquire();
var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(1); var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(1);
var accelerator = gpu.Accelerator; var accelerator = gpu.Accelerator;
var kernel = gpu.HeuristicTrialDivisionKernel; var kernel = PrimeTester.PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.HeuristicTrialDivisionKernel;
var flagView1D = gpu.HeuristicFlag.View; var flagView1D = gpu.HeuristicFlag.View;
var flagView = flagView1D.AsContiguous(); var flagView = flagView1D.AsContiguous();
@@ -301,7 +301,7 @@ public sealed class HeuristicPrimeTester
} }
gpu.Dispose(); gpu.Dispose();
limiter.Dispose(); GpuPrimeWorkLimiter.Release();
return compositeDetected; return compositeDetected;
} }
@@ -3,6 +3,7 @@ using System.Diagnostics;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using ILGPU;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
@@ -106,6 +107,7 @@ internal static partial class PrimeOrderCalculator
return candidateOrder; return candidateOrder;
} }
// private static ulong specialMaxHits;
private static bool TrySpecialMaxCpu(ulong phi, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData) private static bool TrySpecialMaxCpu(ulong phi, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData)
{ {
int length = factors.Count; int length = factors.Count;
@@ -125,6 +127,8 @@ internal static partial class PrimeOrderCalculator
return EvaluateSpecialMaxCandidates(stackBuffer, factorSpan, phi, prime, divisorData); return EvaluateSpecialMaxCandidates(stackBuffer, factorSpan, phi, prime, divisorData);
} }
// Atomic.Add(ref specialMaxHits, 1);
// Console.WriteLine($"Special max GPU hit {specialMaxHits} ({length})");
return EvaluateSpecialMaxCandidatesGpu(stackBuffer, factorSpan, phi, prime, divisorData); return EvaluateSpecialMaxCandidatesGpu(stackBuffer, factorSpan, phi, prime, divisorData);
} }
@@ -25,8 +25,8 @@ internal static partial class PrimeOrderCalculator
Accelerator accelerator = lease.Accelerator; Accelerator accelerator = lease.Accelerator;
AcceleratorStream stream = lease.Stream; AcceleratorStream stream = lease.Stream;
SmallPrimeFactorTables tables = GpuKernelPool.EnsureSmallPrimeFactorTables(accelerator); SmallPrimeFactorTables tables = GpuKernelPool.EnsureSmallPrimeFactorTables(lease.Kernels, accelerator);
SmallPrimeFactorScratch scratch = GpuKernelPool.EnsureSmallPrimeFactorScratch(accelerator, GpuSmallPrimeFactorSlots); SmallPrimeFactorScratch scratch = GpuKernelPool.EnsureSmallPrimeFactorScratch(lease.Kernels, accelerator, GpuSmallPrimeFactorSlots);
var kernel = lease.SmallPrimeFactorKernel; var kernel = lease.SmallPrimeFactorKernel;
kernel( kernel(
@@ -92,7 +92,7 @@ internal static partial class PrimeOrderCalculator
Accelerator accelerator = lease.Accelerator; Accelerator accelerator = lease.Accelerator;
AcceleratorStream stream = lease.Stream; AcceleratorStream stream = lease.Stream;
SpecialMaxScratch scratch = GpuKernelPool.EnsureSpecialMaxScratch(accelerator, factorCount); SpecialMaxScratch scratch = GpuKernelPool.EnsureSpecialMaxScratch(lease.Kernels, accelerator, factorCapacity: factorCount);
scratch.FactorsView.SubView(0, factorCount).CopyFromCPU(ref MemoryMarshal.GetReference(factorSpan), factorCount); scratch.FactorsView.SubView(0, factorCount).CopyFromCPU(ref MemoryMarshal.GetReference(factorSpan), factorCount);
@@ -109,9 +109,8 @@ internal static partial class PrimeOrderCalculator
stream.Synchronize(); stream.Synchronize();
byte result = 0; ushort result = 0;
scratch.ResultView.CopyToCPU(ref result, 1); scratch.ResultView.CopyToCPU(ref result, 1);
lease.Dispose(); lease.Dispose();
return result != 0; return result != 0;
+18 -19
View File
@@ -95,10 +95,10 @@ public sealed class PrimeTester
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsPrimeGpu(ulong n) public static bool IsPrimeGpu(ulong n)
{ {
var limiter = GpuPrimeWorkLimiter.Acquire(); GpuPrimeWorkLimiter.Acquire();
var gpu = PrimeTesterGpuContextPool.Rent(1); var gpu = PrimeTesterGpuContextPool.Rent(1);
var accelerator = gpu.Accelerator; var accelerator = gpu.Accelerator;
var kernel = gpu.Kernel; var kernel = PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.Kernel;
ulong value = n; ulong value = n;
byte flag = 0; byte flag = 0;
@@ -127,7 +127,7 @@ public sealed class PrimeTester
outputView.CopyToCPU(ref flag, 1); outputView.CopyToCPU(ref flag, 1);
gpu.Dispose(); gpu.Dispose();
limiter.Dispose(); GpuPrimeWorkLimiter.Release();
return flag != 0; return flag != 0;
} }
@@ -160,10 +160,10 @@ public sealed class PrimeTester
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsPrimeBatchGpu(ReadOnlySpan<ulong> values, Span<byte> results) public static void IsPrimeBatchGpu(ReadOnlySpan<ulong> values, Span<byte> results)
{ {
var limiter = GpuPrimeWorkLimiter.Acquire(); GpuPrimeWorkLimiter.Acquire();
var gpu = PrimeTesterGpuContextPool.Rent(GpuBatchSize); var gpu = PrimeTesterGpuContextPool.Rent(GpuBatchSize);
var accelerator = gpu.Accelerator; var accelerator = gpu.Accelerator;
var kernel = gpu.Kernel; var kernel = PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.Kernel;
int totalLength = values.Length; int totalLength = values.Length;
int batchSize = GpuBatchSize; int batchSize = GpuBatchSize;
@@ -205,7 +205,7 @@ public sealed class PrimeTester
} }
gpu.Dispose(); gpu.Dispose();
limiter.Dispose(); GpuPrimeWorkLimiter.Release();
} }
internal static class PrimeTesterGpuContextPool internal static class PrimeTesterGpuContextPool
@@ -244,13 +244,8 @@ public sealed class PrimeTester
} }
} }
internal static void EnsureStaticTables(Accelerator accelerator) internal static void EnsureStaticTables(KernelContainer kernels, Accelerator accelerator)
{ {
if (accelerator is null)
{
return;
}
var tables = RentSharedTables(accelerator); var tables = RentSharedTables(accelerator);
var heuristicTables = tables.CreateHeuristicDivisorTables(); var heuristicTables = tables.CreateHeuristicDivisorTables();
HeuristicGpuDivisorTables.InitializeShared(in heuristicTables); HeuristicGpuDivisorTables.InitializeShared(in heuristicTables);
@@ -271,8 +266,8 @@ public sealed class PrimeTester
return; return;
} }
Accelerator accelerator = SharedGpuContext.Accelerator; KernelContainer kernels = GpuKernelPool.GetKernels();
GpuStaticTableInitializer.EnsureStaticTables(accelerator); GpuStaticTableInitializer.EnsureStaticTables(kernels, SharedGpuContext.Accelerator);
int toCreate = target - WarmedLeaseCount; int toCreate = target - WarmedLeaseCount;
for (int i = 0; i < toCreate; i++) for (int i = 0; i < toCreate; i++)
@@ -280,6 +275,7 @@ public sealed class PrimeTester
Pool.Enqueue(new PrimeTesterGpuContextLease(minBufferCapacity)); Pool.Enqueue(new PrimeTesterGpuContextLease(minBufferCapacity));
} }
kernels.Dispose();
WarmedLeaseCount = target; WarmedLeaseCount = target;
} }
} }
@@ -383,8 +379,11 @@ public sealed class PrimeTester
internal readonly Context AcceleratorContext; internal readonly Context AcceleratorContext;
public readonly Accelerator Accelerator; public readonly Accelerator Accelerator;
public readonly Action<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> Kernel;
public readonly Action<Index1D, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables> HeuristicTrialDivisionKernel; public static readonly Action<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> Kernel = SharedGpuContext.Accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel);
public static readonly Action<Index1D, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables> HeuristicTrialDivisionKernel = SharedGpuContext.Accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables>(PrimeTesterKernels.HeuristicTrialDivisionKernel);
public MemoryBuffer1D<ulong, Stride1D.Dense> Input = null!; public MemoryBuffer1D<ulong, Stride1D.Dense> Input = null!;
public MemoryBuffer1D<byte, Stride1D.Dense> Output = null!; public MemoryBuffer1D<byte, Stride1D.Dense> Output = null!;
public MemoryBuffer1D<int, Stride1D.Dense> HeuristicFlag = null!; public MemoryBuffer1D<int, Stride1D.Dense> HeuristicFlag = null!;
@@ -423,9 +422,9 @@ public sealed class PrimeTester
{ {
AcceleratorContext = SharedGpuContext.Context; AcceleratorContext = SharedGpuContext.Context;
Accelerator = SharedGpuContext.Accelerator; Accelerator = SharedGpuContext.Accelerator;
GpuStaticTableInitializer.EnsureStaticTables(Accelerator); // GpuStaticTableInitializer.EnsureStaticTables(Accelerator);
Kernel = Accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel); // Kernel = Accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel);
HeuristicTrialDivisionKernel = Accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables>(PrimeTesterKernels.HeuristicTrialDivisionKernel); // HeuristicTrialDivisionKernel = Accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables>(PrimeTesterKernels.HeuristicTrialDivisionKernel);
_sharedTables = RentSharedTables(Accelerator); _sharedTables = RentSharedTables(Accelerator);