GpuContextPool removed, because we share context and accelerator between threads, using streams to separate.
This commit is contained in:
1 parent
7c035239b4
commit
f4e365194d
19 files changed
+285
-425
No files matched your search
@@ -269,7 +269,7 @@ public sealed class DivisorCycleCache
|
||||
int length = divisors.Length;
|
||||
var gpuLease = GpuKernelPool.Rent();
|
||||
|
||||
Accelerator accelerator = gpuLease.Accelerator;
|
||||
Accelerator accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = gpuLease.Stream;
|
||||
var kernel = _gpuKernel;
|
||||
EnsureCapacity(length);
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
using System.Collections.Concurrent;
|
||||
using ILGPU;
|
||||
using ILGPU.Runtime;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
public sealed class GpuContextLease
|
||||
{
|
||||
private GpuContextPool.PooledContext? _ctx;
|
||||
|
||||
internal GpuContextLease(GpuContextPool.PooledContext ctx)
|
||||
{
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
public Context Context => _ctx!.Context;
|
||||
public Accelerator Accelerator => _ctx!.Accelerator;
|
||||
|
||||
public void Dispose() => GpuContextPool.Return(_ctx!);
|
||||
}
|
||||
|
||||
public static class GpuContextPool
|
||||
{
|
||||
// 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.
|
||||
// Default device preference for generic GPU kernels (prime scans, NTT, etc.)
|
||||
internal sealed class PooledContext
|
||||
{
|
||||
public readonly Context Context;
|
||||
public readonly Accelerator Accelerator;
|
||||
|
||||
public PooledContext()
|
||||
{
|
||||
Context = SharedGpuContext.Context;
|
||||
Accelerator = SharedGpuContext.Accelerator;
|
||||
// NOTE: Avoid loading/compiling any kernel here to prevent implicit
|
||||
// CL stream/queue creation during accelerator construction.
|
||||
// Some OpenCL drivers are fragile when a queue is created immediately
|
||||
// after device init. Real kernels will be JIT-loaded at first use.
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Ensure all cached GPU buffers for this accelerator are released.
|
||||
// Release PrimeTester GPU state for this accelerator (kernel/device primes)
|
||||
// Clear any per-accelerator cached resources to avoid releasing
|
||||
// after the accelerator is destroyed.
|
||||
|
||||
// These resources are shared between GPU leases
|
||||
// Accelerator.Dispose();
|
||||
// Context.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ConcurrentQueue<PooledContext> GpuPool = new();
|
||||
private static int WarmedGpuContextCount;
|
||||
|
||||
// Allows callers to choose CPU/GPU per use-case, decoupled from ForceCpu.
|
||||
public static GpuContextLease Rent()
|
||||
{
|
||||
if (GpuPool.TryDequeue(out var gpu))
|
||||
{
|
||||
return new GpuContextLease(gpu);
|
||||
}
|
||||
|
||||
// Create a new accelerator when the pool does not have one available.
|
||||
return new GpuContextLease(new PooledContext());
|
||||
}
|
||||
|
||||
|
||||
public static void WarmUpPool(int threadCount)
|
||||
{
|
||||
if (threadCount <= WarmedGpuContextCount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int toCreate = threadCount - WarmedGpuContextCount;
|
||||
Console.WriteLine($"Warming up GPU context pool to {toCreate} contexts...");
|
||||
var contexts = new PooledContext[toCreate];
|
||||
var pool = GpuPool;
|
||||
PooledContext context;
|
||||
for (int i = 0; i < toCreate; i++)
|
||||
{
|
||||
context = new PooledContext();
|
||||
contexts[i] = context;
|
||||
pool.Enqueue(context);
|
||||
}
|
||||
|
||||
WarmedGpuContextCount = threadCount;
|
||||
}
|
||||
|
||||
public static void DisposeAll()
|
||||
{
|
||||
while (GpuPool.TryDequeue(out var gpu))
|
||||
{
|
||||
gpu.Dispose();
|
||||
}
|
||||
|
||||
WarmedGpuContextCount = 0;
|
||||
}
|
||||
|
||||
internal static void Return(PooledContext ctx)
|
||||
{
|
||||
GpuPool.Enqueue(ctx);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,15 @@
|
||||
using System.Collections.Concurrent;
|
||||
using ILGPU;
|
||||
using ILGPU.Algorithms;
|
||||
using ILGPU.Runtime;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
public sealed class GpuKernelLease
|
||||
{
|
||||
private static readonly ConcurrentQueue<GpuKernelLease> Pool = new();
|
||||
private static readonly ConcurrentQueue<GpuKernelLease> Pool = new();
|
||||
|
||||
private GpuContextLease _gpu;
|
||||
public KernelContainer Kernels;
|
||||
public AcceleratorStream Stream;
|
||||
public KernelContainer Kernels;
|
||||
public AcceleratorStream Stream;
|
||||
|
||||
#pragma warning disable CS8618 // _gpu and Stream will be set by Rent method
|
||||
private GpuKernelLease(KernelContainer kernels)
|
||||
@@ -20,125 +18,113 @@ public sealed class GpuKernelLease
|
||||
Kernels = kernels;
|
||||
}
|
||||
|
||||
internal static GpuKernelLease Rent(GpuContextLease gpu, AcceleratorStream stream, KernelContainer kernels)
|
||||
{
|
||||
if (!Pool.TryDequeue(out var lease))
|
||||
{
|
||||
lease = new GpuKernelLease(kernels);
|
||||
}
|
||||
internal static GpuKernelLease Rent(AcceleratorStream stream, KernelContainer kernels)
|
||||
{
|
||||
if (!Pool.TryDequeue(out var lease))
|
||||
{
|
||||
lease = new GpuKernelLease(kernels);
|
||||
}
|
||||
|
||||
lease._gpu = gpu;
|
||||
lease.Kernels = kernels;
|
||||
lease.Stream = stream;
|
||||
return lease;
|
||||
}
|
||||
return lease;
|
||||
}
|
||||
|
||||
public Accelerator Accelerator => _gpu?.Accelerator ?? throw new NullReferenceException("GPU context is already released");
|
||||
public Action<AcceleratorStream, Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>> OrderKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
return GpuKernelPool.InitOnce(ref Kernels!.Order, () =>
|
||||
{
|
||||
var loaded = SharedGpuContext.Accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>(OrderKernels.OrderKernelScan);
|
||||
var kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Action<AcceleratorStream, Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>> OrderKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
var accel = Accelerator; // avoid capturing 'this' in lambda
|
||||
return GpuKernelPool.InitOnce(ref Kernels!.Order, () =>
|
||||
{
|
||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>(OrderKernels.OrderKernelScan);
|
||||
var kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
public 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>> Pow2ModKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
return GpuKernelPool.InitOnce(ref Kernels!.Pow2Mod, () =>
|
||||
{
|
||||
var loaded = SharedGpuContext.Accelerator.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);
|
||||
return kernel.CreateLauncherDelegate<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>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public 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>> Pow2ModKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
var accel = Accelerator;
|
||||
return GpuKernelPool.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 kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<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>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>> IncrementalKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
return GpuKernelPool.InitOnce(ref Kernels!.Incremental, () =>
|
||||
{
|
||||
var loaded = SharedGpuContext.Accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalKernelScan);
|
||||
var kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>> IncrementalKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
var accel = Accelerator; // avoid capturing 'this' in lambda
|
||||
return GpuKernelPool.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 kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>> IncrementalOrderKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
return GpuKernelPool.InitOnce(ref Kernels!.IncrementalOrder, () =>
|
||||
{
|
||||
var loaded = SharedGpuContext.Accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalOrderKernelScan);
|
||||
var kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>> IncrementalOrderKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
var accel = Accelerator;
|
||||
return GpuKernelPool.InitOnce(ref Kernels!.IncrementalOrder, () =>
|
||||
{
|
||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalOrderKernelScan);
|
||||
var kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>> Pow2ModOrderKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
return GpuKernelPool.InitOnce(ref Kernels!.Pow2ModOrder, () =>
|
||||
{
|
||||
var loaded = SharedGpuContext.Accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernels.Pow2ModOrderKernelScan);
|
||||
var kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>> Pow2ModOrderKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
var accel = Accelerator;
|
||||
return GpuKernelPool.InitOnce(ref Kernels!.Pow2ModOrder, () =>
|
||||
{
|
||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernels.Pow2ModOrderKernelScan);
|
||||
var kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
public Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SpecialMaxKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
return GpuKernelPool.InitOnce(ref Kernels!.SpecialMax, () =>
|
||||
{
|
||||
var loaded = SharedGpuContext.Accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel);
|
||||
var kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SpecialMaxKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
var accel = Accelerator;
|
||||
return GpuKernelPool.InitOnce(ref Kernels!.SpecialMax, () =>
|
||||
{
|
||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel);
|
||||
var kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
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>> SmallPrimeFactorKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
return GpuKernelPool.InitOnce(ref Kernels!.SmallPrimeFactor, () =>
|
||||
{
|
||||
var loaded = SharedGpuContext.Accelerator.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);
|
||||
return kernel.CreateLauncherDelegate<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>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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>> SmallPrimeFactorKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
var accel = Accelerator;
|
||||
return GpuKernelPool.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 kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<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>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Stream.Dispose();
|
||||
_gpu.Dispose();
|
||||
Pool.Enqueue(this);
|
||||
public void Dispose()
|
||||
{
|
||||
Stream.Dispose();
|
||||
Pool.Enqueue(this);
|
||||
GpuPrimeWorkLimiter.Release();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
using System.Collections.Concurrent;
|
||||
using ILGPU;
|
||||
using ILGPU.Runtime;
|
||||
using static PerfectNumbers.Core.Gpu.GpuContextPool;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
public class GpuKernelPool
|
||||
{
|
||||
private static readonly ConcurrentDictionary<Accelerator, KernelContainer> _kernels = new();
|
||||
public static readonly KernelContainer Kernels = CreateKernels();
|
||||
|
||||
public static T InitOnce<T>(ref T? target, Func<T> valueFactory) where T : class
|
||||
{
|
||||
@@ -20,26 +19,35 @@ public class GpuKernelPool
|
||||
return Interlocked.CompareExchange(ref target, newValue, null) ?? newValue;
|
||||
}
|
||||
|
||||
internal static KernelContainer GetKernels(Accelerator accelerator, AcceleratorStream stream)
|
||||
private static KernelContainer CreateKernels()
|
||||
{
|
||||
return _kernels.GetOrAdd(accelerator, (accelerator) =>
|
||||
{
|
||||
var kernels = new KernelContainer();
|
||||
PreloadStaticTables(kernels, accelerator, stream);
|
||||
return kernels;
|
||||
});
|
||||
var kernels = new KernelContainer();
|
||||
var stream = SharedGpuContext.Accelerator.CreateStream();
|
||||
PreloadStaticTables(kernels, stream);
|
||||
stream.Synchronize();
|
||||
stream.Dispose();
|
||||
return kernels;
|
||||
}
|
||||
|
||||
internal static void PreloadStaticTables(KernelContainer kernels, Accelerator accelerator, AcceleratorStream stream)
|
||||
private static KernelContainer CreateKernels(AcceleratorStream stream)
|
||||
{
|
||||
EnsureSmallCyclesOnDevice(kernels, accelerator, stream);
|
||||
EnsureSmallPrimesOnDevice(kernels, accelerator, stream);
|
||||
EnsureSmallPrimeFactorTables(kernels, accelerator, stream);
|
||||
KernelContainer kernels = new();
|
||||
PreloadStaticTables(kernels, stream);
|
||||
return Kernels;
|
||||
}
|
||||
|
||||
internal static void PreloadStaticTables(KernelContainer kernels, AcceleratorStream stream)
|
||||
{
|
||||
_ = EnsureSmallCyclesOnDevice(kernels, stream);
|
||||
_ = EnsureSmallPrimesOnDevice(kernels, stream);
|
||||
_ = EnsureSmallPrimeFactorTables(kernels, stream);
|
||||
}
|
||||
|
||||
public static ArrayView1D<ulong, Stride1D.Dense> GetSmallCyclesOnDevice() => Kernels.SmallCycles!;
|
||||
|
||||
// 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).
|
||||
public static ArrayView1D<ulong, Stride1D.Dense> EnsureSmallCyclesOnDevice(KernelContainer kernels, Accelerator accelerator, AcceleratorStream stream)
|
||||
private static ArrayView1D<ulong, Stride1D.Dense> EnsureSmallCyclesOnDevice(KernelContainer kernels, AcceleratorStream stream)
|
||||
{
|
||||
if (kernels.SmallCycles is { } buffer)
|
||||
{
|
||||
@@ -47,13 +55,20 @@ public class GpuKernelPool
|
||||
}
|
||||
|
||||
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 = SharedGpuContext.Accelerator.Allocate1D<ulong>(host.Length);
|
||||
device.View.CopyFromCPU(stream, host);
|
||||
kernels.SmallCycles = device;
|
||||
return device.View;
|
||||
}
|
||||
|
||||
public static ResiduePrimeViews EnsureSmallPrimesOnDevice(KernelContainer kernels, Accelerator accelerator, AcceleratorStream stream)
|
||||
public static ResiduePrimeViews GetSmallPrimesOnDevice() => new(
|
||||
Kernels.SmallPrimesLastOne!,
|
||||
Kernels.SmallPrimesLastSeven!,
|
||||
Kernels.SmallPrimesPow2LastOne!,
|
||||
Kernels.SmallPrimesPow2LastSeven!
|
||||
);
|
||||
|
||||
private static ResiduePrimeViews EnsureSmallPrimesOnDevice(KernelContainer kernels, AcceleratorStream stream)
|
||||
{
|
||||
if (kernels.SmallPrimesLastOne is { } lastOne &&
|
||||
kernels.SmallPrimesLastSeven is { } lastSeven &&
|
||||
@@ -71,6 +86,7 @@ public class GpuKernelPool
|
||||
return new ResiduePrimeViews(existingLastOne.View, existingLastSeven.View, existingLastOnePow2.View, existingLastSevenPow2.View);
|
||||
}
|
||||
|
||||
Accelerator accelerator = SharedGpuContext.Accelerator;
|
||||
var hostLastOne = PrimesGenerator.SmallPrimesLastOne;
|
||||
var hostLastSeven = PrimesGenerator.SmallPrimesLastSeven;
|
||||
var hostLastOnePow2 = PrimesGenerator.SmallPrimesPow2LastOne;
|
||||
@@ -93,34 +109,39 @@ public class GpuKernelPool
|
||||
return new ResiduePrimeViews(deviceLastOne.View, deviceLastSeven.View, deviceLastOnePow2.View, deviceLastSevenPow2.View);
|
||||
}
|
||||
|
||||
public static SmallPrimeFactorViews EnsureSmallPrimeFactorTables(KernelContainer kernels, Accelerator accelerator, AcceleratorStream stream)
|
||||
public static SmallPrimeFactorViews GetSmallPrimeFactorTables() => new(
|
||||
Kernels.SmallPrimeFactorsPrimes!,
|
||||
Kernels.SmallPrimeFactorsSquares!
|
||||
);
|
||||
|
||||
private static SmallPrimeFactorViews EnsureSmallPrimeFactorTables(KernelContainer kernels, AcceleratorStream stream)
|
||||
{
|
||||
if (kernels.SmallPrimeFactorsPrimes is { } primeBuffer && kernels.SmallPrimeFactorsSquares is { } squareBuffer)
|
||||
{
|
||||
return new SmallPrimeFactorViews(primeBuffer, squareBuffer, (int)primeBuffer.Length);
|
||||
return new SmallPrimeFactorViews(primeBuffer, squareBuffer);
|
||||
}
|
||||
|
||||
Accelerator accelerator = SharedGpuContext.Accelerator;
|
||||
var hostPrimes = PrimesGenerator.SmallPrimes;
|
||||
var hostSquares = PrimesGenerator.SmallPrimesPow2;
|
||||
|
||||
var devicePrimes = accelerator.Allocate1D<uint>(hostPrimes.Length);
|
||||
int length = hostPrimes.Length;
|
||||
var devicePrimes = accelerator.Allocate1D<uint>(length);
|
||||
devicePrimes.View.CopyFromCPU(stream, hostPrimes);
|
||||
var deviceSquares = accelerator.Allocate1D<ulong>(hostSquares.Length);
|
||||
var deviceSquares = accelerator.Allocate1D<ulong>(length);
|
||||
deviceSquares.View.CopyFromCPU(stream, hostSquares);
|
||||
|
||||
kernels.SmallPrimeFactorsPrimes = devicePrimes;
|
||||
kernels.SmallPrimeFactorsSquares = deviceSquares;
|
||||
|
||||
return new SmallPrimeFactorViews(devicePrimes, deviceSquares, hostPrimes.Length);
|
||||
return new SmallPrimeFactorViews(devicePrimes, deviceSquares);
|
||||
}
|
||||
|
||||
public static GpuKernelLease Rent()
|
||||
{
|
||||
GpuPrimeWorkLimiter.Acquire();
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var stream = gpu.Accelerator.CreateStream();
|
||||
var kernels = GetKernels(gpu.Accelerator, stream);
|
||||
return GpuKernelLease.Rent(gpu, stream, kernels);
|
||||
var stream = SharedGpuContext.Accelerator.CreateStream();
|
||||
return GpuKernelLease.Rent(stream, Kernels);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -130,7 +151,7 @@ public class GpuKernelPool
|
||||
public static void Run(Action<Accelerator, AcceleratorStream> action)
|
||||
{
|
||||
var lease = Rent();
|
||||
var accelerator = lease.Accelerator;
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = lease.Stream;
|
||||
action(accelerator, stream);
|
||||
lease.Dispose();
|
||||
|
||||
@@ -16,7 +16,7 @@ internal static class GpuStaticTableInitializer
|
||||
Initialized.GetOrAdd(accelerator, acc =>
|
||||
{
|
||||
PrimeTester.PrimeTesterGpuContextPool.EnsureStaticTables(kernels, acc, stream);
|
||||
GpuKernelPool.PreloadStaticTables(kernels, acc, stream);
|
||||
GpuKernelPool.PreloadStaticTables(kernels, stream);
|
||||
PrimeOrderGpuHeuristics.PreloadStaticTables( acc, stream);
|
||||
return 0;
|
||||
});
|
||||
|
||||
@@ -96,7 +96,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
ulong lastProcessed;
|
||||
|
||||
var gpuLease = GpuKernelPool.Rent();
|
||||
var accelerator = gpuLease.Accelerator;
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = gpuLease.Stream;
|
||||
|
||||
// Monitor.Enter(gpuLease.ExecutionLock);
|
||||
@@ -502,7 +502,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
{
|
||||
_owner = owner;
|
||||
_lease = GpuKernelPool.Rent();
|
||||
_accelerator = _lease.Accelerator;
|
||||
_accelerator = SharedGpuContext.Accelerator;
|
||||
_stream = _lease.Stream;
|
||||
_capacity = Math.Max(1, owner._gpuBatchSize);
|
||||
}
|
||||
|
||||
@@ -15,126 +15,124 @@ public sealed class MersenneNumberDivisorGpuTester
|
||||
|
||||
private Action<AcceleratorStream, Index1D, ulong, ReadOnlyGpuUInt128, ArrayView<byte>> GetKernel(Accelerator accelerator) =>
|
||||
_kernelCache.GetOrAdd(accelerator, acc =>
|
||||
{
|
||||
var loaded = acc.LoadAutoGroupedStreamKernel<Index1D, ulong, ReadOnlyGpuUInt128, ArrayView<byte>>(DivisorKernels.Kernel);
|
||||
var kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ReadOnlyGpuUInt128, ArrayView<byte>>>();
|
||||
});
|
||||
{
|
||||
var loaded = acc.LoadAutoGroupedStreamKernel<Index1D, ulong, ReadOnlyGpuUInt128, ArrayView<byte>>(DivisorKernels.Kernel);
|
||||
var kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ReadOnlyGpuUInt128, ArrayView<byte>>>();
|
||||
});
|
||||
|
||||
public static void BuildDivisorCandidates()
|
||||
{
|
||||
ulong[] snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
||||
(ulong divisor, uint cycle)[] list = new (ulong divisor, uint cycle)[snapshot.Length / 2];
|
||||
ulong cycle;
|
||||
int count = 0, i, snapshotLength = snapshot.Length;
|
||||
for (i = 3; i < snapshotLength; i += 2)
|
||||
{
|
||||
cycle = snapshot[i];
|
||||
if (cycle == 0U)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
public static void BuildDivisorCandidates()
|
||||
{
|
||||
ulong[] snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
||||
(ulong divisor, uint cycle)[] list = new (ulong divisor, uint cycle)[snapshot.Length / 2];
|
||||
ulong cycle;
|
||||
int count = 0, i, snapshotLength = snapshot.Length;
|
||||
for (i = 3; i < snapshotLength; i += 2)
|
||||
{
|
||||
cycle = snapshot[i];
|
||||
if (cycle == 0U)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
list[count++] = ((ulong)i, (uint)cycle);
|
||||
}
|
||||
list[count++] = ((ulong)i, (uint)cycle);
|
||||
}
|
||||
|
||||
_divisorCandidates = count == 0 ? [] : list[..count];
|
||||
}
|
||||
_divisorCandidates = count == 0 ? [] : list[..count];
|
||||
}
|
||||
|
||||
public bool IsDivisible(ulong exponent, in ReadOnlyGpuUInt128 divisor)
|
||||
{
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var accelerator = gpu.Accelerator;
|
||||
public bool IsDivisible(ulong exponent, in ReadOnlyGpuUInt128 divisor)
|
||||
{
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = accelerator.CreateStream();
|
||||
var kernel = GetKernel(accelerator);
|
||||
var resultBuffer = _resultBuffers.GetOrAdd(accelerator, acc => acc.Allocate1D<byte>(1));
|
||||
var kernel = GetKernel(accelerator);
|
||||
var resultBuffer = _resultBuffers.GetOrAdd(accelerator, acc => acc.Allocate1D<byte>(1));
|
||||
// There is no point in clearing this buffer. We always override item [0] and never use it beyond item [0]
|
||||
// resultBuffer.MemSetToZero(stream);
|
||||
kernel(stream, 1, exponent, divisor, resultBuffer.View);
|
||||
Span<byte> result = stackalloc byte[1];
|
||||
resultBuffer.View.CopyToCPU(stream, ref result[0], 1);
|
||||
stream.Synchronize();
|
||||
bool divisible = result[0] != 0;
|
||||
// resultBuffer.MemSetToZero(stream);
|
||||
kernel(stream, 1, exponent, divisor, resultBuffer.View);
|
||||
Span<byte> result = stackalloc byte[1];
|
||||
resultBuffer.View.CopyToCPU(stream, ref result[0], 1);
|
||||
stream.Synchronize();
|
||||
bool divisible = result[0] != 0;
|
||||
stream.Dispose();
|
||||
gpu.Dispose();
|
||||
return divisible;
|
||||
}
|
||||
return divisible;
|
||||
}
|
||||
|
||||
private static (ulong divisor, uint cycle)[]? _divisorCandidates = Array.Empty<(ulong divisor, uint cycle)>();
|
||||
private static (ulong divisor, uint cycle)[]? _divisorCandidates = [];
|
||||
|
||||
public bool IsPrime(ulong p, UInt128 d, ulong divisorCyclesSearchLimit, out bool divisorsExhausted)
|
||||
{
|
||||
ReadOnlyGpuUInt128 readOnlyDivisor;
|
||||
public bool IsPrime(ulong p, UInt128 d, ulong divisorCyclesSearchLimit, out bool divisorsExhausted)
|
||||
{
|
||||
ReadOnlyGpuUInt128 readOnlyDivisor;
|
||||
|
||||
if (d != UInt128.Zero)
|
||||
{
|
||||
readOnlyDivisor = new ReadOnlyGpuUInt128(d);
|
||||
if (IsDivisible(p, in readOnlyDivisor))
|
||||
{
|
||||
divisorsExhausted = true;
|
||||
return false;
|
||||
}
|
||||
if (d != UInt128.Zero)
|
||||
{
|
||||
readOnlyDivisor = new ReadOnlyGpuUInt128(d);
|
||||
if (IsDivisible(p, in readOnlyDivisor))
|
||||
{
|
||||
divisorsExhausted = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
divisorsExhausted = false;
|
||||
return true;
|
||||
}
|
||||
divisorsExhausted = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_divisorCandidates is { Length: > 0 } candidates)
|
||||
{
|
||||
int candidateCount = candidates.Length;
|
||||
for (int index = 0; index < candidateCount; index++)
|
||||
{
|
||||
(ulong candidateDivisor, uint cycle) = candidates[index];
|
||||
if (p % cycle != 0UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (_divisorCandidates is { Length: > 0 } candidates)
|
||||
{
|
||||
int candidateCount = candidates.Length;
|
||||
for (int index = 0; index < candidateCount; index++)
|
||||
{
|
||||
(ulong candidateDivisor, uint cycle) = candidates[index];
|
||||
if (p % cycle != 0UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
readOnlyDivisor = new ReadOnlyGpuUInt128(candidateDivisor); // Reusing readOnlyDivisor for candidate divisors.
|
||||
if (IsDivisible(p, in readOnlyDivisor))
|
||||
{
|
||||
divisorsExhausted = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
readOnlyDivisor = new ReadOnlyGpuUInt128(candidateDivisor); // Reusing readOnlyDivisor for candidate divisors.
|
||||
if (IsDivisible(p, in readOnlyDivisor))
|
||||
{
|
||||
divisorsExhausted = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UInt128 kMul2;
|
||||
var divisorCycles = MersenneDivisorCycles.Shared;
|
||||
UInt128 maxK2 = UInt128.MaxValue / ((UInt128)p << 1);
|
||||
ulong limit = divisorCyclesSearchLimit;
|
||||
if ((UInt128)limit > maxK2)
|
||||
{
|
||||
limit = (ulong)maxK2;
|
||||
}
|
||||
UInt128 kMul2;
|
||||
var divisorCycles = MersenneDivisorCycles.Shared;
|
||||
UInt128 maxK2 = UInt128.MaxValue / ((UInt128)p << 1);
|
||||
ulong limit = divisorCyclesSearchLimit;
|
||||
if ((UInt128)limit > maxK2)
|
||||
{
|
||||
limit = (ulong)maxK2;
|
||||
}
|
||||
|
||||
for (ulong k2 = 1UL; k2 <= limit; k2++)
|
||||
{
|
||||
kMul2 = (UInt128)k2 << 1;
|
||||
UInt128 candidate = checked(kMul2 * p);
|
||||
d = checked(candidate + UInt128.One);
|
||||
if (p < 64UL && d == ((UInt128)1 << (int)p) - UInt128.One)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (ulong k2 = 1UL; k2 <= limit; k2++)
|
||||
{
|
||||
kMul2 = (UInt128)k2 << 1;
|
||||
UInt128 candidate = checked(kMul2 * p);
|
||||
d = checked(candidate + UInt128.One);
|
||||
if (p < 64UL && d == ((UInt128)1 << (int)p) - UInt128.One)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
UInt128 cycle128 = MersenneDivisorCycles.GetCycle(d);
|
||||
if ((UInt128)p % cycle128 != UInt128.Zero)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
UInt128 cycle128 = MersenneDivisorCycles.GetCycle(d);
|
||||
if ((UInt128)p % cycle128 != UInt128.Zero)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
readOnlyDivisor = new ReadOnlyGpuUInt128(d); // Reusing readOnlyDivisor for generated divisors.
|
||||
if (IsDivisible(p, in readOnlyDivisor))
|
||||
{
|
||||
divisorsExhausted = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
readOnlyDivisor = new ReadOnlyGpuUInt128(d); // Reusing readOnlyDivisor for generated divisors.
|
||||
if (IsDivisible(p, in readOnlyDivisor))
|
||||
{
|
||||
divisorsExhausted = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
divisorsExhausted = (UInt128)divisorCyclesSearchLimit >= maxK2;
|
||||
return true;
|
||||
}
|
||||
divisorsExhausted = (UInt128)divisorCyclesSearchLimit >= maxK2;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ 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.Rent();
|
||||
var accelerator = gpuLease.Accelerator;
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = gpuLease.Stream;
|
||||
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy
|
||||
UInt128 kStart = 1UL;
|
||||
@@ -31,11 +31,11 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
|
||||
ulong step3 = ((exponent % 3UL) << 1) % 3UL;
|
||||
ulong step5 = ((exponent % 5UL) << 1) % 5UL;
|
||||
GpuUInt128 twoPGpu = (GpuUInt128)twoP;
|
||||
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(gpuLease.Kernels, accelerator, stream);
|
||||
var smallCyclesView = GpuKernelPool.GetSmallCyclesOnDevice();
|
||||
ResiduePrimeViews primeViews = default;
|
||||
if (_kernelType == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(gpuLease.Kernels, accelerator, stream);
|
||||
primeViews = GpuKernelPool.GetSmallPrimesOnDevice();
|
||||
}
|
||||
|
||||
var orderBuffer = accelerator.Allocate1D<ulong>(batchSize);
|
||||
|
||||
@@ -97,8 +97,7 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
}
|
||||
else
|
||||
{
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var accelerator = gpu.Accelerator;
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = accelerator.CreateStream();
|
||||
var kernel = GetKernel(accelerator);
|
||||
var modulus = new GpuUInt128(((UInt128)1 << (int)exponent) - 1UL); // TODO: Cache these Mersenne moduli per exponent so LL GPU runs skip rebuilding them every launch.
|
||||
@@ -109,7 +108,6 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
result = buffer.GetAsArray1D()[0].IsZero;
|
||||
buffer.Dispose();
|
||||
stream.Dispose();
|
||||
gpu.Dispose();
|
||||
}
|
||||
|
||||
GpuPrimeWorkLimiter.Release();
|
||||
@@ -132,8 +130,7 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
}
|
||||
}
|
||||
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var accelerator = gpu.Accelerator;
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = accelerator.CreateStream();
|
||||
var kernel = GetBatchKernel(accelerator);
|
||||
|
||||
@@ -166,7 +163,6 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
gpuUInt128Pool.Return(modulusArray);
|
||||
ulongPool.Return(expArray);
|
||||
stream.Dispose();
|
||||
gpu.Dispose();
|
||||
}
|
||||
|
||||
public void IsMersennePrimeBatch(ReadOnlySpan<ulong> exponents, Span<bool> results)
|
||||
@@ -189,10 +185,10 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
|
||||
private bool IsMersennePrimeNtt(ulong exponent, GpuUInt128 nttMod, GpuUInt128 primitiveRoot, bool runOnGpu)
|
||||
{
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var accelerator = gpu.Accelerator;
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = accelerator.CreateStream();
|
||||
var addKernel = GetAddSmallKernel(accelerator);
|
||||
|
||||
var addKernel = GetAddSmallKernel(accelerator);
|
||||
var subKernel = GetSubSmallKernel(accelerator);
|
||||
var reduceKernel = GetReduceKernel(accelerator);
|
||||
var zeroKernel = GetIsZeroKernel(accelerator);
|
||||
@@ -240,7 +236,6 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
resultBuffer.Dispose();
|
||||
stateBuffer.Dispose();
|
||||
stream.Dispose();
|
||||
gpu.Dispose();
|
||||
return isPrime;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
|
||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||
{
|
||||
var gpuLease = GpuKernelPool.Rent();
|
||||
var accelerator = gpuLease.Accelerator;
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = gpuLease.Stream;
|
||||
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR
|
||||
UInt128 kStart = UInt128.One;
|
||||
@@ -30,7 +30,7 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
|
||||
int currentSize;
|
||||
int found = 0;
|
||||
|
||||
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(gpuLease.Kernels, accelerator, stream);
|
||||
var smallCyclesView = GpuKernelPool.GetSmallCyclesOnDevice();
|
||||
while (kStart <= maxK && Volatile.Read(ref isPrime))
|
||||
{
|
||||
remaining = maxK - kStart + 1UL;
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace PerfectNumbers.Core.Gpu;
|
||||
public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
{
|
||||
private readonly bool _useGpuOrder = useGpuOrder;
|
||||
private readonly ConcurrentBag<GpuContextLease> _acceleratorPool = new();
|
||||
private readonly ConcurrentDictionary<Accelerator, 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>>> _pow2ModKernelCache = new(AcceleratorReferenceComparer.Instance);
|
||||
private readonly ConcurrentDictionary<Accelerator, ConcurrentBag<ResidueResources>> _resourcePools = new(AcceleratorReferenceComparer.Instance);
|
||||
|
||||
@@ -28,15 +27,14 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||
{
|
||||
GpuPrimeWorkLimiter.Acquire();
|
||||
var gpuLease = RentAccelerator();
|
||||
var accelerator = gpuLease.Accelerator;
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
|
||||
// Monitor.Enter(gpuLease.ExecutionLock);
|
||||
|
||||
var stream = accelerator.CreateStream();
|
||||
var kernel = GetPow2ModKernel(accelerator);
|
||||
var resources = RentResources(accelerator, GpuConstants.ScanBatchSize);
|
||||
var gpuKernels = GpuKernelPool.GetKernels(accelerator, stream);
|
||||
var gpuKernels = GpuKernelPool.Kernels;
|
||||
|
||||
var orderBuffer = resources.OrderBuffer;
|
||||
ulong[] orderArray = resources.OrderArray;
|
||||
@@ -44,8 +42,8 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
|
||||
// Ensure device has small cycles and primes tables for in-kernel lookup
|
||||
// TODO: Pass-in the stream down the path
|
||||
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(gpuKernels, accelerator, stream);
|
||||
ResiduePrimeViews primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(gpuKernels, accelerator, stream);
|
||||
var smallCyclesView = GpuKernelPool.GetSmallCyclesOnDevice();
|
||||
ResiduePrimeViews primeViews = GpuKernelPool.GetSmallPrimesOnDevice();
|
||||
|
||||
UInt128 kStart = 1UL;
|
||||
UInt128 limit = maxK + UInt128.One;
|
||||
@@ -114,7 +112,6 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
stream.Dispose();
|
||||
// Monitor.Exit(gpuLease.ExecutionLock);
|
||||
gpuKernels.Dispose();
|
||||
ReturnAccelerator(gpuLease);
|
||||
GpuPrimeWorkLimiter.Release();
|
||||
}
|
||||
|
||||
@@ -130,18 +127,6 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
loaded.Method);
|
||||
});
|
||||
|
||||
private GpuContextLease RentAccelerator()
|
||||
{
|
||||
if (_acceleratorPool.TryTake(out var lease))
|
||||
{
|
||||
return lease;
|
||||
}
|
||||
|
||||
return GpuContextPool.Rent();
|
||||
}
|
||||
|
||||
private void ReturnAccelerator(GpuContextLease lease) => _acceleratorPool.Add(lease);
|
||||
|
||||
private ResidueResources RentResources(Accelerator accelerator, int capacity)
|
||||
{
|
||||
var bag = _resourcePools.GetOrAdd(accelerator, static _ => []);
|
||||
|
||||
@@ -504,8 +504,7 @@ public static class NttGpuMath
|
||||
{
|
||||
int n = values.Length;
|
||||
int bits = (int)Math.Log2(n);
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var accelerator = gpu.Accelerator;
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
var kernel = GetBitReverseKernel(accelerator);
|
||||
var pool = ThreadStaticPools.GpuUInt128Pool;
|
||||
var array = pool.Rent(n);
|
||||
@@ -520,7 +519,6 @@ public static class NttGpuMath
|
||||
pool.Return(array, clearArray: true);
|
||||
buffer.Dispose();
|
||||
stream.Dispose();
|
||||
gpu.Dispose();
|
||||
|
||||
}
|
||||
|
||||
@@ -568,8 +566,7 @@ public static class NttGpuMath
|
||||
private static void ForwardGpuReference(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
||||
{
|
||||
int n = (int)values.Length;
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var accelerator = gpu.Accelerator;
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = accelerator.CreateStream();
|
||||
var kernel = GetForwardKernel(accelerator);
|
||||
var pool = ThreadStaticPools.GpuUInt128Pool;
|
||||
@@ -592,8 +589,6 @@ public static class NttGpuMath
|
||||
outputBuffer.Dispose();
|
||||
inputBuffer.Dispose();
|
||||
stream.Dispose();
|
||||
gpu.Dispose();
|
||||
|
||||
}
|
||||
|
||||
public static void Inverse(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
||||
@@ -667,8 +662,7 @@ public static class NttGpuMath
|
||||
private static void InverseGpuReference(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
||||
{
|
||||
int n = values.Length;
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var accelerator = gpu.Accelerator;
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = accelerator.CreateStream();
|
||||
var kernel = GetInverseKernel(accelerator);
|
||||
var pool = ThreadStaticPools.GpuUInt128Pool;
|
||||
@@ -710,15 +704,12 @@ public static class NttGpuMath
|
||||
outputBuffer.Dispose();
|
||||
inputBuffer.Dispose();
|
||||
stream.Dispose();
|
||||
gpu.Dispose();
|
||||
|
||||
}
|
||||
|
||||
private static void ForwardGpuStaged(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
||||
{
|
||||
int n = values.Length;
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var accelerator = gpu.Accelerator;
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = accelerator.CreateStream();
|
||||
var buffer = accelerator.Allocate1D<GpuUInt128>(n);
|
||||
buffer.View.CopyFromCPU(stream, ref values[0], n);
|
||||
@@ -728,15 +719,12 @@ public static class NttGpuMath
|
||||
stream.Synchronize();
|
||||
stream.Dispose();
|
||||
buffer.Dispose();
|
||||
gpu.Dispose();
|
||||
|
||||
}
|
||||
|
||||
private static void InverseGpuStaged(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
||||
{
|
||||
int n = values.Length;
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var accelerator = gpu.Accelerator;
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = accelerator.CreateStream();
|
||||
var buffer = accelerator.Allocate1D<GpuUInt128>(n);
|
||||
buffer.View.CopyFromCPU(stream, ref values[0], n);
|
||||
@@ -746,8 +734,6 @@ public static class NttGpuMath
|
||||
stream.Synchronize();
|
||||
stream.Dispose();
|
||||
buffer.Dispose();
|
||||
gpu.Dispose();
|
||||
|
||||
}
|
||||
|
||||
private static void ForwardDevice(Accelerator accelerator, AcceleratorStream stream, ArrayView<GpuUInt128> data, int length, GpuUInt128 modulus, SquareCacheEntry cache)
|
||||
@@ -939,8 +925,7 @@ public static class NttGpuMath
|
||||
|
||||
public static void PointwiseMultiply(Span<GpuUInt128> left, Span<GpuUInt128> right, GpuUInt128 modulus)
|
||||
{
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var accelerator = gpu.Accelerator;
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = accelerator.CreateStream();
|
||||
var kernel = GetMulKernel(accelerator);
|
||||
var pool = ThreadStaticPools.GpuUInt128Pool;
|
||||
@@ -963,8 +948,6 @@ public static class NttGpuMath
|
||||
rightBuffer.Dispose();
|
||||
leftBuffer.Dispose();
|
||||
stream.Dispose();
|
||||
gpu.Dispose();
|
||||
|
||||
}
|
||||
|
||||
private static ulong[] GenerateSmallPrimes(int limit)
|
||||
|
||||
@@ -199,7 +199,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
try
|
||||
{
|
||||
var lease = GpuKernelPool.Rent();
|
||||
Accelerator accelerator = lease.Accelerator;
|
||||
Accelerator accelerator = SharedGpuContext.Accelerator;
|
||||
AcceleratorStream stream = lease.Stream;
|
||||
|
||||
var kernel = GetPartialFactorKernel(accelerator);
|
||||
@@ -366,7 +366,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
order = 0UL;
|
||||
|
||||
var lease = GpuKernelPool.Rent();
|
||||
Accelerator accelerator = lease.Accelerator;
|
||||
Accelerator accelerator = SharedGpuContext.Accelerator;
|
||||
AcceleratorStream stream = lease.Stream;
|
||||
|
||||
var kernel = GetOrderKernel(accelerator);
|
||||
@@ -527,7 +527,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
private static bool TryComputeOnGpu(ReadOnlySpan<ulong> exponents, ulong prime, in MontgomeryDivisorData divisorData, Span<ulong> results)
|
||||
{
|
||||
var lease = GpuKernelPool.Rent();
|
||||
Accelerator accelerator = lease.Accelerator;
|
||||
Accelerator accelerator = SharedGpuContext.Accelerator;
|
||||
AcceleratorStream stream = lease.Stream;
|
||||
var kernel = GetPow2ModKernel(accelerator);
|
||||
// TODO: These buffers should be allocated once per accelerator and only reallocated when the new length exceeds capacity.
|
||||
@@ -586,7 +586,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
|
||||
try
|
||||
{
|
||||
Accelerator accelerator = lease.Accelerator;
|
||||
Accelerator accelerator = SharedGpuContext.Accelerator;
|
||||
AcceleratorStream stream = lease.Stream;
|
||||
var kernel = GetPow2ModWideKernel(accelerator);
|
||||
// TODO: These buffers should be allocated once per accelerator and only reallocated when the new length exceeds capacity.
|
||||
|
||||
@@ -6,12 +6,11 @@ namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
public readonly struct SmallPrimeFactorViews(
|
||||
MemoryBuffer1D<uint, Stride1D.Dense> primes,
|
||||
MemoryBuffer1D<ulong, Stride1D.Dense> squares,
|
||||
int count)
|
||||
MemoryBuffer1D<ulong, Stride1D.Dense> squares)
|
||||
{
|
||||
public readonly MemoryBuffer1D<uint, Stride1D.Dense> Primes = primes;
|
||||
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> Squares = squares;
|
||||
public readonly int Count = count;
|
||||
public readonly int Count = (int)primes.Length;
|
||||
|
||||
public ArrayView1D<uint, Stride1D.Dense> PrimesView
|
||||
{
|
||||
|
||||
@@ -805,7 +805,7 @@ public class MersenneDivisorCycles
|
||||
divisors = pool.Rent(count);
|
||||
outCycles = pool.Rent(count);
|
||||
var lease = GpuKernelPool.Rent();
|
||||
var accelerator = lease.Accelerator;
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = lease.Stream;
|
||||
var loaded = accelerator.LoadAutoGroupedStreamKernel<
|
||||
Index1D,
|
||||
|
||||
@@ -148,7 +148,7 @@ public sealed class MersenneNumberTester(
|
||||
UInt128[] qs = qsBuffer;
|
||||
|
||||
var gpuLease = GpuKernelPool.Rent();
|
||||
var accelerator = gpuLease.Accelerator;
|
||||
var accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = gpuLease.Stream;
|
||||
var orderKernel = gpuLease.OrderKernel!;
|
||||
|
||||
|
||||
@@ -20,13 +20,13 @@ internal static partial class PrimeOrderCalculator
|
||||
remaining = value;
|
||||
|
||||
var lease = GpuKernelPool.Rent();
|
||||
Accelerator accelerator = lease.Accelerator;
|
||||
Accelerator accelerator = SharedGpuContext.Accelerator;
|
||||
var stream = lease.Stream;
|
||||
KernelContainer kernels = lease.Kernels;
|
||||
|
||||
ScratchBuffer scratch = GpuScratchBufferPool.Rent(accelerator, GpuSmallPrimeFactorSlots, 0);
|
||||
|
||||
SmallPrimeFactorViews tables = GpuKernelPool.EnsureSmallPrimeFactorTables(kernels, accelerator, stream);
|
||||
SmallPrimeFactorViews tables = GpuKernelPool.GetSmallPrimeFactorTables();
|
||||
|
||||
if (kernels.SmallPrimeFactorsPrimes is null) throw new NullReferenceException($"{nameof(kernels.SmallPrimeFactorsPrimes)} is null");
|
||||
if (kernels.SmallPrimeFactorsSquares is null) throw new NullReferenceException($"{nameof(kernels.SmallPrimeFactorsSquares)} is null");
|
||||
@@ -101,7 +101,7 @@ internal static partial class PrimeOrderCalculator
|
||||
}
|
||||
|
||||
GpuKernelLease lease = GpuKernelPool.Rent();
|
||||
Accelerator accelerator = lease.Accelerator;
|
||||
Accelerator accelerator = SharedGpuContext.Accelerator;
|
||||
|
||||
var stream = lease.Stream;
|
||||
|
||||
|
||||
@@ -273,7 +273,7 @@ public sealed class PrimeTester
|
||||
|
||||
Accelerator accelerator = SharedGpuContext.Accelerator;
|
||||
AcceleratorStream stream = accelerator.CreateStream();
|
||||
KernelContainer kernels = GpuKernelPool.GetKernels(accelerator, stream);
|
||||
KernelContainer kernels = GpuKernelPool.Kernels;
|
||||
|
||||
GpuStaticTableInitializer.EnsureStaticTables(kernels, accelerator, stream);
|
||||
|
||||
@@ -433,7 +433,7 @@ public sealed class PrimeTester
|
||||
AcceleratorContext = SharedGpuContext.Context;
|
||||
Accelerator = SharedGpuContext.Accelerator;
|
||||
AcceleratorStream stream = SharedGpuContext.Accelerator.CreateStream();
|
||||
var kernels = GpuKernelPool.GetKernels(Accelerator, stream);
|
||||
var kernels = GpuKernelPool.Kernels;
|
||||
GpuStaticTableInitializer.EnsureStaticTables(kernels, Accelerator, stream);
|
||||
|
||||
_sharedTables = RentSharedTables(Accelerator, stream);
|
||||
|
||||
@@ -463,7 +463,7 @@ public static partial class ULongExtensions
|
||||
|
||||
GpuKernelLease lease = GpuKernelPool.Rent();
|
||||
|
||||
Accelerator accelerator = lease.Accelerator;
|
||||
Accelerator accelerator = SharedGpuContext.Accelerator;
|
||||
AcceleratorStream stream = lease.Stream;
|
||||
var kernelGroup = Kernel;
|
||||
|
||||
|
||||
Reference in new issue
Block a user