Various optimizations to separate non-static data between threads and resolve read/write access issue.

This commit is contained in:
mlemanczyk committed 2025-11-04 20:45:56 +01:00
1 parent 5a17fa64d5
commit 64d615c402
25 files changed
+393 -329

No files matched your search

+1
View File
@@ -64,6 +64,7 @@ internal static class Program
GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads); GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads);
GpuContextPool.WarmUpPool(threadCount); GpuContextPool.WarmUpPool(threadCount);
PrimeTester.WarmUpGpuKernels(gpuPrimeThreads); PrimeTester.WarmUpGpuKernels(gpuPrimeThreads);
GpuScratchBufferPool.WarmUp(gpuPrimeThreads << 4, 64, 1024);
Console.WriteLine("Starting up threads..."); Console.WriteLine("Starting up threads...");
_ = UnboundedTaskScheduler.Instance; _ = UnboundedTaskScheduler.Instance;
int blockSize = Math.Max(1, _cliArguments.BlockSize); int blockSize = Math.Max(1, _cliArguments.BlockSize);
@@ -330,26 +330,26 @@ public class GpuKernelCompilationTests
private static void CompileMersenneDivisorCyclesKernel(Accelerator accelerator) private static void CompileMersenneDivisorCyclesKernel(Accelerator accelerator)
{ {
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(MersenneDivisorCyclesKernel); var kernel = accelerator.LoadAutoGroupedStreamKernel(MersenneDivisorCyclesKernel);
_ = kernel; _ = kernel;
} }
private static void CompilePrimeTesterSmallPrimeKernel(Accelerator accelerator) private static void CompilePrimeTesterSmallPrimeKernel(Accelerator accelerator)
{ {
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterSmallPrimeKernel); var kernel = accelerator.LoadAutoGroupedStreamKernel(PrimeTesterSmallPrimeKernel);
_ = kernel; _ = kernel;
} }
private static void CompilePrimeTesterSharesFactorKernel(Accelerator accelerator) private static void CompilePrimeTesterSharesFactorKernel(Accelerator accelerator)
{ {
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterSharesFactorKernel); var kernel = accelerator.LoadAutoGroupedStreamKernel(PrimeTesterSharesFactorKernel);
_ = kernel; _ = kernel;
} }
private static void CompileGpuKernelPoolKernels(Accelerator accelerator) private static void CompileGpuKernelPoolKernels(Accelerator accelerator)
{ {
_ = accelerator; _ = accelerator;
var lease = GpuKernelPool.GetKernel(useGpuOrder: false); var lease = GpuKernelPool.GetKernel();
try try
{ {
_ = lease.OrderKernel; _ = lease.OrderKernel;
@@ -363,7 +363,7 @@ public class GpuKernelCompilationTests
lease.Dispose(); lease.Dispose();
} }
var orderLease = GpuKernelPool.GetKernel(useGpuOrder: true); var orderLease = GpuKernelPool.GetKernel();
try try
{ {
_ = orderLease.OrderKernel; _ = orderLease.OrderKernel;
@@ -150,7 +150,7 @@ public class NttGpuMathTests
var modulus = new GpuUInt128(0UL, 17UL); var modulus = new GpuUInt128(0UL, 17UL);
var primitiveRoot = new GpuUInt128(0UL, 3UL); var primitiveRoot = new GpuUInt128(0UL, 3UL);
GpuContextPool.GpuContextLease gpu = GpuContextPool.Rent(); GpuContextLease gpu = GpuContextPool.Rent();
try try
{ {
var accelerator = gpu.Accelerator; var accelerator = gpu.Accelerator;
@@ -345,7 +345,7 @@ public class NttGpuMathTests
values[1] = new GpuUInt128(0UL, 2UL); values[1] = new GpuUInt128(0UL, 2UL);
var modulus = new GpuUInt128(0UL, 17UL); var modulus = new GpuUInt128(0UL, 17UL);
var primitiveRoot = new GpuUInt128(0UL, 3UL); var primitiveRoot = new GpuUInt128(0UL, 3UL);
GpuContextPool.GpuContextLease gpu = GpuContextPool.Rent(); GpuContextLease gpu = GpuContextPool.Rent();
try try
{ {
var buffer = gpu.Accelerator.Allocate1D<GpuUInt128>(values.Length); var buffer = gpu.Accelerator.Allocate1D<GpuUInt128>(values.Length);
@@ -33,7 +33,7 @@ public class NttMontgomeryTests
NttGpuMath.Square(cpu, modulus, primitiveRoot); NttGpuMath.Square(cpu, modulus, primitiveRoot);
// Device execution // Device execution
GpuContextPool.GpuContextLease context = GpuContextPool.Rent(); GpuContextLease context = GpuContextPool.Rent();
try try
{ {
var accelerator = context.Accelerator; var accelerator = context.Accelerator;
+10 -9
View File
@@ -226,9 +226,10 @@ public sealed class DivisorCycleCache
private void ComputeCyclesGpuCore(ReadOnlySpan<ulong> divisors, Span<ulong> destination) private void ComputeCyclesGpuCore(ReadOnlySpan<ulong> divisors, Span<ulong> destination)
{ {
int length = divisors.Length; int length = divisors.Length;
var gpuLease = GpuKernelPool.GetKernel(useGpuOrder: true); var gpuLease = GpuKernelPool.GetKernel();
Accelerator accelerator = gpuLease.Accelerator; Accelerator accelerator = gpuLease.Accelerator;
var stream = gpuLease.Stream;
var kernel = _gpuKernelCache.GetOrAdd(accelerator, LoadKernel); var kernel = _gpuKernelCache.GetOrAdd(accelerator, LoadKernel);
using MemoryBuffer1D<ulong, Stride1D.Dense> divisorBuffer = accelerator.Allocate1D<ulong>(length); using MemoryBuffer1D<ulong, Stride1D.Dense> divisorBuffer = accelerator.Allocate1D<ulong>(length);
@@ -258,7 +259,7 @@ public sealed class DivisorCycleCache
: new Span<byte>(rentedStatus = ArrayPool<byte>.Shared.Rent(length), 0, length); : new Span<byte>(rentedStatus = ArrayPool<byte>.Shared.Rent(length), 0, length);
ref ulong divisorRef = ref MemoryMarshal.GetReference(divisors); ref ulong divisorRef = ref MemoryMarshal.GetReference(divisors);
divisorBuffer.View.CopyFromCPU(ref divisorRef, length); divisorBuffer.View.CopyFromCPU(stream, ref divisorRef, length);
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
{ {
@@ -275,18 +276,18 @@ public sealed class DivisorCycleCache
statusSpan[i] = ByteZero; statusSpan[i] = ByteZero;
} }
powBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(powSpan), length); powBuffer.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(powSpan), length);
orderBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(orderSpan), length); orderBuffer.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(orderSpan), length);
resultBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(resultSpan), length); resultBuffer.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(resultSpan), length);
statusBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(statusSpan), length); statusBuffer.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(statusSpan), length);
int pending; int pending;
do do
{ {
kernel(length, _divisorCyclesBatchSize, divisorBuffer.View, powBuffer.View, orderBuffer.View, resultBuffer.View, statusBuffer.View); kernel(length, _divisorCyclesBatchSize, divisorBuffer.View, powBuffer.View, orderBuffer.View, resultBuffer.View, statusBuffer.View);
accelerator.Synchronize(); stream.Synchronize();
statusBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(statusSpan), length); statusBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(statusSpan), length);
pending = 0; pending = 0;
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
{ {
@@ -298,7 +299,7 @@ public sealed class DivisorCycleCache
} }
while (pending > 0); while (pending > 0);
resultBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(resultSpan), length); resultBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(resultSpan), length);
resultSpan.CopyTo(destination); resultSpan.CopyTo(destination);
if (rentedPow is not null) if (rentedPow is not null)
{ {
+20 -39
View File
@@ -4,6 +4,21 @@ using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu; 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 public static class GpuContextPool
{ {
// The pool intentionally skips pre-loading ProcessEightBitWindows kernels so accelerator initialization // The pool intentionally skips pre-loading ProcessEightBitWindows kernels so accelerator initialization
@@ -14,12 +29,10 @@ public static class GpuContextPool
public readonly Context Context; public readonly Context Context;
public readonly Accelerator Accelerator; public readonly Accelerator Accelerator;
public PooledContext(KernelContainer kernels) public PooledContext()
{ {
Context = SharedGpuContext.Context; Context = SharedGpuContext.Context;
Accelerator = SharedGpuContext.Accelerator; Accelerator = SharedGpuContext.Accelerator;
GpuStaticTableInitializer.EnsureStaticTables(kernels, Accelerator);
kernels.Dispose();
// NOTE: Avoid loading/compiling any kernel here to prevent implicit // NOTE: Avoid loading/compiling any kernel here to prevent implicit
// CL stream/queue creation during accelerator construction. // CL stream/queue creation during accelerator construction.
// Some OpenCL drivers are fragile when a queue is created immediately // Some OpenCL drivers are fragile when a queue is created immediately
@@ -29,11 +42,9 @@ public static class GpuContextPool
public void Dispose() public void Dispose()
{ {
// Ensure all cached GPU buffers for this accelerator are released. // Ensure all cached GPU buffers for this accelerator are released.
NttGpuMath.ClearCaches(Accelerator);
// Release PrimeTester GPU state for this accelerator (kernel/device primes) // Release PrimeTester GPU state for this accelerator (kernel/device primes)
// Clear any per-accelerator cached resources to avoid releasing // Clear any per-accelerator cached resources to avoid releasing
// after the accelerator is destroyed. // after the accelerator is destroyed.
PrimeTester.ClearGpuCaches(Accelerator);
// These resources are shared between GPU leases // These resources are shared between GPU leases
// Accelerator.Dispose(); // Accelerator.Dispose();
@@ -53,7 +64,7 @@ public static class GpuContextPool
} }
// Create a new accelerator when the pool does not have one available. // Create a new accelerator when the pool does not have one available.
return new GpuContextLease(new PooledContext(GpuKernelPool.GetKernels())); return new GpuContextLease(new PooledContext());
} }
@@ -71,8 +82,7 @@ public static class GpuContextPool
PooledContext context; PooledContext context;
for (int i = 0; i < toCreate; i++) for (int i = 0; i < toCreate; i++)
{ {
KernelContainer kernels = GpuKernelPool.GetKernels(); context = new PooledContext();
context = new PooledContext(kernels);
contexts[i] = context; contexts[i] = context;
pool.Enqueue(context); pool.Enqueue(context);
} }
@@ -87,41 +97,12 @@ public static class GpuContextPool
gpu.Dispose(); gpu.Dispose();
} }
PrimeTester.DisposeGpuContexts();
WarmedGpuContextCount = 0; WarmedGpuContextCount = 0;
} }
private static void Return(PooledContext ctx) internal static void Return(PooledContext ctx)
{ {
ctx.Accelerator.Synchronize(); // ctx.Accelerator.Synchronize();
GpuPool.Enqueue(ctx); GpuPool.Enqueue(ctx);
} }
public struct GpuContextLease
{
private PooledContext? _ctx;
private bool _disposed;
internal GpuContextLease(PooledContext ctx)
{
_ctx = ctx;
_disposed = false;
}
public Context Context => _ctx!.Context;
public Accelerator Accelerator => _ctx!.Accelerator;
public void Dispose()
{
if (_disposed)
{
return;
}
_disposed = true;
Return(_ctx!);
_ctx = null;
}
}
} }
+18 -40
View File
@@ -1,9 +1,6 @@
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using ILGPU; using ILGPU;
using ILGPU.Runtime; using ILGPU.Runtime;
using PerfectNumbers.Core;
using static PerfectNumbers.Core.Gpu.GpuContextPool;
namespace PerfectNumbers.Core.Gpu; namespace PerfectNumbers.Core.Gpu;
@@ -11,11 +8,13 @@ public sealed class GpuKernelLease
{ {
private static readonly ConcurrentQueue<GpuKernelLease> Pool = new(); private static readonly ConcurrentQueue<GpuKernelLease> Pool = new();
private GpuContextLease? _gpu; private GpuContextLease _gpu;
public KernelContainer Kernels; public KernelContainer Kernels;
private AcceleratorStream? _stream; public AcceleratorStream Stream;
#pragma warning disable CS8618 // _gpu and Stream will be set by Rent method
private GpuKernelLease(KernelContainer kernels) private GpuKernelLease(KernelContainer kernels)
#pragma warning restore CS8618
{ {
Kernels = kernels; Kernels = kernels;
} }
@@ -29,34 +28,18 @@ public sealed class GpuKernelLease
lease._gpu = gpu; lease._gpu = gpu;
lease.Kernels = kernels; lease.Kernels = kernels;
lease._stream = null; lease.Stream = gpu.Accelerator.CreateStream();
return lease; return lease;
} }
public Accelerator Accelerator => _gpu?.Accelerator ?? throw new NullReferenceException("GPU context is already released"); public Accelerator Accelerator => _gpu?.Accelerator ?? throw new NullReferenceException("GPU context is already released");
public AcceleratorStream Stream
{
get
{
if (_stream is { } stream)
{
return stream;
}
// It feels that we can combine these into 1 return statement, but that increases the max stack frame size to 3 from 2
stream = Accelerator.CreateStream();
_stream = stream;
return stream;
}
}
public Action<AcceleratorStream, Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>> OrderKernel public Action<AcceleratorStream, Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>> OrderKernel
{ {
get get
{ {
var accel = Accelerator; // avoid capturing 'this' in lambda var accel = Accelerator; // avoid capturing 'this' in lambda
return KernelContainer.InitOnce(ref Kernels!.Order, () => return GpuKernelPool.InitOnce(ref Kernels!.Order, () =>
{ {
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>(OrderKernels.OrderKernelScan); var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>(OrderKernels.OrderKernelScan);
var kernel = KernelUtil.GetKernel(loaded); var kernel = KernelUtil.GetKernel(loaded);
@@ -70,7 +53,7 @@ public sealed class GpuKernelLease
get get
{ {
var accel = Accelerator; var accel = Accelerator;
return KernelContainer.InitOnce(ref Kernels!.Pow2Mod, () => 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 loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernels.Pow2ModKernelScan);
var kernel = KernelUtil.GetKernel(loaded); var kernel = KernelUtil.GetKernel(loaded);
@@ -84,7 +67,7 @@ public sealed class GpuKernelLease
get get
{ {
var accel = Accelerator; var accel = Accelerator;
return KernelContainer.InitOnce(ref Kernels!.Incremental, () => 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 loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalKernelScan);
var kernel = KernelUtil.GetKernel(loaded); var kernel = KernelUtil.GetKernel(loaded);
@@ -98,7 +81,7 @@ public sealed class GpuKernelLease
get get
{ {
var accel = Accelerator; var accel = Accelerator;
return KernelContainer.InitOnce(ref Kernels!.IncrementalOrder, () => 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 loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalOrderKernelScan);
var kernel = KernelUtil.GetKernel(loaded); var kernel = KernelUtil.GetKernel(loaded);
@@ -112,7 +95,7 @@ public sealed class GpuKernelLease
get get
{ {
var accel = Accelerator; var accel = Accelerator;
return KernelContainer.InitOnce(ref Kernels!.Pow2ModOrder, () => 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 loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernels.Pow2ModOrderKernelScan);
var kernel = KernelUtil.GetKernel(loaded); var kernel = KernelUtil.GetKernel(loaded);
@@ -121,16 +104,16 @@ public sealed class GpuKernelLease
} }
} }
public Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ushort, Stride1D.Dense>> SpecialMaxKernel public Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SpecialMaxKernel
{ {
get get
{ {
var accel = Accelerator; var accel = Accelerator;
return KernelContainer.InitOnce(ref Kernels!.SpecialMax, () => return GpuKernelPool.InitOnce(ref Kernels!.SpecialMax, () =>
{ {
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ushort, Stride1D.Dense>>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel); 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); var kernel = KernelUtil.GetKernel(loaded);
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ushort, Stride1D.Dense>>>(); return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
}); });
} }
} }
@@ -140,7 +123,7 @@ public sealed class GpuKernelLease
get get
{ {
var accel = Accelerator; var accel = Accelerator;
return KernelContainer.InitOnce(ref Kernels!.SmallPrimeFactor, () => 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 loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(SmallPrimeFactorKernels.SmallPrimeFactorKernelScan);
var kernel = KernelUtil.GetKernel(loaded); var kernel = KernelUtil.GetKernel(loaded);
@@ -151,15 +134,10 @@ public sealed class GpuKernelLease
public void Dispose() public void Dispose()
{ {
_stream?.Dispose(); Stream.Dispose();
_stream = null; _gpu.Dispose();
_gpu?.Dispose();
_gpu = null;
Kernels?.Dispose();
GpuPrimeWorkLimiter.Release();
Pool.Enqueue(this); Pool.Enqueue(this);
GpuPrimeWorkLimiter.Release();
} }
} }
+51 -136
View File
@@ -1,4 +1,3 @@
using PerfectNumbers.Core;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using ILGPU; using ILGPU;
using ILGPU.Runtime; using ILGPU.Runtime;
@@ -23,7 +22,8 @@ public readonly struct ResidueAutomatonArgs
} }
} }
public sealed class KernelContainer
public sealed class KernelContainer : IDisposable
{ {
// Serializes first-time initialization of kernels/buffers per accelerator. // Serializes first-time initialization of kernels/buffers per accelerator.
public Action<AcceleratorStream, Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>? Order; public Action<AcceleratorStream, Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>? Order;
@@ -38,42 +38,37 @@ public sealed class KernelContainer
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2ModOrder; ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2ModOrder;
public Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>? SmallPrimeFactor; public Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>? SmallPrimeFactor;
public Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ushort, Stride1D.Dense>>? SpecialMax; public Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>? SpecialMax;
// Optional device buffer with small divisor cycles (<= 4M). Index = divisor, value = cycle length. // Optional device buffer with small divisor cycles (<= 4M). Index = divisor, value = cycle length.
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallCycles; public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallCycles;
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimeFactorsPrimes;
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimeFactorsSquares;
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimeFactorPrimeSlots;
public MemoryBuffer1D<int, Stride1D.Dense>? SmallPrimeFactorExponentSlots;
public MemoryBuffer1D<int, Stride1D.Dense>? SmallPrimeFactorCountSlot;
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimeFactorRemainingSlot;
public MemoryBuffer1D<ulong, Stride1D.Dense>? SpecialMaxFactors;
public MemoryBuffer1D<ulong, Stride1D.Dense>? SpecialMaxCandidates;
public MemoryBuffer1D<ushort, Stride1D.Dense>? SpecialMaxResult;
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastOne; public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastOne;
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2LastOne;
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastSeven; public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastSeven;
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2LastOne;
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2LastSeven; public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2LastSeven;
public static T InitOnce<T>(ref T? slot, Func<T> factory) where T : class public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimeFactorsPrimes;
{ public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimeFactorsSquares;
var current = Volatile.Read(ref slot);
if (current is not null)
{
return current;
}
current = Volatile.Read(ref slot); public void Dispose()
if (current is null)
{ {
current = factory(); // Order = null;
Volatile.Write(ref slot, current); // Incremental = null;
} // Pow2Mod = null;
return current; // IncrementalOrder = null;
} // Pow2ModOrder = null;
// SmallPrimeFactor = null;
// SpecialMax = null;
public void Dispose() => GpuKernelPool.Return(this); // SmallCycles?.Dispose();
// SmallPrimesLastOne?.Dispose();
// SmallPrimesLastSeven?.Dispose();
// SmallPrimesPow2LastOne?.Dispose();
// SmallPrimesPow2LastSeven?.Dispose();
// SmallPrimeFactorsPrimes?.Dispose();
// SmallPrimeFactorsSquares?.Dispose();
}
} }
public readonly struct ResiduePrimeViews( public readonly struct ResiduePrimeViews(
@@ -101,49 +96,30 @@ public readonly struct SmallPrimeFactorTables(
public ArrayView1D<ulong, Stride1D.Dense> SquaresView => Squares.View; public ArrayView1D<ulong, Stride1D.Dense> SquaresView => Squares.View;
} }
public readonly struct SmallPrimeFactorScratch(
MemoryBuffer1D<ulong, Stride1D.Dense> primeSlots,
MemoryBuffer1D<int, Stride1D.Dense> exponentSlots,
MemoryBuffer1D<int, Stride1D.Dense> countSlot,
MemoryBuffer1D<ulong, Stride1D.Dense> remainingSlot)
{
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> PrimeSlots = primeSlots;
public readonly MemoryBuffer1D<int, Stride1D.Dense> ExponentSlots = exponentSlots;
public readonly MemoryBuffer1D<int, Stride1D.Dense> CountSlot = countSlot;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> RemainingSlot = remainingSlot;
public void Clear()
{
PrimeSlots.MemSetToZero();
ExponentSlots.MemSetToZero();
CountSlot.MemSetToZero();
RemainingSlot.MemSetToZero();
}
}
public readonly struct SpecialMaxScratch(
MemoryBuffer1D<ulong, Stride1D.Dense> factorValues,
MemoryBuffer1D<ulong, Stride1D.Dense> candidateValues,
MemoryBuffer1D<ushort, Stride1D.Dense> resultSlot)
{
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> FactorValues = factorValues;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> CandidateValues = candidateValues;
public readonly MemoryBuffer1D<ushort, Stride1D.Dense> ResultSlot = resultSlot;
public ArrayView1D<ulong, Stride1D.Dense> FactorsView => FactorValues.View;
public ArrayView1D<ulong, Stride1D.Dense> CandidatesView => CandidateValues.View;
public ArrayView1D<ushort, Stride1D.Dense> ResultView => ResultSlot.View;
}
public class GpuKernelPool public class GpuKernelPool
{ {
private static readonly ConcurrentQueue<KernelContainer> KernelCache = new(); // TODO: Replace this concurrent map with a simple accelerator-indexed lookup once kernel launchers are prewarmed during startup so we can drop the thread-safe wrapper entirely. private static readonly ConcurrentDictionary<Accelerator, KernelContainer> _kernels = new();
public static KernelContainer GetKernels() => KernelCache.TryDequeue(out var container) public static T InitOnce<T>(ref T? target, Func<T> valueFactory) where T : class
? container {
: new KernelContainer(); if (target is { } value)
{
return value;
}
public static void Return(KernelContainer container) => KernelCache.Enqueue(container); var newValue = valueFactory();
return Interlocked.CompareExchange(ref target, newValue, null) ?? newValue;
}
internal static KernelContainer GetKernels(Accelerator accelerator)
{
return _kernels.GetOrAdd(accelerator, static (accelerator) =>
{
var kernels = new KernelContainer();
PreloadStaticTables(kernels, accelerator);
return kernels;
});
}
internal static void PreloadStaticTables(KernelContainer kernels, Accelerator accelerator) internal static void PreloadStaticTables(KernelContainer kernels, Accelerator accelerator)
{ {
@@ -215,10 +191,10 @@ public class GpuKernelPool
return new SmallPrimeFactorTables(primeBuffer, squareBuffer, (int)primeBuffer.Length); return new SmallPrimeFactorTables(primeBuffer, squareBuffer, (int)primeBuffer.Length);
} }
if (kernels.SmallPrimeFactorsPrimes is { } existingPrimes && kernels.SmallPrimeFactorsSquares is { } existingSquares) // if (kernels.SmallPrimeFactorsPrimes is { } existingPrimes && kernels.SmallPrimeFactorsSquares is { } existingSquares)
{ // {
return new SmallPrimeFactorTables(existingPrimes, existingSquares, (int)existingPrimes.Length); // return new SmallPrimeFactorTables(existingPrimes, existingSquares, (int)existingPrimes.Length);
} // }
var hostPrimes = PrimesGenerator.SmallPrimes; var hostPrimes = PrimesGenerator.SmallPrimes;
var hostSquares = PrimesGenerator.SmallPrimesPow2; var hostSquares = PrimesGenerator.SmallPrimesPow2;
@@ -234,74 +210,15 @@ public class GpuKernelPool
return new SmallPrimeFactorTables(devicePrimes, deviceSquares, hostPrimes.Length); return new SmallPrimeFactorTables(devicePrimes, deviceSquares, hostPrimes.Length);
} }
public static SmallPrimeFactorScratch EnsureSmallPrimeFactorScratch(KernelContainer kernels, Accelerator accelerator, int slotCount)
{
MemoryBuffer1D<ulong, Stride1D.Dense>? primeSlots = kernels.SmallPrimeFactorPrimeSlots;
if (primeSlots is null || primeSlots.Length < slotCount)
{
primeSlots?.Dispose();
primeSlots = accelerator.Allocate1D<ulong>(slotCount);
kernels.SmallPrimeFactorPrimeSlots = primeSlots;
}
MemoryBuffer1D<int, Stride1D.Dense>? exponentSlots = kernels.SmallPrimeFactorExponentSlots;
if (exponentSlots is null || exponentSlots.Length < slotCount)
{
exponentSlots?.Dispose();
exponentSlots = accelerator.Allocate1D<int>(slotCount);
kernels.SmallPrimeFactorExponentSlots = exponentSlots;
}
MemoryBuffer1D<int, Stride1D.Dense>? countSlot = kernels.SmallPrimeFactorCountSlot;
if (countSlot is null)
{
countSlot = accelerator.Allocate1D<int>(1);
kernels.SmallPrimeFactorCountSlot = countSlot;
}
MemoryBuffer1D<ulong, Stride1D.Dense>? remainingSlot = kernels.SmallPrimeFactorRemainingSlot;
if (remainingSlot is null)
{
remainingSlot = accelerator.Allocate1D<ulong>(1);
kernels.SmallPrimeFactorRemainingSlot = remainingSlot;
}
return new SmallPrimeFactorScratch(primeSlots!, exponentSlots!, countSlot!, remainingSlot!); public static GpuKernelLease GetKernel()
}
public static SpecialMaxScratch EnsureSpecialMaxScratch(KernelContainer kernels, Accelerator accelerator, int factorCapacity)
{
MemoryBuffer1D<ulong, Stride1D.Dense>? factorValues = kernels.SpecialMaxFactors;
if (factorValues is null || factorValues.Length < factorCapacity)
{
factorValues?.Dispose();
factorValues = accelerator.Allocate1D<ulong>(factorCapacity);
kernels.SpecialMaxFactors = factorValues;
}
MemoryBuffer1D<ulong, Stride1D.Dense>? candidateValues = kernels.SpecialMaxCandidates;
if (candidateValues is null || candidateValues.Length < factorCapacity)
{
candidateValues?.Dispose();
candidateValues = accelerator.Allocate1D<ulong>(factorCapacity);
kernels.SpecialMaxCandidates = candidateValues;
}
MemoryBuffer1D<ushort, Stride1D.Dense>? resultSlot = kernels.SpecialMaxResult;
if (resultSlot is null)
{
resultSlot = accelerator.Allocate1D<ushort>(1);
kernels.SpecialMaxResult = resultSlot;
}
return new SpecialMaxScratch(factorValues!, candidateValues!, resultSlot!);
}
public static GpuKernelLease GetKernel(bool useGpuOrder)
{ {
GpuPrimeWorkLimiter.Acquire(); GpuPrimeWorkLimiter.Acquire();
var gpu = Rent(); var gpu = Rent();
var kernels = GetKernels(); var kernels = GetKernels(gpu.Accelerator);
return GpuKernelLease.Rent(gpu, kernels); return GpuKernelLease.Rent(gpu, kernels);
} }
@@ -311,12 +228,10 @@ public class GpuKernelPool
/// <param name="action">Action to run with (Accelerator, Stream).</param> /// <param name="action">Action to run with (Accelerator, Stream).</param>
public static void Run(Action<Accelerator, AcceleratorStream> action) public static void Run(Action<Accelerator, AcceleratorStream> action)
{ {
var lease = GetKernel(useGpuOrder: true); var lease = GetKernel();
var accelerator = lease.Accelerator; var accelerator = lease.Accelerator;
var stream = accelerator.CreateStream(); var stream = lease.Stream;
action(accelerator, stream); action(accelerator, stream);
stream.Dispose();
lease.Dispose(); lease.Dispose();
} }
} }
@@ -0,0 +1,134 @@
using System.Collections.Concurrent;
using ILGPU;
using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu;
public sealed class ScratchBuffer
{
public MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimeFactorPrimeSlots { get; private set; }
public MemoryBuffer1D<int, Stride1D.Dense> SmallPrimeFactorExponentSlots { get; private set; }
public MemoryBuffer1D<int, Stride1D.Dense> SmallPrimeFactorCountSlot { get; private set; }
public MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimeFactorRemainingSlot { get; private set; }
public MemoryBuffer1D<ulong, Stride1D.Dense> SpecialMaxFactors { get; private set; }
public MemoryBuffer1D<ulong, Stride1D.Dense> SpecialMaxCandidates { get; private set; }
public MemoryBuffer1D<ulong, Stride1D.Dense> SpecialMaxResult { get; private set; }
internal readonly Accelerator _accelerator;
public ScratchBuffer(Accelerator accelerator, int smallPrimeFactorSlotCount, int specialMaxFactorCapacity)
{
_accelerator = accelerator;
// lock (accelerator)
// {
SmallPrimeFactorPrimeSlots = accelerator.Allocate1D<ulong>(smallPrimeFactorSlotCount);
SmallPrimeFactorExponentSlots = accelerator.Allocate1D<int>(smallPrimeFactorSlotCount);
SmallPrimeFactorCountSlot = accelerator.Allocate1D<int>(1);
SmallPrimeFactorRemainingSlot = accelerator.Allocate1D<ulong>(1);
SpecialMaxFactors = accelerator.Allocate1D<ulong>(specialMaxFactorCapacity);
SpecialMaxCandidates = accelerator.Allocate1D<ulong>(specialMaxFactorCapacity);
SpecialMaxResult = accelerator.Allocate1D<ulong>(1);
// }
}
public void ResizeSmallPrimeFactorSlots(int newSize)
{
if (SmallPrimeFactorPrimeSlots.Length < newSize)
{
SmallPrimeFactorPrimeSlots.Dispose();
SmallPrimeFactorExponentSlots.Dispose();
SmallPrimeFactorPrimeSlots = _accelerator.Allocate1D<ulong>(newSize);
SmallPrimeFactorExponentSlots = _accelerator.Allocate1D<int>(newSize);
}
// else
// {
// SmallPrimeFactorPrimeSlots.MemSetToZero();
// SmallPrimeFactorExponentSlots.MemSetToZero();
// }
}
public void ResizeSpecialMaxFactors(int newSize)
{
if (SpecialMaxFactors.Length < newSize)
{
SpecialMaxFactors.Dispose();
SpecialMaxCandidates.Dispose();
SpecialMaxFactors = _accelerator.Allocate1D<ulong>(newSize);
SpecialMaxCandidates = _accelerator.Allocate1D<ulong>(newSize);
}
// else
// {
// SpecialMaxFactors.MemSetToZero();
// SpecialMaxCandidates.MemSetToZero();
// }
}
}
public static class GpuScratchBufferPool
{
[ThreadStatic]
private static Dictionary<Accelerator, Queue<ScratchBuffer>>? _pools;
// private static readonly ConcurrentDictionary<Accelerator, ConcurrentQueue<ScratchBuffer>> _pools = new();
private const int DefaultSmallPrimeFactorSlotCount = 64; // From PrimeOrderCalculator.Gpu.cs
private const int DefaultSpecialMaxFactorCapacity = 1024; // A reasonable default, will be resized if needed
public static ScratchBuffer Rent(Accelerator accelerator, int smallPrimeFactorSlotCount, int specialMaxFactorCapacity)
{
const int WarmUpCapacity = 100;
// var pool = _pools.GetOrAdd(accelerator, _ => new ConcurrentQueue<ScratchBuffer>());
if (_pools is not { })
{
_pools = new(WarmUpCapacity);
WarmUp(WarmUpCapacity, DefaultSmallPrimeFactorSlotCount, DefaultSpecialMaxFactorCapacity);
}
var pools = _pools;
if (!pools.TryGetValue(accelerator, out var pool))
{
pool = new();
pools[accelerator] = pool;
}
// var pool = _pools.GetOrAdd(accelerator, _ => new ConcurrentQueue<ScratchBuffer>());
if (pool.TryDequeue(out var buffer))
{
if (buffer.SmallPrimeFactorPrimeSlots.Length < smallPrimeFactorSlotCount || buffer.SpecialMaxFactors.Length < specialMaxFactorCapacity)
{
throw new InvalidOperationException();
// lock (accelerator)
// {
// Console.WriteLine($"Resizing GPU scratch buffer from pool ({buffer.SmallPrimeFactorPrimeSlots.Length} / {smallPrimeFactorSlotCount}), ({buffer.SpecialMaxFactors.Length}/{specialMaxFactorCapacity})");
// buffer.ResizeSmallPrimeFactorSlots(smallPrimeFactorSlotCount);
// buffer.ResizeSpecialMaxFactors(specialMaxFactorCapacity);
// }
}
return buffer;
}
return new ScratchBuffer(accelerator, smallPrimeFactorSlotCount, specialMaxFactorCapacity);
}
public static void Return(ScratchBuffer buffer)
{
if (_pools!.TryGetValue(buffer._accelerator, out var pool))
{
pool.Enqueue(buffer);
}
}
public static void WarmUp(int count, int smallPrimeFactorSlots, int specialMaxFactorCapacity)
{
Accelerator accelerator = SharedGpuContext.Accelerator;
for (int i = 0; i < count; i++)
{
var buffer = Rent(accelerator, smallPrimeFactorSlots, specialMaxFactorCapacity);
Return(buffer);
}
}
}
@@ -1,6 +1,5 @@
using ILGPU; using ILGPU;
using ILGPU.Runtime; using ILGPU.Runtime;
using PerfectNumbers.Core;
namespace PerfectNumbers.Core.Gpu; namespace PerfectNumbers.Core.Gpu;
@@ -13,7 +12,7 @@ internal static partial class PrimeOrderGpuHeuristics
int factorCount, int factorCount,
MontgomeryDivisorData divisor, MontgomeryDivisorData divisor,
ArrayView1D<ulong, Stride1D.Dense> candidates, ArrayView1D<ulong, Stride1D.Dense> candidates,
ArrayView1D<ushort, Stride1D.Dense> resultOut) ArrayView1D<ulong, Stride1D.Dense> resultOut)
{ {
if (index != 0) if (index != 0)
{ {
@@ -62,7 +61,7 @@ internal static partial class PrimeOrderGpuHeuristics
var stepper = new ExponentRemainderStepperGpu(divisorPartial); var stepper = new ExponentRemainderStepperGpu(divisorPartial);
if (stepper.InitializeIsUnityGpu(candidates[0])) if (stepper.InitializeIsUnityGpu(candidates[0]))
{ {
resultOut[0] = 0; Atomic.Exchange(ref resultOut[0], 0);
return; return;
} }
@@ -70,11 +69,11 @@ internal static partial class PrimeOrderGpuHeuristics
{ {
if (stepper.ComputeNextIsUnityGpu(candidates[i])) if (stepper.ComputeNextIsUnityGpu(candidates[i]))
{ {
resultOut[0] = 0; Atomic.Exchange(ref resultOut[0], 0);
return; return;
} }
} }
resultOut[0] = 1; Atomic.Exchange(ref resultOut[0], 1);
} }
} }
@@ -1,3 +1,4 @@
using PerfectNumbers.Core.Gpu;
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@@ -25,7 +26,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>> _kernelCache = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>> _kernelCache = new(AcceleratorReferenceComparer.Instance);
private readonly ConcurrentBag<BatchResources> _resourcePool = new(); private readonly ConcurrentBag<BatchResources> _resourcePool = new();
private readonly ConcurrentBag<GpuContextPool.GpuContextLease> _acceleratorPool = new(); private readonly ConcurrentBag<GpuContextLease> _acceleratorPool = new();
private readonly ConcurrentBag<DivisorScanSession> _sessionPool = new(); private readonly ConcurrentBag<DivisorScanSession> _sessionPool = new();
private Action<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> GetKernel(Accelerator accelerator) => private Action<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> GetKernel(Accelerator accelerator) =>
@@ -476,7 +477,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
public sealed class DivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession public sealed class DivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
{ {
private readonly MersenneNumberDivisorByDivisorGpuTester _owner; private readonly MersenneNumberDivisorByDivisorGpuTester _owner;
private readonly GpuContextPool.GpuContextLease _lease; private readonly GpuContextLease _lease;
private readonly Accelerator _accelerator; private readonly Accelerator _accelerator;
private Action<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> _kernel = null!; private Action<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> _kernel = null!;
private MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense> _divisorBuffer = null!; private MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense> _divisorBuffer = null!;
@@ -650,7 +651,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
} }
} }
private GpuContextPool.GpuContextLease RentAccelerator() private GpuContextLease RentAccelerator()
{ {
if (_acceleratorPool.TryTake(out var lease)) if (_acceleratorPool.TryTake(out var lease))
{ {
@@ -660,7 +661,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
return GpuContextPool.Rent(); return GpuContextPool.Rent();
} }
private void ReturnAccelerator(GpuContextPool.GpuContextLease lease) => _acceleratorPool.Add(lease); private void ReturnAccelerator(GpuContextLease lease) => _acceleratorPool.Add(lease);
private BatchResources RentBatchResources(Accelerator accelerator, int capacity) private BatchResources RentBatchResources(Accelerator accelerator, int capacity)
{ {
@@ -900,7 +901,3 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
IMersenneNumberDivisorByDivisorTester.IDivisorScanSession IMersenneNumberDivisorByDivisorTester.CreateDivisorSession() => CreateDivisorSession(); IMersenneNumberDivisorByDivisorTester.IDivisorScanSession IMersenneNumberDivisorByDivisorTester.CreateDivisorSession() => CreateDivisorSession();
} }
@@ -39,18 +39,20 @@ public sealed class MersenneNumberDivisorGpuTester
{ {
var gpu = GpuContextPool.Rent(); var gpu = GpuContextPool.Rent();
var accelerator = gpu.Accelerator; var accelerator = gpu.Accelerator;
var stream = accelerator.CreateStream();
var kernel = GetKernel(accelerator); var kernel = GetKernel(accelerator);
var resultBuffer = _resultBuffers.GetOrAdd(accelerator, acc => acc.Allocate1D<byte>(1)); 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] // There is no point in clearing this buffer. We always override item [0] and never use it beyond item [0]
// resultBuffer.MemSetToZero(); // resultBuffer.MemSetToZero();
kernel(1, exponent, divisor, resultBuffer.View); kernel(1, exponent, divisor, resultBuffer.View);
accelerator.Synchronize(); stream.Synchronize();
Span<byte> result = stackalloc byte[1]; Span<byte> result = stackalloc byte[1];
resultBuffer.View.CopyToCPU(ref result[0], 1); resultBuffer.View.CopyToCPU(stream, ref result[0], 1);
bool divisible = result[0] != 0; bool divisible = result[0] != 0;
result[0] = 0; result[0] = 0;
resultBuffer.View.CopyFromCPU(ref result[0], 1); resultBuffer.View.CopyFromCPU(stream, ref result[0], 1);
accelerator.Synchronize(); stream.Synchronize();
stream.Dispose();
gpu.Dispose(); gpu.Dispose();
return divisible; return divisible;
} }
@@ -16,7 +16,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})."); throw new NotImplementedException($"GPU incremental scanning requires the device cycle heuristics implementation (kernel: {_kernelType}, GPU order: {_useGpuOrder}).");
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder); var gpuLease = GpuKernelPool.GetKernel();
var accelerator = gpuLease.Accelerator; var accelerator = gpuLease.Accelerator;
var stream = gpuLease.Stream; var stream = gpuLease.Stream;
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy
@@ -9,7 +9,7 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime) public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
{ {
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder); var gpuLease = GpuKernelPool.GetKernel();
var accelerator = gpuLease.Accelerator; var accelerator = gpuLease.Accelerator;
var stream = gpuLease.Stream; var stream = gpuLease.Stream;
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR
@@ -1,3 +1,4 @@
using PerfectNumbers.Core.Gpu;
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@@ -13,7 +14,7 @@ namespace PerfectNumbers.Core.Gpu;
public class MersenneNumberResidueGpuTester(bool useGpuOrder) public class MersenneNumberResidueGpuTester(bool useGpuOrder)
{ {
private readonly bool _useGpuOrder = useGpuOrder; private readonly bool _useGpuOrder = useGpuOrder;
private readonly ConcurrentBag<GpuContextPool.GpuContextLease> _acceleratorPool = new(); 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, 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); private readonly ConcurrentDictionary<Accelerator, ConcurrentBag<ResidueResources>> _resourcePools = new(AcceleratorReferenceComparer.Instance);
@@ -35,7 +36,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
var stream = accelerator.CreateStream(); var stream = accelerator.CreateStream();
var kernel = GetPow2ModKernel(accelerator); var kernel = GetPow2ModKernel(accelerator);
var resources = RentResources(accelerator, GpuConstants.ScanBatchSize); var resources = RentResources(accelerator, GpuConstants.ScanBatchSize);
var gpuKernels = GpuKernelPool.GetKernels(); var gpuKernels = GpuKernelPool.GetKernels(accelerator);
var orderBuffer = resources.OrderBuffer; var orderBuffer = resources.OrderBuffer;
ulong[] orderArray = resources.OrderArray; ulong[] orderArray = resources.OrderArray;
@@ -70,6 +71,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
kernelArgs, orderBuffer.View, smallCyclesView, primeViews.LastOne, primeViews.LastSeven, primeViews.LastOnePow2, primeViews.LastSevenPow2); kernelArgs, orderBuffer.View, smallCyclesView, primeViews.LastOne, primeViews.LastSeven, primeViews.LastOnePow2, primeViews.LastSevenPow2);
stream.Synchronize(); stream.Synchronize();
orderBuffer.View.CopyToCPU(ref ordersRef, currentSize); orderBuffer.View.CopyToCPU(ref ordersRef, currentSize);
if (!Volatile.Read(ref isPrime)) if (!Volatile.Read(ref isPrime))
{ {
@@ -127,7 +129,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
loaded.Method); loaded.Method);
}); });
private GpuContextPool.GpuContextLease RentAccelerator() private GpuContextLease RentAccelerator()
{ {
if (_acceleratorPool.TryTake(out var lease)) if (_acceleratorPool.TryTake(out var lease))
{ {
@@ -137,11 +139,11 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
return GpuContextPool.Rent(); return GpuContextPool.Rent();
} }
private void ReturnAccelerator(GpuContextPool.GpuContextLease lease) => _acceleratorPool.Add(lease); private void ReturnAccelerator(GpuContextLease lease) => _acceleratorPool.Add(lease);
private ResidueResources RentResources(Accelerator accelerator, int capacity) private ResidueResources RentResources(Accelerator accelerator, int capacity)
{ {
var bag = _resourcePools.GetOrAdd(accelerator, static _ => new ConcurrentBag<ResidueResources>()); var bag = _resourcePools.GetOrAdd(accelerator, static _ => []);
while (bag.TryTake(out var resources)) while (bag.TryTake(out var resources))
{ {
if (resources.Capacity >= capacity) if (resources.Capacity >= capacity)
@@ -198,7 +198,7 @@ internal static partial class PrimeOrderGpuHeuristics
try try
{ {
var lease = GpuKernelPool.GetKernel(useGpuOrder: true); var lease = GpuKernelPool.GetKernel();
Accelerator accelerator = lease.Accelerator; Accelerator accelerator = lease.Accelerator;
AcceleratorStream stream = lease.Stream; AcceleratorStream stream = lease.Stream;
@@ -364,7 +364,7 @@ internal static partial class PrimeOrderGpuHeuristics
{ {
order = 0UL; order = 0UL;
var lease = GpuKernelPool.GetKernel(useGpuOrder: true); var lease = GpuKernelPool.GetKernel();
Accelerator accelerator = lease.Accelerator; Accelerator accelerator = lease.Accelerator;
AcceleratorStream stream = lease.Stream; AcceleratorStream stream = lease.Stream;
@@ -526,7 +526,7 @@ internal static partial class PrimeOrderGpuHeuristics
private static bool TryComputeOnGpu(ReadOnlySpan<ulong> exponents, ulong prime, in MontgomeryDivisorData divisorData, Span<ulong> results) private static bool TryComputeOnGpu(ReadOnlySpan<ulong> exponents, ulong prime, in MontgomeryDivisorData divisorData, Span<ulong> results)
{ {
var lease = GpuKernelPool.GetKernel(useGpuOrder: true); var lease = GpuKernelPool.GetKernel();
Accelerator accelerator = lease.Accelerator; Accelerator accelerator = lease.Accelerator;
AcceleratorStream stream = lease.Stream; AcceleratorStream stream = lease.Stream;
var kernel = GetPow2ModKernel(accelerator); var kernel = GetPow2ModKernel(accelerator);
@@ -581,7 +581,7 @@ internal static partial class PrimeOrderGpuHeuristics
GpuUInt128[]? rentedExponents = null; GpuUInt128[]? rentedExponents = null;
GpuUInt128[]? rentedResults = null; GpuUInt128[]? rentedResults = null;
var lease = GpuKernelPool.GetKernel(useGpuOrder: true); var lease = GpuKernelPool.GetKernel();
try try
{ {
+1 -1
View File
@@ -5,7 +5,7 @@ using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu; namespace PerfectNumbers.Core.Gpu;
internal static class SharedGpuContext public static class SharedGpuContext
{ {
private static readonly Lazy<Context> s_context = new(() => ILGPU.Context.CreateDefault(), LazyThreadSafetyMode.ExecutionAndPublication); private static readonly Lazy<Context> s_context = new(() => ILGPU.Context.CreateDefault(), LazyThreadSafetyMode.ExecutionAndPublication);
private static readonly Lazy<Device> s_device = new(() => s_context.Value.GetPreferredDevice(false), LazyThreadSafetyMode.ExecutionAndPublication); private static readonly Lazy<Device> s_device = new(() => s_context.Value.GetPreferredDevice(false), LazyThreadSafetyMode.ExecutionAndPublication);
@@ -2,6 +2,7 @@ using Open.Numeric.Primes;
using ILGPU.Runtime; using ILGPU.Runtime;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
using System.Runtime.InteropServices;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
@@ -227,6 +228,7 @@ public sealed class HeuristicCombinedPrimeTester
GpuPrimeWorkLimiter.Acquire(); GpuPrimeWorkLimiter.Acquire();
var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(1); var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(1);
var accelerator = gpu.Accelerator; var accelerator = gpu.Accelerator;
var stream = gpu.Stream;
var kernel = PrimeTester.PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.HeuristicTrialDivisionKernel; var kernel = PrimeTester.PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.HeuristicTrialDivisionKernel;
var flagView1D = gpu.HeuristicFlag.View; var flagView1D = gpu.HeuristicFlag.View;
var flagView = flagView1D.AsContiguous(); var flagView = flagView1D.AsContiguous();
@@ -243,8 +245,9 @@ public sealed class HeuristicCombinedPrimeTester
maxDivisorSquare, maxDivisorSquare,
HeuristicGpuDivisorTableKind.Combined, HeuristicGpuDivisorTableKind.Combined,
gpu.HeuristicGpuTables); gpu.HeuristicGpuTables);
accelerator.Synchronize(); stream.Synchronize();
flagView1D.CopyToCPU(ref compositeFlag, 1);
flagView1D.CopyToCPU(stream, ref compositeFlag, 1);
compositeDetected = compositeFlag != 0; compositeDetected = compositeFlag != 0;
gpu.Dispose(); gpu.Dispose();
+4 -2
View File
@@ -243,6 +243,7 @@ public sealed class HeuristicPrimeTester
GpuPrimeWorkLimiter.Acquire(); GpuPrimeWorkLimiter.Acquire();
var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(1); var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(1);
var accelerator = gpu.Accelerator; var accelerator = gpu.Accelerator;
var stream = gpu.Stream;
var kernel = PrimeTester.PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.HeuristicTrialDivisionKernel; var kernel = PrimeTester.PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.HeuristicTrialDivisionKernel;
var flagView1D = gpu.HeuristicFlag.View; var flagView1D = gpu.HeuristicFlag.View;
var flagView = flagView1D.AsContiguous(); var flagView = flagView1D.AsContiguous();
@@ -260,8 +261,9 @@ public sealed class HeuristicPrimeTester
maxDivisorSquare, maxDivisorSquare,
HeuristicGpuDivisorTableKind.GroupA, HeuristicGpuDivisorTableKind.GroupA,
gpu.HeuristicGpuTables); gpu.HeuristicGpuTables);
accelerator.Synchronize(); stream.Synchronize();
flagView1D.CopyToCPU(ref compositeFlag, 1);
flagView1D.CopyToCPU(stream, ref compositeFlag, 1);
compositeDetected = compositeFlag != 0; compositeDetected = compositeFlag != 0;
if (!compositeDetected) if (!compositeDetected)
+10 -19
View File
@@ -736,11 +736,10 @@ public class MersenneDivisorCycles
tasks[taskIndex] = Task.Run(() => tasks[taskIndex] = Task.Run(() =>
{ {
ulong rangeLength = localEnd - localStart + 1UL; ulong rangeLength = localEnd - localStart + 1UL;
(ulong divisor, ulong cycle)[] localCycles = ArrayPool<(ulong, ulong)>.Shared.Rent(checked((int)rangeLength)); (ulong divisor, ulong cycle)[] localCycles = ThreadStaticPools.RentUlongUlongArray(checked((int)rangeLength));
// (ulong divisor, ulong cycle)[] localCycles = ArrayPool<(ulong, ulong)>.Shared.Rent(checked((int)rangeLength));
int localCycleIndex = 0; int localCycleIndex = 0;
try
{
for (ulong divisor = localStart; divisor <= localEnd; divisor++) for (ulong divisor = localStart; divisor <= localEnd; divisor++)
{ {
if ((divisor & 1UL) == 0UL) if ((divisor & 1UL) == 0UL)
@@ -766,6 +765,7 @@ public class MersenneDivisorCycles
if (localCycleIndex == 0) if (localCycleIndex == 0)
{ {
ThreadStaticPools.Return(localCycles);
return; return;
} }
@@ -781,11 +781,8 @@ public class MersenneDivisorCycles
writer.Flush(); writer.Flush();
} }
}
finally ThreadStaticPools.Return(localCycles);
{
ArrayPool<(ulong, ulong)>.Shared.Return(localCycles, clearArray: false);
}
}); });
} }
@@ -806,9 +803,9 @@ public class MersenneDivisorCycles
divisors = pool.Rent(count); divisors = pool.Rent(count);
outCycles = pool.Rent(count); outCycles = pool.Rent(count);
var lease = GpuKernelPool.GetKernel(useGpuOrder: true); var lease = GpuKernelPool.GetKernel();
var accelerator = lease.Accelerator; var accelerator = lease.Accelerator;
var stream = accelerator.CreateStream(); var stream = lease.Stream;
var kernel = accelerator.LoadAutoGroupedStreamKernel< var kernel = accelerator.LoadAutoGroupedStreamKernel<
Index1D, Index1D,
ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>,
@@ -827,8 +824,6 @@ public class MersenneDivisorCycles
} }
using var writer = new BinaryWriter(outputStream, Encoding.UTF8, leaveOpen: false); using var writer = new BinaryWriter(outputStream, Encoding.UTF8, leaveOpen: false);
try
{
while (skipCount > 0L && start <= maxDivisor) while (skipCount > 0L && start <= maxDivisor)
{ {
// TODO: Swap these modulo checks for the cached Mod3/Mod5/Mod7/Mod11 helpers so // TODO: Swap these modulo checks for the cached Mod3/Mod5/Mod7/Mod11 helpers so
@@ -870,9 +865,9 @@ public class MersenneDivisorCycles
bufferDiv.View, bufferDiv.View,
bufferCycle.View); bufferCycle.View);
accelerator.Synchronize(); stream.Synchronize();
bufferCycle.View.CopyToCPU(ref outCycles[0], idx); bufferCycle.View.CopyToCPU(stream, ref outCycles[0], idx);
bufferCycle.Dispose(); bufferCycle.Dispose();
bufferDiv.Dispose(); bufferDiv.Dispose();
@@ -894,16 +889,12 @@ public class MersenneDivisorCycles
Console.WriteLine($"...processed divisor = {d}"); Console.WriteLine($"...processed divisor = {d}");
start = end + 1UL; start = end + 1UL;
} }
}
finally
{
pool.Return(divisors, clearArray: false); pool.Return(divisors, clearArray: false);
pool.Return(outCycles, clearArray: false); pool.Return(outCycles, clearArray: false);
}
stream.Dispose(); stream.Dispose();
lease.Dispose(); lease.Dispose();
} }
+1 -1
View File
@@ -146,7 +146,7 @@ public sealed class MersenneNumberTester(
UInt128[] qs = qsBuffer; UInt128[] qs = qsBuffer;
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder); var gpuLease = GpuKernelPool.GetKernel();
var accelerator = gpuLease.Accelerator; var accelerator = gpuLease.Accelerator;
var stream = gpuLease.Stream; var stream = gpuLease.Stream;
var orderKernel = gpuLease.OrderKernel!; var orderKernel = gpuLease.OrderKernel!;
+49 -31
View File
@@ -1,8 +1,6 @@
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using ILGPU; using ILGPU;
using ILGPU.Runtime; using ILGPU.Runtime;
using ILGPU.Runtime.OpenCL;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
@@ -21,40 +19,53 @@ internal static partial class PrimeOrderCalculator
Span<int> exponentBuffer = exponentBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots); Span<int> exponentBuffer = exponentBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots);
remaining = value; remaining = value;
var lease = GpuKernelPool.GetKernel(useGpuOrder: true); var lease = GpuKernelPool.GetKernel();
Accelerator accelerator = lease.Accelerator; Accelerator accelerator = lease.Accelerator;
AcceleratorStream stream = lease.Stream; var stream = lease.Stream;
KernelContainer kernels = lease.Kernels;
SmallPrimeFactorTables tables = GpuKernelPool.EnsureSmallPrimeFactorTables(lease.Kernels, accelerator); ScratchBuffer scratch = GpuScratchBufferPool.Rent(accelerator, GpuSmallPrimeFactorSlots, 0);
SmallPrimeFactorScratch scratch = GpuKernelPool.EnsureSmallPrimeFactorScratch(lease.Kernels, accelerator, GpuSmallPrimeFactorSlots);
SmallPrimeFactorTables tables = GpuKernelPool.EnsureSmallPrimeFactorTables(kernels, accelerator);
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");
var kernel = lease.SmallPrimeFactorKernel; var kernel = lease.SmallPrimeFactorKernel;
ArrayView1D<int, Stride1D.Dense> smallPrimeFactorCountSlotView = scratch.SmallPrimeFactorCountSlot.View;
ArrayView1D<ulong, Stride1D.Dense> smallPrimeFactorRemainingSlotView = scratch.SmallPrimeFactorRemainingSlot.View;
ArrayView1D<ulong, Stride1D.Dense> smallPrimeFactorPrimeSlotsView = scratch.SmallPrimeFactorPrimeSlots.View;
ArrayView1D<int, Stride1D.Dense> smallPrimeFactorExponentSlotsView = scratch.SmallPrimeFactorExponentSlots.View;
ArrayView1D<uint, Stride1D.Dense> smallPrimeFactorsPrimesView = kernels.SmallPrimeFactorsPrimes!.View;
kernel( kernel(
stream, stream,
1, 1,
value, value,
limit, limit,
tables.PrimesView, smallPrimeFactorsPrimesView,
tables.SquaresView, kernels.SmallPrimeFactorsSquares!.View,
tables.Count, (int)smallPrimeFactorsPrimesView.Length,
scratch.PrimeSlots.View, smallPrimeFactorPrimeSlotsView,
scratch.ExponentSlots.View, smallPrimeFactorExponentSlotsView,
scratch.CountSlot.View, smallPrimeFactorCountSlotView,
scratch.RemainingSlot.View); smallPrimeFactorRemainingSlotView);
stream.Synchronize(); stream.Synchronize();
factorCount = 0; factorCount = 0;
scratch.CountSlot.View.CopyToCPU(ref factorCount, 1); smallPrimeFactorCountSlotView.CopyToCPU(stream, ref factorCount, 1);
factorCount = Math.Min(factorCount, GpuSmallPrimeFactorSlots); factorCount = Math.Min(factorCount, GpuSmallPrimeFactorSlots);
if (factorCount > 0) if (factorCount > 0)
{ {
scratch.PrimeSlots.View.CopyToCPU(ref MemoryMarshal.GetReference(primeBuffer), factorCount); smallPrimeFactorPrimeSlotsView.CopyToCPU(stream, ref MemoryMarshal.GetReference(primeBuffer), factorCount);
scratch.ExponentSlots.View.CopyToCPU(ref MemoryMarshal.GetReference(exponentBuffer), factorCount);
smallPrimeFactorExponentSlotsView.CopyToCPU(stream, ref MemoryMarshal.GetReference(exponentBuffer), factorCount);
} }
scratch.RemainingSlot.View.CopyToCPU(ref remaining, 1); smallPrimeFactorRemainingSlotView.CopyToCPU(stream, ref remaining, 1);
for (int i = 0; i < factorCount; i++) for (int i = 0; i < factorCount; i++)
{ {
@@ -63,9 +74,8 @@ internal static partial class PrimeOrderCalculator
counts.Add(primeValue, exponent); counts.Add(primeValue, exponent);
} }
GpuScratchBufferPool.Return(scratch);
lease.Dispose(); lease.Dispose();
ThreadStaticPools.UlongPool.Return(primeBufferArray);
ThreadStaticPools.IntPool.Return(exponentBufferArray);
return true; return true;
} }
@@ -88,32 +98,40 @@ internal static partial class PrimeOrderCalculator
factorSpan[i] = factors[i].Value; factorSpan[i] = factors[i].Value;
} }
var lease = GpuKernelPool.GetKernel(useGpuOrder: true); GpuKernelLease lease = GpuKernelPool.GetKernel();
Accelerator accelerator = lease.Accelerator; Accelerator accelerator = lease.Accelerator;
AcceleratorStream stream = lease.Stream;
SpecialMaxScratch scratch = GpuKernelPool.EnsureSpecialMaxScratch(lease.Kernels, accelerator, factorCapacity: factorCount); var stream = lease.Stream;
scratch.FactorsView.SubView(0, factorCount).CopyFromCPU(ref MemoryMarshal.GetReference(factorSpan), factorCount); ScratchBuffer scratch = GpuScratchBufferPool.Rent(accelerator, 0, factorCount);
ArrayView1D<ulong, Stride1D.Dense> specialMaxFactorsView = scratch.SpecialMaxFactors.View;
specialMaxFactorsView.SubView(0, factorCount).CopyFromCPU(stream, ref MemoryMarshal.GetReference(factorSpan), factorCount);
var kernel = lease.SpecialMaxKernel; var kernel = lease.SpecialMaxKernel;
ArrayView1D<ulong, Stride1D.Dense> specialMaxResultView = scratch.SpecialMaxResult.View;
kernel( kernel(
stream, stream,
1, 1,
phi, phi,
scratch.FactorsView, specialMaxFactorsView,
factorCount, factorCount,
divisorData, divisorData,
scratch.CandidatesView, scratch.SpecialMaxCandidates.View,
scratch.ResultView); specialMaxResultView);
stream.Synchronize(); stream.Synchronize();
ushort result = 0; Span<ulong> result = stackalloc ulong[1];
scratch.ResultView.CopyToCPU(ref result, 1); // specialMaxResultView.CopyToCPU(stream, ref result, 1);
lease.Dispose(); specialMaxResultView.CopyToCPU(stream, in result);
return result != 0; // stream.Synchronize();
// return result != 0;
GpuScratchBufferPool.Return(scratch);
lease.Dispose();
return result[0] != 0;
} }
} }
+37 -11
View File
@@ -98,6 +98,7 @@ public sealed class PrimeTester
GpuPrimeWorkLimiter.Acquire(); GpuPrimeWorkLimiter.Acquire();
var gpu = PrimeTesterGpuContextPool.Rent(1); var gpu = PrimeTesterGpuContextPool.Rent(1);
var accelerator = gpu.Accelerator; var accelerator = gpu.Accelerator;
var stream = gpu.Stream;
var kernel = PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.Kernel; var kernel = PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.Kernel;
ulong value = n; ulong value = n;
@@ -108,7 +109,7 @@ public sealed class PrimeTester
var inputView = input.View; var inputView = input.View;
var outputView = output.View; var outputView = output.View;
inputView.CopyFromCPU(ref value, 1); inputView.CopyFromCPU(stream, ref value, 1);
kernel( kernel(
1, 1,
inputView, inputView,
@@ -123,8 +124,8 @@ public sealed class PrimeTester
gpu.DevicePrimesPow2LastThree.View, gpu.DevicePrimesPow2LastThree.View,
gpu.DevicePrimesPow2LastNine.View, gpu.DevicePrimesPow2LastNine.View,
outputView); outputView);
accelerator.Synchronize(); stream.Synchronize();
outputView.CopyToCPU(ref flag, 1); outputView.CopyToCPU(stream, ref flag, 1);
gpu.Dispose(); gpu.Dispose();
GpuPrimeWorkLimiter.Release(); GpuPrimeWorkLimiter.Release();
@@ -163,6 +164,7 @@ public sealed class PrimeTester
GpuPrimeWorkLimiter.Acquire(); GpuPrimeWorkLimiter.Acquire();
var gpu = PrimeTesterGpuContextPool.Rent(GpuBatchSize); var gpu = PrimeTesterGpuContextPool.Rent(GpuBatchSize);
var accelerator = gpu.Accelerator; var accelerator = gpu.Accelerator;
var stream = gpu.Stream;
var kernel = PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.Kernel; var kernel = PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.Kernel;
int totalLength = values.Length; int totalLength = values.Length;
int batchSize = GpuBatchSize; int batchSize = GpuBatchSize;
@@ -196,10 +198,11 @@ public sealed class PrimeTester
gpu.DevicePrimesPow2LastThree.View, gpu.DevicePrimesPow2LastThree.View,
gpu.DevicePrimesPow2LastNine.View, gpu.DevicePrimesPow2LastNine.View,
outputView); outputView);
accelerator.Synchronize(); stream.Synchronize();
var resultSlice = results.Slice(pos, count); var resultSlice = results.Slice(pos, count);
ref byte resultRef = ref MemoryMarshal.GetReference(resultSlice); ref byte resultRef = ref MemoryMarshal.GetReference(resultSlice);
outputView.CopyToCPU(ref resultRef, count); outputView.CopyToCPU(stream, ref resultRef, count);
pos += count; pos += count;
} }
@@ -266,8 +269,9 @@ public sealed class PrimeTester
return; return;
} }
KernelContainer kernels = GpuKernelPool.GetKernels(); Accelerator accelerator = SharedGpuContext.Accelerator;
GpuStaticTableInitializer.EnsureStaticTables(kernels, SharedGpuContext.Accelerator); KernelContainer kernels = GpuKernelPool.GetKernels(accelerator);
GpuStaticTableInitializer.EnsureStaticTables(kernels, accelerator);
int toCreate = target - WarmedLeaseCount; int toCreate = target - WarmedLeaseCount;
for (int i = 0; i < toCreate; i++) for (int i = 0; i < toCreate; i++)
@@ -275,7 +279,6 @@ public sealed class PrimeTester
Pool.Enqueue(new PrimeTesterGpuContextLease(minBufferCapacity)); Pool.Enqueue(new PrimeTesterGpuContextLease(minBufferCapacity));
} }
kernels.Dispose();
WarmedLeaseCount = target; WarmedLeaseCount = target;
} }
} }
@@ -379,6 +382,21 @@ public sealed class PrimeTester
internal readonly Context AcceleratorContext; internal readonly Context AcceleratorContext;
public readonly Accelerator Accelerator; public readonly Accelerator Accelerator;
private AcceleratorStream? _stream;
public AcceleratorStream Stream
{
get
{
if (_stream is { } stream)
{
return stream;
}
stream = Accelerator.CreateStream();
_stream = stream;
return stream;
}
}
public static readonly Action<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> Kernel = SharedGpuContext.Accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel); public static readonly Action<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> Kernel = SharedGpuContext.Accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel);
@@ -445,6 +463,7 @@ public sealed class PrimeTester
Input?.Dispose(); Input?.Dispose();
Output?.Dispose(); Output?.Dispose();
HeuristicFlag?.Dispose(); HeuristicFlag?.Dispose();
Input = Accelerator.Allocate1D<ulong>(minCapacity); Input = Accelerator.Allocate1D<ulong>(minCapacity);
Output = Accelerator.Allocate1D<byte>(minCapacity); Output = Accelerator.Allocate1D<byte>(minCapacity);
HeuristicFlag = Accelerator.Allocate1D<int>(minCapacity); HeuristicFlag = Accelerator.Allocate1D<int>(minCapacity);
@@ -464,6 +483,12 @@ public sealed class PrimeTester
} }
_returned = true; _returned = true;
if (_stream is { } stream)
{
stream.Dispose();
_stream = null;
}
Return(this); Return(this);
} }
@@ -734,6 +759,7 @@ public sealed class PrimeTester
// and divisor-cycle staging instead of allocating new device buffers per call. // and divisor-cycle staging instead of allocating new device buffers per call.
var gpu = PrimeTesterGpuContextPool.Rent(); var gpu = PrimeTesterGpuContextPool.Rent();
var accelerator = gpu.Accelerator; var accelerator = gpu.Accelerator;
var stream = gpu.Stream;
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SharesFactorKernel); var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SharesFactorKernel);
int length = values.Length; int length = values.Length;
@@ -743,10 +769,10 @@ public sealed class PrimeTester
ArrayPool<ulong> pool = ThreadStaticPools.UlongPool; ArrayPool<ulong> pool = ThreadStaticPools.UlongPool;
ulong[] temp = pool.Rent(length); ulong[] temp = pool.Rent(length);
values.CopyTo(temp); values.CopyTo(temp);
inputBuffer.View.CopyFromCPU(ref temp[0], length); inputBuffer.View.CopyFromCPU(stream, ref temp[0], length);
kernel(length, inputBuffer.View, resultBuffer.View); kernel(length, inputBuffer.View, resultBuffer.View);
accelerator.Synchronize(); stream.Synchronize();
resultBuffer.View.CopyToCPU(ref results[0], length); resultBuffer.View.CopyToCPU(stream, ref results[0], length);
pool.Return(temp, clearArray: false); pool.Return(temp, clearArray: false);
resultBuffer.Dispose(); resultBuffer.Dispose();
inputBuffer.Dispose(); inputBuffer.Dispose();
+14
View File
@@ -179,6 +179,20 @@ namespace PerfectNumbers.Core
pool.Add(list); pool.Add(list);
} }
[ThreadStatic]
private static ArrayPool<(ulong, ulong)>? _ulongUlongTupleArrayPool;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (ulong, ulong)[] RentUlongUlongArray(int capacityHint)
{
_ulongUlongTupleArrayPool ??= ArrayPool<(ulong, ulong)>.Create();
ArrayPool<(ulong, ulong)>? pool = _ulongUlongTupleArrayPool;
return pool.Rent(capacityHint);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Return((ulong, ulong)[] array) => _ulongUlongTupleArrayPool!.Return(array, clearArray: false);
[ThreadStatic] [ThreadStatic]
private static List<List<PrimeOrderCalculator.PendingEntry>>? _primeOrderPendingEntryListPool; private static List<List<PrimeOrderCalculator.PendingEntry>>? _primeOrderPendingEntryListPool;
+2 -2
View File
@@ -456,7 +456,7 @@ public static partial class ULongExtensions
{ {
ulong result = 0UL; ulong result = 0UL;
GpuKernelLease lease = GpuKernelPool.GetKernel(useGpuOrder: true); GpuKernelLease lease = GpuKernelPool.GetKernel();
Accelerator accelerator = lease.Accelerator; Accelerator accelerator = lease.Accelerator;
// Keep this commented out. It should never happen in production code. // Keep this commented out. It should never happen in production code.
@@ -479,7 +479,7 @@ public static partial class ULongExtensions
kernel(stream, 1, exponentBuffer.View, divisor, resultBuffer.View); kernel(stream, 1, exponentBuffer.View, divisor, resultBuffer.View);
stream.Synchronize(); stream.Synchronize();
resultBuffer.View.CopyToCPU(ref result, 1); resultBuffer.View.CopyToCPU(stream, ref result, 1);
resultBuffer.Dispose(); resultBuffer.Dispose();
exponentBuffer.Dispose(); exponentBuffer.Dispose();