GpuContextPool removed, because we share context and accelerator between threads, using streams to separate.

This commit is contained in:
mlemanczyk committed 2025-11-07 12:52:20 +01:00
1 parent 7c035239b4
commit f4e365194d
19 files changed
+285 -425

No files matched your search

+1 -1
View File
@@ -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);
-107
View File
@@ -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);
}
}
+100 -114
View File
@@ -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();
}
}
}
+47 -26
View File
@@ -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 _ => []);
+6 -23
View File
@@ -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
{
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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;
+2 -2
View File
@@ -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);
+1 -1
View File
@@ -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;