Reuse GPU divisor tables across PrimeTester batches

This commit is contained in:
Marek Lemańczyk committed 2025-10-28 19:53:11 +01:00
1 parent c53966e481
commit 1f9421430b
1 file changed
+31 -5
+31 -5
View File
@@ -132,8 +132,8 @@ public sealed class PrimeTester
{ {
var limiter = GpuPrimeWorkLimiter.Acquire(); var limiter = GpuPrimeWorkLimiter.Acquire();
var gpu = PrimeTesterGpuContextPool.Rent(); var gpu = PrimeTesterGpuContextPool.Rent();
var state = gpu.State;
var accelerator = gpu.Accelerator; var accelerator = gpu.Accelerator;
var state = GpuKernelState.GetOrCreate(accelerator);
int totalLength = values.Length; int totalLength = values.Length;
int batchSize = Math.Max(1, GpuBatchSize); int batchSize = Math.Max(1, GpuBatchSize);
@@ -181,22 +181,40 @@ public sealed class PrimeTester
{ {
internal sealed class PooledContext internal sealed class PooledContext
{ {
public Context Context { get; } public Context Context { get; }
public Accelerator Accelerator { get; } public Accelerator Accelerator { get; }
public object ExecutionLock { get; } = new(); public object ExecutionLock { get; } = new();
private KernelState? _kernelState;
public PooledContext() public PooledContext()
{ {
Context = Context.CreateDefault(); Context = Context.CreateDefault();
Accelerator = Context.GetPreferredDevice(false).CreateAccelerator(Context); Accelerator = Context.GetPreferredDevice(false).CreateAccelerator(Context);
_kernelState = GpuKernelState.GetOrCreate(Accelerator);
}
public KernelState KernelState
{
get
{
var state = _kernelState;
if (state is null || state.IsDisposed)
{
state = GpuKernelState.GetOrCreate(Accelerator);
_kernelState = state;
}
return state;
}
} }
public void Dispose() public void Dispose()
{ {
PrimeTester.ClearGpuCaches(Accelerator); ClearGpuCaches(Accelerator);
_kernelState = null;
Accelerator.Dispose(); Accelerator.Dispose();
Context.Dispose(); Context.Dispose();
} }
@@ -245,6 +263,8 @@ public sealed class PrimeTester
public object ExecutionLock => _ctx!.ExecutionLock; public object ExecutionLock => _ctx!.ExecutionLock;
public KernelState State => _ctx!.KernelState;
public void Dispose() public void Dispose()
{ {
Return(_ctx ?? throw new InvalidOperationException("GPU context lease is not initialized.")); Return(_ctx ?? throw new InvalidOperationException("GPU context lease is not initialized."));
@@ -262,7 +282,7 @@ public sealed class PrimeTester
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastSeven { get; } public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastSeven { get; }
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastThree { get; } public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastThree { get; }
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastNine { get; } public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastNine { get; }
private readonly Accelerator _accel; public bool IsDisposed { get; private set; }
private readonly System.Collections.Concurrent.ConcurrentBag<ScratchBuffers> _scratchPool = []; private readonly System.Collections.Concurrent.ConcurrentBag<ScratchBuffers> _scratchPool = [];
// TODO: Replace this ConcurrentBag with the lock-free ring buffer variant validated in // TODO: Replace this ConcurrentBag with the lock-free ring buffer variant validated in
// GpuModularArithmeticBenchmarks so renting scratch buffers stops contending on the bag's internal locks when // GpuModularArithmeticBenchmarks so renting scratch buffers stops contending on the bag's internal locks when
@@ -270,7 +290,7 @@ public sealed class PrimeTester
public KernelState(Accelerator accelerator) public KernelState(Accelerator accelerator)
{ {
_accel = accelerator; IsDisposed = false;
// Compile once per accelerator and upload primes once. // Compile once per accelerator and upload primes once.
Kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel); Kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel);
HeuristicTrialDivisionKernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ulong, ArrayView<byte>>(PrimeTesterKernels.HeuristicTrialDivisionKernel); HeuristicTrialDivisionKernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ulong, ArrayView<byte>>(PrimeTesterKernels.HeuristicTrialDivisionKernel);
@@ -341,6 +361,11 @@ public sealed class PrimeTester
public void Clear() public void Clear()
{ {
if (IsDisposed)
{
return;
}
while (_scratchPool.TryTake(out var sb)) while (_scratchPool.TryTake(out var sb))
{ {
sb.Dispose(); sb.Dispose();
@@ -351,6 +376,7 @@ public sealed class PrimeTester
DevicePrimesLastSeven.Dispose(); DevicePrimesLastSeven.Dispose();
DevicePrimesLastThree.Dispose(); DevicePrimesLastThree.Dispose();
DevicePrimesLastNine.Dispose(); DevicePrimesLastNine.Dispose();
IsDisposed = true;
} }
} }