Various optimizations to separate non-static data between threads and resolve read/write access issue.
This commit is contained in:
1 parent
5a17fa64d5
commit
64d615c402
26 files changed
+1181
-1117
No files matched your search
@@ -64,6 +64,7 @@ internal static class Program
|
||||
GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads);
|
||||
GpuContextPool.WarmUpPool(threadCount);
|
||||
PrimeTester.WarmUpGpuKernels(gpuPrimeThreads);
|
||||
GpuScratchBufferPool.WarmUp(gpuPrimeThreads << 4, 64, 1024);
|
||||
Console.WriteLine("Starting up threads...");
|
||||
_ = UnboundedTaskScheduler.Instance;
|
||||
int blockSize = Math.Max(1, _cliArguments.BlockSize);
|
||||
|
||||
@@ -330,26 +330,26 @@ public class GpuKernelCompilationTests
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private static void CompilePrimeTesterSharesFactorKernel(Accelerator accelerator)
|
||||
{
|
||||
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterSharesFactorKernel);
|
||||
var kernel = accelerator.LoadAutoGroupedStreamKernel(PrimeTesterSharesFactorKernel);
|
||||
_ = kernel;
|
||||
}
|
||||
|
||||
private static void CompileGpuKernelPoolKernels(Accelerator accelerator)
|
||||
{
|
||||
_ = accelerator;
|
||||
var lease = GpuKernelPool.GetKernel(useGpuOrder: false);
|
||||
var lease = GpuKernelPool.GetKernel();
|
||||
try
|
||||
{
|
||||
_ = lease.OrderKernel;
|
||||
@@ -363,7 +363,7 @@ public class GpuKernelCompilationTests
|
||||
lease.Dispose();
|
||||
}
|
||||
|
||||
var orderLease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
||||
var orderLease = GpuKernelPool.GetKernel();
|
||||
try
|
||||
{
|
||||
_ = orderLease.OrderKernel;
|
||||
|
||||
@@ -150,7 +150,7 @@ public class NttGpuMathTests
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
|
||||
GpuContextPool.GpuContextLease gpu = GpuContextPool.Rent();
|
||||
GpuContextLease gpu = GpuContextPool.Rent();
|
||||
try
|
||||
{
|
||||
var accelerator = gpu.Accelerator;
|
||||
@@ -345,7 +345,7 @@ public class NttGpuMathTests
|
||||
values[1] = new GpuUInt128(0UL, 2UL);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
GpuContextPool.GpuContextLease gpu = GpuContextPool.Rent();
|
||||
GpuContextLease gpu = GpuContextPool.Rent();
|
||||
try
|
||||
{
|
||||
var buffer = gpu.Accelerator.Allocate1D<GpuUInt128>(values.Length);
|
||||
|
||||
@@ -33,7 +33,7 @@ public class NttMontgomeryTests
|
||||
NttGpuMath.Square(cpu, modulus, primitiveRoot);
|
||||
|
||||
// Device execution
|
||||
GpuContextPool.GpuContextLease context = GpuContextPool.Rent();
|
||||
GpuContextLease context = GpuContextPool.Rent();
|
||||
try
|
||||
{
|
||||
var accelerator = context.Accelerator;
|
||||
|
||||
@@ -226,9 +226,10 @@ public sealed class DivisorCycleCache
|
||||
private void ComputeCyclesGpuCore(ReadOnlySpan<ulong> divisors, Span<ulong> destination)
|
||||
{
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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++)
|
||||
{
|
||||
@@ -275,18 +276,18 @@ public sealed class DivisorCycleCache
|
||||
statusSpan[i] = ByteZero;
|
||||
}
|
||||
|
||||
powBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(powSpan), length);
|
||||
orderBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(orderSpan), length);
|
||||
resultBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(resultSpan), length);
|
||||
statusBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(statusSpan), length);
|
||||
powBuffer.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(powSpan), length);
|
||||
orderBuffer.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(orderSpan), length);
|
||||
resultBuffer.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(resultSpan), length);
|
||||
statusBuffer.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(statusSpan), length);
|
||||
|
||||
int pending;
|
||||
do
|
||||
{
|
||||
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;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
@@ -298,7 +299,7 @@ public sealed class DivisorCycleCache
|
||||
}
|
||||
while (pending > 0);
|
||||
|
||||
resultBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(resultSpan), length);
|
||||
resultBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(resultSpan), length);
|
||||
resultSpan.CopyTo(destination);
|
||||
if (rentedPow is not null)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,21 @@ 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
|
||||
@@ -14,12 +29,10 @@ public static class GpuContextPool
|
||||
public readonly Context Context;
|
||||
public readonly Accelerator Accelerator;
|
||||
|
||||
public PooledContext(KernelContainer kernels)
|
||||
public PooledContext()
|
||||
{
|
||||
Context = SharedGpuContext.Context;
|
||||
Accelerator = SharedGpuContext.Accelerator;
|
||||
GpuStaticTableInitializer.EnsureStaticTables(kernels, Accelerator);
|
||||
kernels.Dispose();
|
||||
// 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
|
||||
@@ -29,11 +42,9 @@ public static class GpuContextPool
|
||||
public void Dispose()
|
||||
{
|
||||
// Ensure all cached GPU buffers for this accelerator are released.
|
||||
NttGpuMath.ClearCaches(Accelerator);
|
||||
// Release PrimeTester GPU state for this accelerator (kernel/device primes)
|
||||
// Clear any per-accelerator cached resources to avoid releasing
|
||||
// after the accelerator is destroyed.
|
||||
PrimeTester.ClearGpuCaches(Accelerator);
|
||||
|
||||
// These resources are shared between GPU leases
|
||||
// Accelerator.Dispose();
|
||||
@@ -53,7 +64,7 @@ public static class GpuContextPool
|
||||
}
|
||||
|
||||
// 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;
|
||||
for (int i = 0; i < toCreate; i++)
|
||||
{
|
||||
KernelContainer kernels = GpuKernelPool.GetKernels();
|
||||
context = new PooledContext(kernels);
|
||||
context = new PooledContext();
|
||||
contexts[i] = context;
|
||||
pool.Enqueue(context);
|
||||
}
|
||||
@@ -87,41 +97,12 @@ public static class GpuContextPool
|
||||
gpu.Dispose();
|
||||
}
|
||||
|
||||
PrimeTester.DisposeGpuContexts();
|
||||
WarmedGpuContextCount = 0;
|
||||
}
|
||||
|
||||
private static void Return(PooledContext ctx)
|
||||
internal static void Return(PooledContext ctx)
|
||||
{
|
||||
ctx.Accelerator.Synchronize();
|
||||
// ctx.Accelerator.Synchronize();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using ILGPU;
|
||||
using ILGPU.Runtime;
|
||||
using PerfectNumbers.Core;
|
||||
using static PerfectNumbers.Core.Gpu.GpuContextPool;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
@@ -11,11 +8,13 @@ public sealed class GpuKernelLease
|
||||
{
|
||||
private static readonly ConcurrentQueue<GpuKernelLease> Pool = new();
|
||||
|
||||
private GpuContextLease? _gpu;
|
||||
private GpuContextLease _gpu;
|
||||
public KernelContainer Kernels;
|
||||
private AcceleratorStream? _stream;
|
||||
public AcceleratorStream Stream;
|
||||
|
||||
private GpuKernelLease(KernelContainer kernels)
|
||||
#pragma warning disable CS8618 // _gpu and Stream will be set by Rent method
|
||||
private GpuKernelLease(KernelContainer kernels)
|
||||
#pragma warning restore CS8618
|
||||
{
|
||||
Kernels = kernels;
|
||||
}
|
||||
@@ -28,35 +27,19 @@ public sealed class GpuKernelLease
|
||||
}
|
||||
|
||||
lease._gpu = gpu;
|
||||
lease.Kernels = kernels;
|
||||
lease._stream = null;
|
||||
lease.Kernels = kernels;
|
||||
lease.Stream = gpu.Accelerator.CreateStream();
|
||||
return lease;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
get
|
||||
{
|
||||
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 kernel = KernelUtil.GetKernel(loaded);
|
||||
@@ -70,7 +53,7 @@ public sealed class GpuKernelLease
|
||||
get
|
||||
{
|
||||
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 kernel = KernelUtil.GetKernel(loaded);
|
||||
@@ -84,7 +67,7 @@ public sealed class GpuKernelLease
|
||||
get
|
||||
{
|
||||
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 kernel = KernelUtil.GetKernel(loaded);
|
||||
@@ -98,7 +81,7 @@ public sealed class GpuKernelLease
|
||||
get
|
||||
{
|
||||
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 kernel = KernelUtil.GetKernel(loaded);
|
||||
@@ -112,7 +95,7 @@ public sealed class GpuKernelLease
|
||||
get
|
||||
{
|
||||
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 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
|
||||
{
|
||||
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);
|
||||
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
|
||||
{
|
||||
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 kernel = KernelUtil.GetKernel(loaded);
|
||||
@@ -151,15 +134,10 @@ public sealed class GpuKernelLease
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_stream?.Dispose();
|
||||
_stream = null;
|
||||
|
||||
_gpu?.Dispose();
|
||||
_gpu = null;
|
||||
|
||||
Kernels?.Dispose();
|
||||
GpuPrimeWorkLimiter.Release();
|
||||
Stream.Dispose();
|
||||
_gpu.Dispose();
|
||||
Pool.Enqueue(this);
|
||||
GpuPrimeWorkLimiter.Release();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
using PerfectNumbers.Core;
|
||||
using System.Collections.Concurrent;
|
||||
using ILGPU;
|
||||
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.
|
||||
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,
|
||||
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, 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.
|
||||
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<ulong, Stride1D.Dense>? SmallPrimesPow2LastOne;
|
||||
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastSeven;
|
||||
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2LastOne;
|
||||
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;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
var current = Volatile.Read(ref slot);
|
||||
if (current is not null)
|
||||
{
|
||||
return current;
|
||||
}
|
||||
// Order = null;
|
||||
// Incremental = null;
|
||||
// Pow2Mod = null;
|
||||
// IncrementalOrder = null;
|
||||
// Pow2ModOrder = null;
|
||||
// SmallPrimeFactor = null;
|
||||
// SpecialMax = null;
|
||||
|
||||
current = Volatile.Read(ref slot);
|
||||
if (current is null)
|
||||
{
|
||||
current = factory();
|
||||
Volatile.Write(ref slot, current);
|
||||
}
|
||||
return current;
|
||||
// SmallCycles?.Dispose();
|
||||
// SmallPrimesLastOne?.Dispose();
|
||||
// SmallPrimesLastSeven?.Dispose();
|
||||
// SmallPrimesPow2LastOne?.Dispose();
|
||||
// SmallPrimesPow2LastSeven?.Dispose();
|
||||
// SmallPrimeFactorsPrimes?.Dispose();
|
||||
// SmallPrimeFactorsSquares?.Dispose();
|
||||
}
|
||||
|
||||
public void Dispose() => GpuKernelPool.Return(this);
|
||||
}
|
||||
|
||||
public readonly struct ResiduePrimeViews(
|
||||
@@ -101,49 +96,30 @@ public readonly struct SmallPrimeFactorTables(
|
||||
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
|
||||
{
|
||||
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)
|
||||
? container
|
||||
: new KernelContainer();
|
||||
public static T InitOnce<T>(ref T? target, Func<T> valueFactory) where T : class
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -215,10 +191,10 @@ public class GpuKernelPool
|
||||
return new SmallPrimeFactorTables(primeBuffer, squareBuffer, (int)primeBuffer.Length);
|
||||
}
|
||||
|
||||
if (kernels.SmallPrimeFactorsPrimes is { } existingPrimes && kernels.SmallPrimeFactorsSquares is { } existingSquares)
|
||||
{
|
||||
return new SmallPrimeFactorTables(existingPrimes, existingSquares, (int)existingPrimes.Length);
|
||||
}
|
||||
// if (kernels.SmallPrimeFactorsPrimes is { } existingPrimes && kernels.SmallPrimeFactorsSquares is { } existingSquares)
|
||||
// {
|
||||
// return new SmallPrimeFactorTables(existingPrimes, existingSquares, (int)existingPrimes.Length);
|
||||
// }
|
||||
|
||||
var hostPrimes = PrimesGenerator.SmallPrimes;
|
||||
var hostSquares = PrimesGenerator.SmallPrimesPow2;
|
||||
@@ -234,74 +210,15 @@ public class GpuKernelPool
|
||||
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 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)
|
||||
public static GpuKernelLease GetKernel()
|
||||
{
|
||||
GpuPrimeWorkLimiter.Acquire();
|
||||
var gpu = Rent();
|
||||
var kernels = GetKernels();
|
||||
var kernels = GetKernels(gpu.Accelerator);
|
||||
return GpuKernelLease.Rent(gpu, kernels);
|
||||
}
|
||||
|
||||
@@ -311,12 +228,10 @@ public class GpuKernelPool
|
||||
/// <param name="action">Action to run with (Accelerator, Stream).</param>
|
||||
public static void Run(Action<Accelerator, AcceleratorStream> action)
|
||||
{
|
||||
var lease = GetKernel(useGpuOrder: true);
|
||||
var lease = GetKernel();
|
||||
var accelerator = lease.Accelerator;
|
||||
var stream = accelerator.CreateStream();
|
||||
var stream = lease.Stream;
|
||||
action(accelerator, stream);
|
||||
stream.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.Runtime;
|
||||
using PerfectNumbers.Core;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
@@ -13,7 +12,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
int factorCount,
|
||||
MontgomeryDivisorData divisor,
|
||||
ArrayView1D<ulong, Stride1D.Dense> candidates,
|
||||
ArrayView1D<ushort, Stride1D.Dense> resultOut)
|
||||
ArrayView1D<ulong, Stride1D.Dense> resultOut)
|
||||
{
|
||||
if (index != 0)
|
||||
{
|
||||
@@ -62,7 +61,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
var stepper = new ExponentRemainderStepperGpu(divisorPartial);
|
||||
if (stepper.InitializeIsUnityGpu(candidates[0]))
|
||||
{
|
||||
resultOut[0] = 0;
|
||||
Atomic.Exchange(ref resultOut[0], 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -70,11 +69,11 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
{
|
||||
if (stepper.ComputeNextIsUnityGpu(candidates[i]))
|
||||
{
|
||||
resultOut[0] = 0;
|
||||
Atomic.Exchange(ref resultOut[0], 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
resultOut[0] = 1;
|
||||
Atomic.Exchange(ref resultOut[0], 1);
|
||||
}
|
||||
}
|
||||
@@ -18,19 +18,19 @@ internal static class PrimeTesterKernels
|
||||
{
|
||||
// GPU kernel: small-prime sieve only. Returns 1 if passes sieve (probable prime), 0 otherwise.
|
||||
public static void SmallPrimeSieveKernel(
|
||||
Index1D index,
|
||||
ArrayView<ulong> numbers,
|
||||
ArrayView<uint> smallPrimes,
|
||||
ArrayView<uint> smallPrimesLastOne,
|
||||
ArrayView<uint> smallPrimesLastSeven,
|
||||
ArrayView<uint> smallPrimesLastThree,
|
||||
ArrayView<uint> smallPrimesLastNine,
|
||||
ArrayView<ulong> smallPrimesPow2,
|
||||
ArrayView<ulong> smallPrimesPow2LastOne,
|
||||
ArrayView<ulong> smallPrimesPow2LastSeven,
|
||||
ArrayView<ulong> smallPrimesPow2LastThree,
|
||||
ArrayView<ulong> smallPrimesPow2LastNine,
|
||||
ArrayView<byte> results)
|
||||
Index1D index,
|
||||
ArrayView<ulong> numbers,
|
||||
ArrayView<uint> smallPrimes,
|
||||
ArrayView<uint> smallPrimesLastOne,
|
||||
ArrayView<uint> smallPrimesLastSeven,
|
||||
ArrayView<uint> smallPrimesLastThree,
|
||||
ArrayView<uint> smallPrimesLastNine,
|
||||
ArrayView<ulong> smallPrimesPow2,
|
||||
ArrayView<ulong> smallPrimesPow2LastOne,
|
||||
ArrayView<ulong> smallPrimesPow2LastSeven,
|
||||
ArrayView<ulong> smallPrimesPow2LastThree,
|
||||
ArrayView<ulong> smallPrimesPow2LastNine,
|
||||
ArrayView<byte> results)
|
||||
{
|
||||
ulong n = numbers[index];
|
||||
byte result = 1;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
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 ConcurrentBag<BatchResources> _resourcePool = new();
|
||||
private readonly ConcurrentBag<GpuContextPool.GpuContextLease> _acceleratorPool = new();
|
||||
private readonly ConcurrentBag<GpuContextLease> _acceleratorPool = 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) =>
|
||||
@@ -476,7 +477,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
public sealed class DivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
|
||||
{
|
||||
private readonly MersenneNumberDivisorByDivisorGpuTester _owner;
|
||||
private readonly GpuContextPool.GpuContextLease _lease;
|
||||
private readonly GpuContextLease _lease;
|
||||
private readonly Accelerator _accelerator;
|
||||
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!;
|
||||
@@ -650,7 +651,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
}
|
||||
}
|
||||
|
||||
private GpuContextPool.GpuContextLease RentAccelerator()
|
||||
private GpuContextLease RentAccelerator()
|
||||
{
|
||||
if (_acceleratorPool.TryTake(out var lease))
|
||||
{
|
||||
@@ -660,7 +661,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
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)
|
||||
{
|
||||
@@ -899,8 +900,4 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
}
|
||||
|
||||
IMersenneNumberDivisorByDivisorTester.IDivisorScanSession IMersenneNumberDivisorByDivisorTester.CreateDivisorSession() => CreateDivisorSession();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -38,19 +38,21 @@ public sealed class MersenneNumberDivisorGpuTester
|
||||
public bool IsDivisible(ulong exponent, in ReadOnlyGpuUInt128 divisor)
|
||||
{
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var accelerator = gpu.Accelerator;
|
||||
var accelerator = gpu.Accelerator;
|
||||
var stream = accelerator.CreateStream();
|
||||
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();
|
||||
kernel(1, exponent, divisor, resultBuffer.View);
|
||||
accelerator.Synchronize();
|
||||
stream.Synchronize();
|
||||
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;
|
||||
result[0] = 0;
|
||||
resultBuffer.View.CopyFromCPU(ref result[0], 1);
|
||||
accelerator.Synchronize();
|
||||
resultBuffer.View.CopyFromCPU(stream, ref result[0], 1);
|
||||
stream.Synchronize();
|
||||
stream.Dispose();
|
||||
gpu.Dispose();
|
||||
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}).");
|
||||
|
||||
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
|
||||
var gpuLease = GpuKernelPool.GetKernel();
|
||||
var accelerator = gpuLease.Accelerator;
|
||||
var stream = gpuLease.Stream;
|
||||
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)
|
||||
{
|
||||
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
|
||||
var gpuLease = GpuKernelPool.GetKernel();
|
||||
var accelerator = gpuLease.Accelerator;
|
||||
var stream = gpuLease.Stream;
|
||||
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Concurrent;
|
||||
@@ -13,7 +14,7 @@ namespace PerfectNumbers.Core.Gpu;
|
||||
public class MersenneNumberResidueGpuTester(bool 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, ConcurrentBag<ResidueResources>> _resourcePools = new(AcceleratorReferenceComparer.Instance);
|
||||
|
||||
@@ -35,7 +36,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
var stream = accelerator.CreateStream();
|
||||
var kernel = GetPow2ModKernel(accelerator);
|
||||
var resources = RentResources(accelerator, GpuConstants.ScanBatchSize);
|
||||
var gpuKernels = GpuKernelPool.GetKernels();
|
||||
var gpuKernels = GpuKernelPool.GetKernels(accelerator);
|
||||
|
||||
var orderBuffer = resources.OrderBuffer;
|
||||
ulong[] orderArray = resources.OrderArray;
|
||||
@@ -69,7 +70,8 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
kernel(stream, currentSize, exponent, twoPGpu, (GpuUInt128)kStart, last, 0UL,
|
||||
kernelArgs, orderBuffer.View, smallCyclesView, primeViews.LastOne, primeViews.LastSeven, primeViews.LastOnePow2, primeViews.LastSevenPow2);
|
||||
|
||||
stream.Synchronize();
|
||||
stream.Synchronize();
|
||||
|
||||
orderBuffer.View.CopyToCPU(ref ordersRef, currentSize);
|
||||
if (!Volatile.Read(ref isPrime))
|
||||
{
|
||||
@@ -127,7 +129,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
loaded.Method);
|
||||
});
|
||||
|
||||
private GpuContextPool.GpuContextLease RentAccelerator()
|
||||
private GpuContextLease RentAccelerator()
|
||||
{
|
||||
if (_acceleratorPool.TryTake(out var lease))
|
||||
{
|
||||
@@ -137,11 +139,11 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
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)
|
||||
{
|
||||
var bag = _resourcePools.GetOrAdd(accelerator, static _ => new ConcurrentBag<ResidueResources>());
|
||||
var bag = _resourcePools.GetOrAdd(accelerator, static _ => []);
|
||||
while (bag.TryTake(out var resources))
|
||||
{
|
||||
if (resources.Capacity >= capacity)
|
||||
@@ -195,4 +197,4 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -198,7 +198,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
|
||||
try
|
||||
{
|
||||
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
||||
var lease = GpuKernelPool.GetKernel();
|
||||
Accelerator accelerator = lease.Accelerator;
|
||||
AcceleratorStream stream = lease.Stream;
|
||||
|
||||
@@ -364,7 +364,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
{
|
||||
order = 0UL;
|
||||
|
||||
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
||||
var lease = GpuKernelPool.GetKernel();
|
||||
Accelerator accelerator = lease.Accelerator;
|
||||
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)
|
||||
{
|
||||
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
||||
var lease = GpuKernelPool.GetKernel();
|
||||
Accelerator accelerator = lease.Accelerator;
|
||||
AcceleratorStream stream = lease.Stream;
|
||||
var kernel = GetPow2ModKernel(accelerator);
|
||||
@@ -581,7 +581,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
|
||||
GpuUInt128[]? rentedExponents = null;
|
||||
GpuUInt128[]? rentedResults = null;
|
||||
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
||||
var lease = GpuKernelPool.GetKernel();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ using ILGPU.Runtime;
|
||||
|
||||
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<Device> s_device = new(() => s_context.Value.GetPreferredDevice(false), LazyThreadSafetyMode.ExecutionAndPublication);
|
||||
|
||||
@@ -2,6 +2,7 @@ using Open.Numeric.Primes;
|
||||
using ILGPU.Runtime;
|
||||
using System.Runtime.CompilerServices;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
@@ -227,6 +228,7 @@ public sealed class HeuristicCombinedPrimeTester
|
||||
GpuPrimeWorkLimiter.Acquire();
|
||||
var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(1);
|
||||
var accelerator = gpu.Accelerator;
|
||||
var stream = gpu.Stream;
|
||||
var kernel = PrimeTester.PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.HeuristicTrialDivisionKernel;
|
||||
var flagView1D = gpu.HeuristicFlag.View;
|
||||
var flagView = flagView1D.AsContiguous();
|
||||
@@ -243,8 +245,9 @@ public sealed class HeuristicCombinedPrimeTester
|
||||
maxDivisorSquare,
|
||||
HeuristicGpuDivisorTableKind.Combined,
|
||||
gpu.HeuristicGpuTables);
|
||||
accelerator.Synchronize();
|
||||
flagView1D.CopyToCPU(ref compositeFlag, 1);
|
||||
stream.Synchronize();
|
||||
|
||||
flagView1D.CopyToCPU(stream, ref compositeFlag, 1);
|
||||
compositeDetected = compositeFlag != 0;
|
||||
|
||||
gpu.Dispose();
|
||||
|
||||
@@ -243,6 +243,7 @@ public sealed class HeuristicPrimeTester
|
||||
GpuPrimeWorkLimiter.Acquire();
|
||||
var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(1);
|
||||
var accelerator = gpu.Accelerator;
|
||||
var stream = gpu.Stream;
|
||||
var kernel = PrimeTester.PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.HeuristicTrialDivisionKernel;
|
||||
var flagView1D = gpu.HeuristicFlag.View;
|
||||
var flagView = flagView1D.AsContiguous();
|
||||
@@ -260,8 +261,9 @@ public sealed class HeuristicPrimeTester
|
||||
maxDivisorSquare,
|
||||
HeuristicGpuDivisorTableKind.GroupA,
|
||||
gpu.HeuristicGpuTables);
|
||||
accelerator.Synchronize();
|
||||
flagView1D.CopyToCPU(ref compositeFlag, 1);
|
||||
stream.Synchronize();
|
||||
|
||||
flagView1D.CopyToCPU(stream, ref compositeFlag, 1);
|
||||
compositeDetected = compositeFlag != 0;
|
||||
|
||||
if (!compositeDetected)
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -146,7 +146,7 @@ public sealed class MersenneNumberTester(
|
||||
|
||||
UInt128[] qs = qsBuffer;
|
||||
|
||||
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
|
||||
var gpuLease = GpuKernelPool.GetKernel();
|
||||
var accelerator = gpuLease.Accelerator;
|
||||
var stream = gpuLease.Stream;
|
||||
var orderKernel = gpuLease.OrderKernel!;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using ILGPU;
|
||||
using ILGPU.Runtime;
|
||||
using ILGPU.Runtime.OpenCL;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
@@ -21,40 +19,53 @@ internal static partial class PrimeOrderCalculator
|
||||
Span<int> exponentBuffer = exponentBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots);
|
||||
remaining = value;
|
||||
|
||||
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
||||
var lease = GpuKernelPool.GetKernel();
|
||||
Accelerator accelerator = lease.Accelerator;
|
||||
AcceleratorStream stream = lease.Stream;
|
||||
var stream = lease.Stream;
|
||||
KernelContainer kernels = lease.Kernels;
|
||||
|
||||
SmallPrimeFactorTables tables = GpuKernelPool.EnsureSmallPrimeFactorTables(lease.Kernels, accelerator);
|
||||
SmallPrimeFactorScratch scratch = GpuKernelPool.EnsureSmallPrimeFactorScratch(lease.Kernels, accelerator, GpuSmallPrimeFactorSlots);
|
||||
ScratchBuffer scratch = GpuScratchBufferPool.Rent(accelerator, GpuSmallPrimeFactorSlots, 0);
|
||||
|
||||
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;
|
||||
|
||||
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(
|
||||
stream,
|
||||
1,
|
||||
value,
|
||||
limit,
|
||||
tables.PrimesView,
|
||||
tables.SquaresView,
|
||||
tables.Count,
|
||||
scratch.PrimeSlots.View,
|
||||
scratch.ExponentSlots.View,
|
||||
scratch.CountSlot.View,
|
||||
scratch.RemainingSlot.View);
|
||||
smallPrimeFactorsPrimesView,
|
||||
kernels.SmallPrimeFactorsSquares!.View,
|
||||
(int)smallPrimeFactorsPrimesView.Length,
|
||||
smallPrimeFactorPrimeSlotsView,
|
||||
smallPrimeFactorExponentSlotsView,
|
||||
smallPrimeFactorCountSlotView,
|
||||
smallPrimeFactorRemainingSlotView);
|
||||
|
||||
stream.Synchronize();
|
||||
|
||||
factorCount = 0;
|
||||
scratch.CountSlot.View.CopyToCPU(ref factorCount, 1);
|
||||
smallPrimeFactorCountSlotView.CopyToCPU(stream, ref factorCount, 1);
|
||||
factorCount = Math.Min(factorCount, GpuSmallPrimeFactorSlots);
|
||||
|
||||
if (factorCount > 0)
|
||||
{
|
||||
scratch.PrimeSlots.View.CopyToCPU(ref MemoryMarshal.GetReference(primeBuffer), factorCount);
|
||||
scratch.ExponentSlots.View.CopyToCPU(ref MemoryMarshal.GetReference(exponentBuffer), factorCount);
|
||||
smallPrimeFactorPrimeSlotsView.CopyToCPU(stream, ref MemoryMarshal.GetReference(primeBuffer), 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++)
|
||||
{
|
||||
@@ -63,9 +74,8 @@ internal static partial class PrimeOrderCalculator
|
||||
counts.Add(primeValue, exponent);
|
||||
}
|
||||
|
||||
GpuScratchBufferPool.Return(scratch);
|
||||
lease.Dispose();
|
||||
ThreadStaticPools.UlongPool.Return(primeBufferArray);
|
||||
ThreadStaticPools.IntPool.Return(exponentBufferArray);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -88,32 +98,40 @@ internal static partial class PrimeOrderCalculator
|
||||
factorSpan[i] = factors[i].Value;
|
||||
}
|
||||
|
||||
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
||||
GpuKernelLease lease = GpuKernelPool.GetKernel();
|
||||
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;
|
||||
|
||||
ArrayView1D<ulong, Stride1D.Dense> specialMaxResultView = scratch.SpecialMaxResult.View;
|
||||
|
||||
kernel(
|
||||
stream,
|
||||
1,
|
||||
phi,
|
||||
scratch.FactorsView,
|
||||
specialMaxFactorsView,
|
||||
factorCount,
|
||||
divisorData,
|
||||
scratch.CandidatesView,
|
||||
scratch.ResultView);
|
||||
|
||||
scratch.SpecialMaxCandidates.View,
|
||||
specialMaxResultView);
|
||||
stream.Synchronize();
|
||||
|
||||
ushort result = 0;
|
||||
scratch.ResultView.CopyToCPU(ref result, 1);
|
||||
lease.Dispose();
|
||||
Span<ulong> result = stackalloc ulong[1];
|
||||
// specialMaxResultView.CopyToCPU(stream, ref result, 1);
|
||||
specialMaxResultView.CopyToCPU(stream, in result);
|
||||
|
||||
return result != 0;
|
||||
// stream.Synchronize();
|
||||
// return result != 0;
|
||||
GpuScratchBufferPool.Return(scratch);
|
||||
lease.Dispose();
|
||||
return result[0] != 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ public sealed class PrimeTester
|
||||
GpuPrimeWorkLimiter.Acquire();
|
||||
var gpu = PrimeTesterGpuContextPool.Rent(1);
|
||||
var accelerator = gpu.Accelerator;
|
||||
var stream = gpu.Stream;
|
||||
var kernel = PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.Kernel;
|
||||
|
||||
ulong value = n;
|
||||
@@ -108,7 +109,7 @@ public sealed class PrimeTester
|
||||
var inputView = input.View;
|
||||
var outputView = output.View;
|
||||
|
||||
inputView.CopyFromCPU(ref value, 1);
|
||||
inputView.CopyFromCPU(stream, ref value, 1);
|
||||
kernel(
|
||||
1,
|
||||
inputView,
|
||||
@@ -123,8 +124,8 @@ public sealed class PrimeTester
|
||||
gpu.DevicePrimesPow2LastThree.View,
|
||||
gpu.DevicePrimesPow2LastNine.View,
|
||||
outputView);
|
||||
accelerator.Synchronize();
|
||||
outputView.CopyToCPU(ref flag, 1);
|
||||
stream.Synchronize();
|
||||
outputView.CopyToCPU(stream, ref flag, 1);
|
||||
|
||||
gpu.Dispose();
|
||||
GpuPrimeWorkLimiter.Release();
|
||||
@@ -163,6 +164,7 @@ public sealed class PrimeTester
|
||||
GpuPrimeWorkLimiter.Acquire();
|
||||
var gpu = PrimeTesterGpuContextPool.Rent(GpuBatchSize);
|
||||
var accelerator = gpu.Accelerator;
|
||||
var stream = gpu.Stream;
|
||||
var kernel = PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.Kernel;
|
||||
int totalLength = values.Length;
|
||||
int batchSize = GpuBatchSize;
|
||||
@@ -196,10 +198,11 @@ public sealed class PrimeTester
|
||||
gpu.DevicePrimesPow2LastThree.View,
|
||||
gpu.DevicePrimesPow2LastNine.View,
|
||||
outputView);
|
||||
accelerator.Synchronize();
|
||||
stream.Synchronize();
|
||||
|
||||
var resultSlice = results.Slice(pos, count);
|
||||
ref byte resultRef = ref MemoryMarshal.GetReference(resultSlice);
|
||||
outputView.CopyToCPU(ref resultRef, count);
|
||||
outputView.CopyToCPU(stream, ref resultRef, count);
|
||||
|
||||
pos += count;
|
||||
}
|
||||
@@ -266,8 +269,9 @@ public sealed class PrimeTester
|
||||
return;
|
||||
}
|
||||
|
||||
KernelContainer kernels = GpuKernelPool.GetKernels();
|
||||
GpuStaticTableInitializer.EnsureStaticTables(kernels, SharedGpuContext.Accelerator);
|
||||
Accelerator accelerator = SharedGpuContext.Accelerator;
|
||||
KernelContainer kernels = GpuKernelPool.GetKernels(accelerator);
|
||||
GpuStaticTableInitializer.EnsureStaticTables(kernels, accelerator);
|
||||
|
||||
int toCreate = target - WarmedLeaseCount;
|
||||
for (int i = 0; i < toCreate; i++)
|
||||
@@ -275,7 +279,6 @@ public sealed class PrimeTester
|
||||
Pool.Enqueue(new PrimeTesterGpuContextLease(minBufferCapacity));
|
||||
}
|
||||
|
||||
kernels.Dispose();
|
||||
WarmedLeaseCount = target;
|
||||
}
|
||||
}
|
||||
@@ -379,6 +382,21 @@ public sealed class PrimeTester
|
||||
|
||||
internal readonly Context AcceleratorContext;
|
||||
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);
|
||||
|
||||
@@ -445,6 +463,7 @@ public sealed class PrimeTester
|
||||
Input?.Dispose();
|
||||
Output?.Dispose();
|
||||
HeuristicFlag?.Dispose();
|
||||
|
||||
Input = Accelerator.Allocate1D<ulong>(minCapacity);
|
||||
Output = Accelerator.Allocate1D<byte>(minCapacity);
|
||||
HeuristicFlag = Accelerator.Allocate1D<int>(minCapacity);
|
||||
@@ -464,6 +483,12 @@ public sealed class PrimeTester
|
||||
}
|
||||
|
||||
_returned = true;
|
||||
if (_stream is { } stream)
|
||||
{
|
||||
stream.Dispose();
|
||||
_stream = null;
|
||||
}
|
||||
|
||||
Return(this);
|
||||
}
|
||||
|
||||
@@ -734,6 +759,7 @@ public sealed class PrimeTester
|
||||
// and divisor-cycle staging instead of allocating new device buffers per call.
|
||||
var gpu = PrimeTesterGpuContextPool.Rent();
|
||||
var accelerator = gpu.Accelerator;
|
||||
var stream = gpu.Stream;
|
||||
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SharesFactorKernel);
|
||||
|
||||
int length = values.Length;
|
||||
@@ -743,10 +769,10 @@ public sealed class PrimeTester
|
||||
ArrayPool<ulong> pool = ThreadStaticPools.UlongPool;
|
||||
ulong[] temp = pool.Rent(length);
|
||||
values.CopyTo(temp);
|
||||
inputBuffer.View.CopyFromCPU(ref temp[0], length);
|
||||
inputBuffer.View.CopyFromCPU(stream, ref temp[0], length);
|
||||
kernel(length, inputBuffer.View, resultBuffer.View);
|
||||
accelerator.Synchronize();
|
||||
resultBuffer.View.CopyToCPU(ref results[0], length);
|
||||
stream.Synchronize();
|
||||
resultBuffer.View.CopyToCPU(stream, ref results[0], length);
|
||||
pool.Return(temp, clearArray: false);
|
||||
resultBuffer.Dispose();
|
||||
inputBuffer.Dispose();
|
||||
|
||||
@@ -180,6 +180,20 @@ namespace PerfectNumbers.Core
|
||||
}
|
||||
|
||||
[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]
|
||||
private static List<List<PrimeOrderCalculator.PendingEntry>>? _primeOrderPendingEntryListPool;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
@@ -456,7 +456,7 @@ public static partial class ULongExtensions
|
||||
{
|
||||
ulong result = 0UL;
|
||||
|
||||
GpuKernelLease lease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
||||
GpuKernelLease lease = GpuKernelPool.GetKernel();
|
||||
|
||||
Accelerator accelerator = lease.Accelerator;
|
||||
// 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);
|
||||
stream.Synchronize();
|
||||
|
||||
resultBuffer.View.CopyToCPU(ref result, 1);
|
||||
resultBuffer.View.CopyToCPU(stream, ref result, 1);
|
||||
|
||||
resultBuffer.Dispose();
|
||||
exponentBuffer.Dispose();
|
||||
|
||||
Reference in new issue
Block a user