Remove GPU context creation lock

This commit is contained in:
Marek Lemańczyk committed 2025-10-28 13:26:20 +01:00
1 parent c39a99a4fd
commit de7ddd8db3
10 files changed
+17 -77

No files matched your search

-2
View File
@@ -227,7 +227,6 @@ public sealed class DivisorCycleCache
{
int length = divisors.Length;
var gpuLease = GpuKernelPool.GetKernel(useGpuOrder: true);
var execution = gpuLease.EnterExecutionScope();
Accelerator accelerator = gpuLease.Accelerator;
var kernel = _gpuKernelCache.GetOrAdd(accelerator, LoadKernel);
@@ -309,7 +308,6 @@ public sealed class DivisorCycleCache
ArrayPool<byte>.Shared.Return(rentedStatus!, clearArray: false);
}
execution.Dispose();
gpuLease.Dispose();
}
+6 -16
View File
@@ -47,7 +47,6 @@ public static class GpuContextPool
private static readonly ConcurrentQueue<PooledContext> CpuPool = new();
private static readonly ConcurrentQueue<PooledContext> GpuPool = new();
private static readonly object CreationLock = new();
public static GpuContextLease Rent()
{
@@ -61,20 +60,17 @@ public static class GpuContextPool
{
if (preferCpu && CpuPool.TryDequeue(out var cpu))
{
return new GpuContextLease(cpu, CreationLock);
return new GpuContextLease(cpu);
}
if (!preferCpu && GpuPool.TryDequeue(out var gpu))
{
return new GpuContextLease(gpu, CreationLock);
return new GpuContextLease(gpu);
}
}
// Serialize accelerator creation to avoid concurrent CL device/stream setup across threads.
lock (CreationLock)
{
return new GpuContextLease(new PooledContext(preferCpu), CreationLock);
}
// Create a new accelerator when the pool does not have one available.
return new GpuContextLease(new PooledContext(preferCpu));
}
public static void DisposeAll()
@@ -114,10 +110,7 @@ public static class GpuContextPool
else
{
lock (CreationLock)
{
ctx.Dispose();
}
ctx.Dispose();
}
}
@@ -125,18 +118,15 @@ public static class GpuContextPool
{
private readonly PooledContext _ctx;
internal GpuContextLease(PooledContext ctx, object kernelInitLock)
internal GpuContextLease(PooledContext ctx)
{
_ctx = ctx;
KernelInitLock = kernelInitLock;
}
public Context Context => _ctx.Context;
public Accelerator Accelerator => _ctx.Accelerator;
public object KernelInitLock { get; }
public object ExecutionLock => _ctx.ExecutionLock;
+6 -33
View File
@@ -14,7 +14,6 @@ public sealed class GpuKernelLease
private GpuContextLease? _gpu;
private KernelContainer? _kernels;
private AcceleratorStream? _stream;
private object? _executionLock;
private GpuKernelLease()
{
@@ -31,7 +30,6 @@ public sealed class GpuKernelLease
lease._gpu = gpu;
lease._kernels = kernels;
lease._stream = null;
lease._executionLock = gpu.ExecutionLock;
return lease;
}
@@ -53,14 +51,12 @@ public sealed class GpuKernelLease
}
}
public ExecutionScope EnterExecutionScope() => new ExecutionScope(_executionLock);
public Action<AcceleratorStream, Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>> OrderKernel
{
get
{
var accel = Accelerator; // avoid capturing 'this' in lambda
lock (_gpu!.Value.KernelInitLock)
lock (_gpu!.Value.ExecutionLock)
{
return KernelContainer.InitOnce(ref _kernels!.Order, () =>
{
@@ -77,7 +73,7 @@ public sealed class GpuKernelLease
get
{
var accel = Accelerator;
lock (_gpu!.Value.KernelInitLock)
lock (_gpu!.Value.ExecutionLock)
{
return KernelContainer.InitOnce(ref _kernels!.Pow2Mod, () =>
{
@@ -94,7 +90,7 @@ public sealed class GpuKernelLease
get
{
var accel = Accelerator;
lock (_gpu!.Value.KernelInitLock)
lock (_gpu!.Value.ExecutionLock)
{
return KernelContainer.InitOnce(ref _kernels!.Incremental, () =>
{
@@ -111,7 +107,7 @@ public sealed class GpuKernelLease
get
{
var accel = Accelerator;
lock (_gpu!.Value.KernelInitLock)
lock (_gpu!.Value.ExecutionLock)
{
return KernelContainer.InitOnce(ref _kernels!.IncrementalOrder, () =>
{
@@ -128,7 +124,7 @@ public sealed class GpuKernelLease
get
{
var accel = Accelerator;
lock (_gpu!.Value.KernelInitLock)
lock (_gpu!.Value.ExecutionLock)
{
return KernelContainer.InitOnce(ref _kernels!.Pow2ModOrder, () =>
{
@@ -145,7 +141,7 @@ public sealed class GpuKernelLease
get
{
var accel = Accelerator;
lock (_gpu!.Value.KernelInitLock)
lock (_gpu!.Value.ExecutionLock)
{
return KernelContainer.InitOnce(ref _kernels!.SmallPrimeFactor, () =>
{
@@ -165,8 +161,6 @@ public sealed class GpuKernelLease
_gpu?.Dispose();
_gpu = null;
_executionLock = null;
_releaseLimiter?.Invoke();
_releaseLimiter = null;
@@ -174,25 +168,4 @@ public sealed class GpuKernelLease
Pool.Enqueue(this);
}
public readonly struct ExecutionScope
{
private readonly object? _lock;
public ExecutionScope(object? sync)
{
_lock = sync;
if (_lock is not null)
{
Monitor.Enter(_lock);
}
}
public void Dispose()
{
if (_lock is not null)
{
Monitor.Exit(_lock);
}
}
}
}
-2
View File
@@ -293,12 +293,10 @@ public class GpuKernelPool
public static void Run(Action<Accelerator, AcceleratorStream> action)
{
var lease = GetKernel(useGpuOrder: true);
var execution = lease.EnterExecutionScope();
var accelerator = lease.Accelerator;
var stream = accelerator.CreateStream();
action(accelerator, stream);
stream.Dispose();
execution.Dispose();
lease.Dispose();
}
@@ -17,7 +17,6 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
throw new NotImplementedException($"GPU incremental scanning requires the device cycle heuristics implementation (kernel: {_kernelType}, GPU order: {_useGpuOrder}).");
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
var execution = gpuLease.EnterExecutionScope();
var accelerator = gpuLease.Accelerator;
var stream = gpuLease.Stream;
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy
@@ -123,7 +122,6 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
ArrayPool<ulong>.Shared.Return(orderArray);
orderBuffer.Dispose();
execution.Dispose();
gpuLease.Dispose();
}
}
@@ -10,7 +10,6 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
{
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
var execution = gpuLease.EnterExecutionScope();
var accelerator = gpuLease.Accelerator;
var stream = gpuLease.Stream;
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR
@@ -61,7 +60,6 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
}
foundBuffer.Dispose();
execution.Dispose();
gpuLease.Dispose();
}
}
@@ -194,21 +194,20 @@ internal static partial class PrimeOrderGpuHeuristics
try
{
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
var execution = lease.EnterExecutionScope();
Accelerator accelerator = lease.Accelerator;
AcceleratorStream stream = lease.Stream;
var kernel = GetPartialFactorKernel(accelerator);
SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator);
SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator);
// TODO: We should create / reallocate these buffer only once or if the new required length is bigger than capacity.
// TODO: We should create / reallocate these buffer only once or if the new required length is bigger than capacity.
var factorBuffer = accelerator.Allocate1D<ulong>(primeTargets.Length);
var exponentBuffer = accelerator.Allocate1D<int>(exponentTargets.Length);
var countBuffer = accelerator.Allocate1D<int>(1);
var remainingBuffer = accelerator.Allocate1D<ulong>(1);
var fullyFactoredBuffer = accelerator.Allocate1D<byte>(1);
// TODO: There is no need to clear these buffers because the kernel will always assign values within the required bounds.
// TODO: There is no need to clear these buffers because the kernel will always assign values within the required bounds.
// factorBuffer.MemSetToZero();
// exponentBuffer.MemSetToZero();
// countBuffer.MemSetToZero();
@@ -242,13 +241,12 @@ internal static partial class PrimeOrderGpuHeuristics
fullyFactoredBuffer.View.CopyToCPU(ref fullyFactoredFlag, 1);
fullyFactored = fullyFactoredFlag != 0;
// TODO: These buffers shouldn't be disposed but rather reused accross call with the assigned accelerator.
// TODO: These buffers shouldn't be disposed but rather reused accross call with the assigned accelerator.
factorBuffer.Dispose();
exponentBuffer.Dispose();
countBuffer.Dispose();
remainingBuffer.Dispose();
fullyFactoredBuffer.Dispose();
execution.Dispose();
lease.Dispose();
return true;
}
@@ -362,12 +360,11 @@ internal static partial class PrimeOrderGpuHeuristics
order = 0UL;
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
var execution = lease.EnterExecutionScope();
Accelerator accelerator = lease.Accelerator;
AcceleratorStream stream = lease.Stream;
var kernel = GetOrderKernel(accelerator);
SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator);
SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator);
// TODO: These buffers should be created reallocated once and assigned to an accelerator. Callers should use their own
// thread static cache to prevent other threads from using taking accelerators with pre-allocated buffers if they don't
@@ -467,7 +464,6 @@ internal static partial class PrimeOrderGpuHeuristics
stackProductBuffer.Dispose();
resultBuffer.Dispose();
statusBuffer.Dispose();
execution.Dispose();
}
}
@@ -526,7 +522,6 @@ internal static partial class PrimeOrderGpuHeuristics
private static bool TryComputeOnGpu(ReadOnlySpan<ulong> exponents, ulong prime, in MontgomeryDivisorData divisorData, Span<ulong> results)
{
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
var execution = lease.EnterExecutionScope();
Accelerator accelerator = lease.Accelerator;
AcceleratorStream stream = lease.Stream;
var kernel = GetPow2ModKernel(accelerator);
@@ -551,7 +546,6 @@ internal static partial class PrimeOrderGpuHeuristics
Console.WriteLine($"Exception for {prime} and exponents: {string.Join(",", exponents.ToArray())}.");
exponentBuffer.Dispose();
remainderBuffer.Dispose();
execution.Dispose();
lease.Dispose();
throw;
}
@@ -559,7 +553,6 @@ internal static partial class PrimeOrderGpuHeuristics
remainderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(results), exponents.Length);
exponentBuffer.Dispose();
remainderBuffer.Dispose();
execution.Dispose();
lease.Dispose();
return true;
}
@@ -587,7 +580,6 @@ internal static partial class PrimeOrderGpuHeuristics
try
{
var execution = lease.EnterExecutionScope();
Accelerator accelerator = lease.Accelerator;
AcceleratorStream stream = lease.Stream;
var kernel = GetPow2ModWideKernel(accelerator);
@@ -628,7 +620,6 @@ internal static partial class PrimeOrderGpuHeuristics
exponentBuffer.Dispose();
remainderBuffer.Dispose();
execution.Dispose();
return true;
}
catch (Exception)
@@ -147,7 +147,6 @@ public sealed class MersenneNumberTester(
UInt128[] qs = qsBuffer;
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
var execution = gpuLease.EnterExecutionScope();
var accelerator = gpuLease.Accelerator;
var stream = gpuLease.Stream;
var orderKernel = gpuLease.OrderKernel!;
@@ -194,7 +193,6 @@ public sealed class MersenneNumberTester(
qBuffer.Dispose();
}
execution.Dispose();
gpuLease.Dispose();
ArrayPool<UInt128>.Shared.Return(qs);
}
@@ -22,7 +22,6 @@ internal static partial class PrimeOrderCalculator
remaining = value;
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
var execution = lease.EnterExecutionScope();
Accelerator accelerator = lease.Accelerator;
AcceleratorStream stream = lease.Stream;
@@ -64,7 +63,6 @@ internal static partial class PrimeOrderCalculator
counts.Add(primeValue, exponent);
}
execution.Dispose();
lease.Dispose();
ThreadStaticPools.UlongPool.Return(primeBufferArray);
ThreadStaticPools.IntPool.Return(exponentBufferArray);
@@ -417,7 +417,6 @@ public static partial class ULongExtensions
ulong result = 0UL;
GpuKernelLease lease = GpuKernelPool.GetKernel(useGpuOrder: true);
var execution = lease.EnterExecutionScope();
Accelerator accelerator = lease.Accelerator;
// Keep this commented out. It should never happen in production code.
@@ -444,7 +443,6 @@ public static partial class ULongExtensions
resultBuffer.Dispose();
exponentBuffer.Dispose();
execution.Dispose();
lease.Dispose();
// Keep this commented. We don't want to catch any exceptions. All should crash the scanner.
// catch (AcceleratorException)