Accelerator index added for dictionary-less use-cases

This commit is contained in:
mlemanczyk committed 2025-11-15 02:21:45 +01:00
1 parent 6cb63b6e73
commit 0540411cad
21 files changed
+158 -81

No files matched your search

+9 -3
View File
@@ -17,6 +17,7 @@ public sealed class DivisorCycleCache
private const byte ByteOne = 1; private const byte ByteOne = 1;
private const int StackBufferThreshold = 128; private const int StackBufferThreshold = 128;
private readonly Accelerator _accelerator; private readonly Accelerator _accelerator;
private readonly int _acceleratorIndex;
private readonly Action<AcceleratorStream, Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>> _gpuKernel; private readonly Action<AcceleratorStream, Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>> _gpuKernel;
private volatile ulong[] _snapshot; private volatile ulong[] _snapshot;
private volatile bool _useGpuGeneration = true; private volatile bool _useGpuGeneration = true;
@@ -38,10 +39,14 @@ public sealed class DivisorCycleCache
public int PreferredBatchSize => _divisorCyclesBatchSize; public int PreferredBatchSize => _divisorCyclesBatchSize;
// private static readonly Accelerator[] _accelerators;
private DivisorCycleCache(int divisorCyclesBatchSize) private DivisorCycleCache(int divisorCyclesBatchSize)
{ {
_divisorCyclesBatchSize = Math.Max(1, divisorCyclesBatchSize); _divisorCyclesBatchSize = Math.Max(1, divisorCyclesBatchSize);
var accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
_acceleratorIndex = acceleratorIndex;
Accelerator accelerator = AcceleratorPool.Shared.Accelerators[acceleratorIndex];
_accelerator = accelerator; _accelerator = accelerator;
_gpuKernel = LoadKernel(accelerator); _gpuKernel = LoadKernel(accelerator);
_snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot(); _snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
@@ -311,7 +316,8 @@ public sealed class DivisorCycleCache
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
int pending; int pending;
var kernel = _gpuKernel; var kernel = _gpuKernel;
var stream = AcceleratorStreamPool.Rent(_accelerator); var acceleratorIndex = _acceleratorIndex;
var stream = AcceleratorStreamPool.Rent(acceleratorIndex);
divisorBuffer!.View.CopyFromCPU(stream, divisors); divisorBuffer!.View.CopyFromCPU(stream, divisors);
powBuffer!.View.CopyFromCPU(stream, powSpan); powBuffer!.View.CopyFromCPU(stream, powSpan);
@@ -339,7 +345,7 @@ public sealed class DivisorCycleCache
resultBuffer.View.CopyToCPU(stream, resultSpan); resultBuffer.View.CopyToCPU(stream, resultSpan);
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
resultSpan.CopyTo(destination); resultSpan.CopyTo(destination);
if (rentedPow is not null) if (rentedPow is not null)
+7 -3
View File
@@ -11,7 +11,7 @@ namespace PerfectNumbers.Core.Gpu
private int _index; private int _index;
public Accelerator Rent() public int Rent()
{ {
Atomic.CompareExchange(ref _index, Capacity, 0); Atomic.CompareExchange(ref _index, Capacity, 0);
var index = Atomic.Add(ref _index, 1); var index = Atomic.Add(ref _index, 1);
@@ -20,27 +20,31 @@ namespace PerfectNumbers.Core.Gpu
index -= Capacity; index -= Capacity;
} }
return Accelerators[index]; return index;
} }
public void Return(Accelerator accelerator) public void Return(int acceleratorIndex)
{ {
// Intentionally left empty - there's nothing to do here. We're not really renting anything. // Intentionally left empty - there's nothing to do here. We're not really renting anything.
} }
public static readonly AcceleratorPool Shared = new(PerfectNumberConstants.RollingAccelerators); public static readonly AcceleratorPool Shared = new(PerfectNumberConstants.RollingAccelerators);
private readonly int Capacity; private readonly int Capacity;
public AcceleratorPool(int capacity) public AcceleratorPool(int capacity)
{ {
Console.WriteLine("Creating accelerators");
Capacity = capacity; Capacity = capacity;
var context = Context.CreateDefault(); var context = Context.CreateDefault();
var device = context.GetPreferredDevice(false); var device = context.GetPreferredDevice(false);
Context = context; Context = context;
Device = device; Device = device;
int acceleratorsInContext = 0; int acceleratorsInContext = 0;
int acceleratorIndex = 0;
Accelerators = [.. Enumerable.Range(0, capacity).Select(_ => Accelerators = [.. Enumerable.Range(0, capacity).Select(_ =>
{ {
Console.WriteLine($"Preparing accelerator {acceleratorIndex++}");
Accelerator accelerator = device.CreateAccelerator(context); Accelerator accelerator = device.CreateAccelerator(context);
acceleratorsInContext++; acceleratorsInContext++;
if (acceleratorsInContext >= 128) if (acceleratorsInContext >= 128)
@@ -5,38 +5,38 @@ namespace PerfectNumbers.Core.Gpu.Accelerators;
public static class AcceleratorStreamPool public static class AcceleratorStreamPool
{ {
private static readonly Dictionary<Accelerator, ConcurrentQueue<AcceleratorStream>> _streams = new(PerfectNumberConstants .RollingAccelerators << 1); private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
private static readonly SemaphoreSlim[] _locks = new SemaphoreSlim[PerfectNumberConstants.RollingAccelerators];
private static readonly ConcurrentQueue<AcceleratorStream>[] _streams = new ConcurrentQueue<AcceleratorStream> [PerfectNumberConstants.RollingAccelerators];
private static SemaphoreSlim CreateLock() => new(PerfectNumberConstants.ThreadsByStream); private static SemaphoreSlim CreateLock() => new(PerfectNumberConstants.ThreadsByAccelerator);
private static readonly Dictionary<Accelerator, SemaphoreSlim> _locks = new(PerfectNumberConstants.RollingAccelerators << 1);
// private static int _rented; // private static int _rented;
public static AcceleratorStream Rent(Accelerator accelerator) public static AcceleratorStream Rent(int acceleratorIndex)
{ {
var streamLock = _locks[accelerator]; var streamLock = _locks[acceleratorIndex];
var queue = _streams[accelerator]; var queue = _streams[acceleratorIndex];
streamLock.Wait(); streamLock.Wait();
return queue.TryDequeue(out var stream) return queue.TryDequeue(out var stream)
? stream ? stream
: accelerator.CreateStream(); : _accelerators[acceleratorIndex].CreateStream();
} }
public static void Return(AcceleratorStream stream) public static void Return(int acceleratorIndex, AcceleratorStream stream)
{ {
Accelerator accelerator = stream.Accelerator; var streamLock = _locks[acceleratorIndex];
var streamLock = _locks[accelerator]; var queue = _streams[acceleratorIndex];
var queue = _streams[accelerator];
queue.Enqueue(stream); queue.Enqueue(stream);
streamLock.Release(); streamLock.Release();
} }
public static void WarmUp(Accelerator accelerator) public static void WarmUp(int acceleratorIndex)
{ {
_streams.Add(accelerator, new()); _streams[acceleratorIndex] = new();
_ = _locks.TryAdd(accelerator, CreateLock()); _locks[acceleratorIndex] = CreateLock();
} }
} }
@@ -131,9 +131,12 @@ internal sealed class HeuristicPrimeTesterAccelerator
public HeuristicGpuDivisorTables HeuristicGpuTables => _heuristicDivisorTables; public HeuristicGpuDivisorTables HeuristicGpuTables => _heuristicDivisorTables;
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
internal HeuristicPrimeTesterAccelerator(int minBufferCapacity) internal HeuristicPrimeTesterAccelerator(int minBufferCapacity)
{ {
Accelerator accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
Accelerator accelerator = _accelerators[acceleratorIndex];
Context = accelerator.Context; Context = accelerator.Context;
Accelerator = accelerator; Accelerator = accelerator;
@@ -141,7 +144,7 @@ internal sealed class HeuristicPrimeTesterAccelerator
HeuristicTrialDivisionKernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables>(PrimeTesterKernels.HeuristicTrialDivisionKernel)); HeuristicTrialDivisionKernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables>(PrimeTesterKernels.HeuristicTrialDivisionKernel));
AcceleratorStream stream = AcceleratorStreamPool.Rent(accelerator); AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
// GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream); // GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream);
@@ -149,7 +152,7 @@ internal sealed class HeuristicPrimeTesterAccelerator
_heuristicDivisorTables = _sharedTables.CreateHeuristicDivisorTables(); _heuristicDivisorTables = _sharedTables.CreateHeuristicDivisorTables();
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
} }
public void EnsureCapacity(int minCapacity) public void EnsureCapacity(int minCapacity)
@@ -6,13 +6,14 @@ namespace PerfectNumbers.Core.Gpu.Accelerators;
internal sealed class LastDigitGpuTables internal sealed class LastDigitGpuTables
{ {
private static readonly Dictionary<Accelerator, LastDigitGpuTables> _sharedTables = new(PerfectNumberConstants.RollingAccelerators); private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
private static readonly LastDigitGpuTables[] _sharedTables = new LastDigitGpuTables[PerfectNumberConstants.RollingAccelerators];
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static LastDigitGpuTables EnsureStaticTables(Accelerator accelerator) => _sharedTables[accelerator]; internal static LastDigitGpuTables EnsureStaticTables(int acceleratorIndex) => _sharedTables[acceleratorIndex];
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void WarmUp(Accelerator accelerator, AcceleratorStream stream) => _sharedTables[accelerator] = new LastDigitGpuTables(accelerator, stream); internal static void WarmUp(int acceleratorIndex, AcceleratorStream stream) => _sharedTables[acceleratorIndex] = new LastDigitGpuTables(_accelerators[acceleratorIndex], stream);
internal LastDigitGpuTables(Accelerator accelerator, AcceleratorStream stream) internal LastDigitGpuTables(Accelerator accelerator, AcceleratorStream stream)
{ {
@@ -1,7 +1,6 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using ILGPU; using ILGPU;
using ILGPU.Runtime; using ILGPU.Runtime;
using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Kernels; using PerfectNumbers.Core.Gpu.Kernels;
namespace PerfectNumbers.Core.Gpu.Accelerators; namespace PerfectNumbers.Core.Gpu.Accelerators;
@@ -19,8 +18,8 @@ public sealed class PrimeOrderCalculatorAccelerator
var pool = _pool ??= []; var pool = _pool ??= [];
if (!pool.TryDequeue(out var gpu)) if (!pool.TryDequeue(out var gpu))
{ {
Accelerator accelerator = AcceleratorPool.Shared.Rent(); int acceleratorIndex = AcceleratorPool.Shared.Rent();
gpu = new(accelerator, PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity, PerfectNumberConstants.DefaultSmallPrimeFactorSlotCount, PerfectNumberConstants.DefaultSpecialMaxFactorCapacity); gpu = new(acceleratorIndex, PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity, PerfectNumberConstants.DefaultSmallPrimeFactorSlotCount, PerfectNumberConstants.DefaultSpecialMaxFactorCapacity);
} }
else else
{ {
@@ -65,22 +64,26 @@ public sealed class PrimeOrderCalculatorAccelerator
{ {
Console.WriteLine($"Preparing accelerator {i}..."); Console.WriteLine($"Preparing accelerator {i}...");
var accelerator = accelerators[i]; var accelerator = accelerators[i];
AcceleratorStreamPool.WarmUp(accelerator); AcceleratorStreamPool.WarmUp(i);
// Don't take this from the pool as quick uploads of data to the accelerator consumes much of GPU's memory and throws. // Don't take this from the pool as quick uploads of data to the accelerator consumes much of GPU's memory and throws.
AcceleratorStream stream = accelerator.CreateStream(); AcceleratorStream stream = accelerator.CreateStream();
LastDigitGpuTables.WarmUp(accelerator, stream); LastDigitGpuTables.WarmUp(i, stream);
// SharedHeuristicGpuTables.EnsureStaticTables(accelerator, stream); // SharedHeuristicGpuTables.EnsureStaticTables(accelerator, stream);
// _ = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.None); // _ = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.None);
// KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream); // KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream);
// GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream); // GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream);
stream.Synchronize(); stream.Synchronize();
stream.Dispose(); stream.Dispose();
accelerator.Synchronize();
} }
} }
#endregion #endregion
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
public readonly Accelerator Accelerator; public readonly Accelerator Accelerator;
public readonly int AcceleratorIndex;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> Input; public readonly MemoryBuffer1D<ulong, Stride1D.Dense> Input;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> OutputUlong; public readonly MemoryBuffer1D<ulong, Stride1D.Dense> OutputUlong;
public MemoryBuffer1D<ulong, Stride1D.Dense> PrimeTestInput; public MemoryBuffer1D<ulong, Stride1D.Dense> PrimeTestInput;
@@ -161,9 +164,11 @@ public sealed class PrimeOrderCalculatorAccelerator
} }
} }
public PrimeOrderCalculatorAccelerator(Accelerator accelerator, int factorsCount, int primeTesterCapacity, int smallPrimeFactorSlotCount, int specialMaxFactorCapacity) public PrimeOrderCalculatorAccelerator(int acceleratorIndex, int factorsCount, int primeTesterCapacity, int smallPrimeFactorSlotCount, int specialMaxFactorCapacity)
{ {
var accelerator = _accelerators[acceleratorIndex];
Accelerator = accelerator; Accelerator = accelerator;
AcceleratorIndex = acceleratorIndex;
Input = accelerator.Allocate1D<ulong>(1); Input = accelerator.Allocate1D<ulong>(1);
OutputUlong = accelerator.Allocate1D<ulong>(1); OutputUlong = accelerator.Allocate1D<ulong>(1);
Pow2ModEntriesToTestOnDevice = accelerator.Allocate1D<KeyValuePair<ulong, int>>(factorsCount); Pow2ModEntriesToTestOnDevice = accelerator.Allocate1D<KeyValuePair<ulong, int>>(factorsCount);
@@ -179,7 +184,7 @@ public sealed class PrimeOrderCalculatorAccelerator
SpecialMaxCandidates = accelerator.Allocate1D<ulong>(specialMaxFactorCapacity); SpecialMaxCandidates = accelerator.Allocate1D<ulong>(specialMaxFactorCapacity);
SpecialMaxResult = accelerator.Allocate1D<ulong>(1); SpecialMaxResult = accelerator.Allocate1D<ulong>(1);
var sharedTables = LastDigitGpuTables.EnsureStaticTables(accelerator); var sharedTables = LastDigitGpuTables.EnsureStaticTables(acceleratorIndex);
DevicePrimesLastOne = sharedTables.DevicePrimesLastOne; DevicePrimesLastOne = sharedTables.DevicePrimesLastOne;
DevicePrimesLastSeven = sharedTables.DevicePrimesLastSeven; DevicePrimesLastSeven = sharedTables.DevicePrimesLastSeven;
@@ -71,6 +71,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
// _isConfigured = true; // _isConfigured = true;
} }
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
public bool IsPrime(ulong prime, out bool divisorsExhausted) public bool IsPrime(ulong prime, out bool divisorsExhausted)
{ {
ulong allowedMax; ulong allowedMax;
@@ -104,7 +106,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
ulong lastProcessed; ulong lastProcessed;
// GpuPrimeWorkLimiter(); // GpuPrimeWorkLimiter();
var accelerator = AcceleratorPool.Shared.Rent(); int acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
var stream = accelerator.CreateStream(); var stream = accelerator.CreateStream();
// Monitor.Enter(gpuLease.ExecutionLock); // Monitor.Enter(gpuLease.ExecutionLock);
@@ -492,6 +495,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
public sealed class DivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession public sealed class DivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
{ {
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
private readonly MersenneNumberDivisorByDivisorGpuTester _owner; private readonly MersenneNumberDivisorByDivisorGpuTester _owner;
private readonly Accelerator _accelerator; private readonly Accelerator _accelerator;
private readonly AcceleratorStream _stream; private readonly AcceleratorStream _stream;
@@ -506,11 +511,13 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
private ulong[] _hostBuffer = null!; private ulong[] _hostBuffer = null!;
private int _capacity; private int _capacity;
internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner) internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner)
{ {
_owner = owner; _owner = owner;
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
Accelerator accelerator = AcceleratorPool.Shared.Rent(); int acceleratorIndex = AcceleratorPool.Shared.Rent();
Accelerator accelerator = _accelerators[acceleratorIndex];
_accelerator = accelerator; _accelerator = accelerator;
_stream = accelerator.CreateStream(); _stream = accelerator.CreateStream();
_capacity = Math.Max(1, owner._gpuBatchSize); _capacity = Math.Max(1, owner._gpuBatchSize);
@@ -54,10 +54,13 @@ public sealed class MersenneNumberDivisorGpuTester
_divisorCandidates = count == 0 ? [] : list[..count]; _divisorCandidates = count == 0 ? [] : list[..count];
} }
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
public bool IsDivisible(ulong exponent, in ReadOnlyGpuUInt128 divisor) public bool IsDivisible(ulong exponent, in ReadOnlyGpuUInt128 divisor)
{ {
// var accelerator = SharedGpuContext.CreateAccelerator(); // var accelerator = SharedGpuContext.CreateAccelerator();
var accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
var stream = accelerator.CreateStream(); var stream = accelerator.CreateStream();
Queue<MemoryBuffer1D<byte, Stride1D.Dense>> resultBuffers = GetQueue(accelerator); Queue<MemoryBuffer1D<byte, Stride1D.Dense>> resultBuffers = GetQueue(accelerator);
if (!resultBuffers.TryDequeue(out var resultBuffer)) if (!resultBuffers.TryDequeue(out var resultBuffer))
@@ -12,12 +12,15 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
private readonly GpuKernelType _kernelType = kernelType; private readonly GpuKernelType _kernelType = kernelType;
private readonly bool _useGpuOrder = useGpuOrder; private readonly bool _useGpuOrder = useGpuOrder;
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
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)
{ {
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}).");
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
var accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
var stream = accelerator.CreateStream(); var stream = accelerator.CreateStream();
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy
UInt128 kStart = 1UL; UInt128 kStart = 1UL;
@@ -160,7 +160,8 @@ public class MersenneNumberLucasLehmerGpuTester
else else
{ {
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
var accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
var stream = accelerator.CreateStream(); var stream = accelerator.CreateStream();
var modulus = new GpuUInt128(((UInt128)1 << (int)exponent) - 1UL); // TODO: Cache these Mersenne moduli per exponent so LL GPU runs skip rebuilding them every launch. var modulus = new GpuUInt128(((UInt128)1 << (int)exponent) - 1UL); // TODO: Cache these Mersenne moduli per exponent so LL GPU runs skip rebuilding them every launch.
var buffer = accelerator.Allocate1D<GpuUInt128>(1); var buffer = accelerator.Allocate1D<GpuUInt128>(1);
@@ -193,7 +194,8 @@ public class MersenneNumberLucasLehmerGpuTester
} }
} }
var accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
var stream = accelerator.CreateStream(); var stream = accelerator.CreateStream();
var expBuffer = accelerator.Allocate1D<ulong>(count); var expBuffer = accelerator.Allocate1D<ulong>(count);
@@ -246,9 +248,12 @@ public class MersenneNumberLucasLehmerGpuTester
gpuUInt128Pool.Return(buffer); gpuUInt128Pool.Return(buffer);
} }
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
private bool IsMersennePrimeNtt(ulong exponent, GpuUInt128 nttMod, GpuUInt128 primitiveRoot, bool runOnGpu) private bool IsMersennePrimeNtt(ulong exponent, GpuUInt128 nttMod, GpuUInt128 primitiveRoot, bool runOnGpu)
{ {
var accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
var stream = accelerator.CreateStream(); var stream = accelerator.CreateStream();
var subKernel = GetSubSmallKernel(accelerator); var subKernel = GetSubSmallKernel(accelerator);
@@ -7,11 +7,14 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
private readonly GpuKernelType _kernelType = kernelType; private readonly GpuKernelType _kernelType = kernelType;
private readonly bool _useGpuOrder = useGpuOrder; private readonly bool _useGpuOrder = useGpuOrder;
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
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)
{ {
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
// var accelerator = SharedGpuContext.Accelerator; // var accelerator = SharedGpuContext.Accelerator;
var accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
var stream = accelerator.CreateStream(); var stream = accelerator.CreateStream();
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR
UInt128 kStart = UInt128.One; UInt128 kStart = UInt128.One;
@@ -22,11 +22,14 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
// TODO: When cycles are missing for larger q values, compute only the single required cycle on the device // TODO: When cycles are missing for larger q values, compute only the single required cycle on the device
// requested by the caller, skip queuing additional block generation, and keep the snapshot cache untouched. // requested by the caller, skip queuing additional block generation, and keep the snapshot cache untouched.
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
// GPU residue variant: check 2^p % q == 1 for q = 2*p*k + 1. // GPU residue variant: check 2^p % q == 1 for q = 2*p*k + 1.
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)
{ {
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
var accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
// Monitor.Enter(gpuLease.ExecutionLock); // Monitor.Enter(gpuLease.ExecutionLock);
+14 -6
View File
@@ -574,7 +574,8 @@ public static class NttGpuMath
{ {
int n = values.Length; int n = values.Length;
int bits = (int)Math.Log2(n); int bits = (int)Math.Log2(n);
var accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
var pool = ThreadStaticPools.GpuUInt128Pool; var pool = ThreadStaticPools.GpuUInt128Pool;
var array = pool.Rent(n); var array = pool.Rent(n);
values.CopyTo(array); values.CopyTo(array);
@@ -636,7 +637,8 @@ public static class NttGpuMath
private static void ForwardGpuReference(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot) private static void ForwardGpuReference(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
{ {
int n = (int)values.Length; int n = (int)values.Length;
var accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
var stream = accelerator.CreateStream(); var stream = accelerator.CreateStream();
var pool = ThreadStaticPools.GpuUInt128Pool; var pool = ThreadStaticPools.GpuUInt128Pool;
var inputArray = pool.Rent(n); var inputArray = pool.Rent(n);
@@ -732,7 +734,8 @@ public static class NttGpuMath
private static void InverseGpuReference(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot) private static void InverseGpuReference(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
{ {
int n = values.Length; int n = values.Length;
var accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
var stream = accelerator.CreateStream(); var stream = accelerator.CreateStream();
var pool = ThreadStaticPools.GpuUInt128Pool; var pool = ThreadStaticPools.GpuUInt128Pool;
var inputArray = pool.Rent(n); var inputArray = pool.Rent(n);
@@ -780,7 +783,8 @@ public static class NttGpuMath
private static void ForwardGpuStaged(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot) private static void ForwardGpuStaged(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
{ {
int n = values.Length; int n = values.Length;
var accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
var stream = accelerator.CreateStream(); var stream = accelerator.CreateStream();
var buffer = accelerator.Allocate1D<GpuUInt128>(n); var buffer = accelerator.Allocate1D<GpuUInt128>(n);
buffer.View.CopyFromCPU(stream, ref values[0], n); buffer.View.CopyFromCPU(stream, ref values[0], n);
@@ -795,7 +799,8 @@ public static class NttGpuMath
private static void InverseGpuStaged(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot) private static void InverseGpuStaged(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
{ {
int n = values.Length; int n = values.Length;
var accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
var stream = accelerator.CreateStream(); var stream = accelerator.CreateStream();
var buffer = accelerator.Allocate1D<GpuUInt128>(n); var buffer = accelerator.Allocate1D<GpuUInt128>(n);
buffer.View.CopyFromCPU(stream, ref values[0], n); buffer.View.CopyFromCPU(stream, ref values[0], n);
@@ -998,9 +1003,12 @@ public static class NttGpuMath
InverseDevice(accelerator, stream, values, (int)values.Length, modulus, cache); InverseDevice(accelerator, stream, values, (int)values.Length, modulus, cache);
} }
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
public static void PointwiseMultiply(Span<GpuUInt128> left, Span<GpuUInt128> right, GpuUInt128 modulus) public static void PointwiseMultiply(Span<GpuUInt128> left, Span<GpuUInt128> right, GpuUInt128 modulus)
{ {
var accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
var stream = accelerator.CreateStream(); var stream = accelerator.CreateStream();
var pool = ThreadStaticPools.GpuUInt128Pool; var pool = ThreadStaticPools.GpuUInt128Pool;
var leftArray = pool.Rent(left.Length); var leftArray = pool.Rent(left.Length);
@@ -14,12 +14,13 @@ public static class Pow2MontgomeryGpuCalculator
ulong result = 0UL; ulong result = 0UL;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
int acceleratorIndex = gpu.AcceleratorIndex;
Accelerator? accelerator = gpu.Accelerator; Accelerator? accelerator = gpu.Accelerator;
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input; MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input;
MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.OutputUlong; MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.OutputUlong;
var kernel = gpu.KeepMontgomeryKernel; var kernel = gpu.KeepMontgomeryKernel;
AcceleratorStream stream = AcceleratorStreamPool.Rent(accelerator); AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1); exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1);
// We don't need to worry about any left-overs here. // We don't need to worry about any left-overs here.
@@ -31,7 +32,7 @@ public static class Pow2MontgomeryGpuCalculator
resultBuffer.View.CopyToCPU(stream, ref result, 1); resultBuffer.View.CopyToCPU(stream, ref result, 1);
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
// GpuPrimeWorkLimiter.Release(); // GpuPrimeWorkLimiter.Release();
PrimeOrderCalculatorAccelerator.Return(gpu); PrimeOrderCalculatorAccelerator.Return(gpu);
@@ -48,7 +49,8 @@ public static class Pow2MontgomeryGpuCalculator
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input; MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input;
MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.OutputUlong; MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.OutputUlong;
AcceleratorStream stream = AcceleratorStreamPool.Rent(gpu.Accelerator); int acceleratorIndex = gpu.AcceleratorIndex;
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1); exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1);
// We don't need to worry about any left-overs here. // We don't need to worry about any left-overs here.
// resultBuffer.MemSetToZero(stream); // resultBuffer.MemSetToZero(stream);
@@ -58,7 +60,7 @@ public static class Pow2MontgomeryGpuCalculator
resultBuffer.View.CopyToCPU(stream, ref result, 1); resultBuffer.View.CopyToCPU(stream, ref result, 1);
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
// GpuPrimeWorkLimiter.Release(); // GpuPrimeWorkLimiter.Release();
PrimeOrderCalculatorAccelerator.Return(gpu); PrimeOrderCalculatorAccelerator.Return(gpu);
@@ -73,7 +75,8 @@ public static class Pow2MontgomeryGpuCalculator
MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.OutputUlong; MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.OutputUlong;
var kernel = gpu.ConvertToStandardKernel; var kernel = gpu.ConvertToStandardKernel;
AcceleratorStream stream = AcceleratorStreamPool.Rent(gpu.Accelerator); int acceleratorIndex = gpu.AcceleratorIndex;
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1); exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1);
// We don't need to worry about any left-overs here. // We don't need to worry about any left-overs here.
// resultBuffer.MemSetToZero(stream); // resultBuffer.MemSetToZero(stream);
@@ -83,7 +86,7 @@ public static class Pow2MontgomeryGpuCalculator
resultBuffer.View.CopyToCPU(stream, ref result, 1); resultBuffer.View.CopyToCPU(stream, ref result, 1);
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
return result; return result;
} }
@@ -205,6 +205,8 @@ internal static partial class PrimeOrderGpuHeuristics
return true; return true;
} }
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
private static bool TryLaunchPartialFactorKernel( private static bool TryLaunchPartialFactorKernel(
PrimeOrderCalculatorAccelerator gpu, PrimeOrderCalculatorAccelerator gpu,
ulong value, ulong value,
@@ -220,7 +222,8 @@ internal static partial class PrimeOrderGpuHeuristics
try try
{ {
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
Accelerator accelerator = AcceleratorPool.Shared.Rent(); int acceleratorIndex = AcceleratorPool.Shared.Rent();
Accelerator accelerator = _accelerators[acceleratorIndex];
AcceleratorStream stream = accelerator.CreateStream(); AcceleratorStream stream = accelerator.CreateStream();
SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator, stream); SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator, stream);
@@ -382,8 +385,9 @@ internal static partial class PrimeOrderGpuHeuristics
order = 0UL; order = 0UL;
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
Accelerator accelerator = AcceleratorPool.Shared.Rent(); int acceleratorIndex = AcceleratorPool.Shared.Rent();
AcceleratorStream stream = AcceleratorStreamPool.Rent(accelerator); Accelerator accelerator = _accelerators[acceleratorIndex];
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator, stream); SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator, stream);
// TODO: These buffers should be created reallocated once and assigned to an accelerator. Callers should use their own // TODO: These buffers should be created reallocated once and assigned to an accelerator. Callers should use their own
@@ -553,7 +557,8 @@ 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)
{ {
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
Accelerator accelerator = AcceleratorPool.Shared.Rent(); int acceleratorIndex = AcceleratorPool.Shared.Rent();
Accelerator accelerator = _accelerators[acceleratorIndex];
AcceleratorStream? stream = accelerator.CreateStream(); AcceleratorStream? stream = accelerator.CreateStream();
// TODO: These buffers should be allocated once per accelerator and only reallocated when the new length exceeds capacity. // TODO: These buffers should be allocated once per accelerator and only reallocated when the new length exceeds capacity.
// Modify the callers to use their own pool of buffers per accelerator, so that other threads don't use the accelerators // Modify the callers to use their own pool of buffers per accelerator, so that other threads don't use the accelerators
@@ -630,19 +635,20 @@ internal static partial class PrimeOrderGpuHeuristics
GpuUInt128 modulus = (GpuUInt128)prime; GpuUInt128 modulus = (GpuUInt128)prime;
gpu = PrimeOrderCalculatorAccelerator.Rent(1); gpu = PrimeOrderCalculatorAccelerator.Rent(1);
int acceleratorIndex = gpu.AcceleratorIndex;
Accelerator accelerator = gpu.Accelerator; Accelerator accelerator = gpu.Accelerator;
MemoryBuffer1D<GpuUInt128, Stride1D.Dense> exponentBuffer = accelerator.Allocate1D<GpuUInt128>(length); MemoryBuffer1D<GpuUInt128, Stride1D.Dense> exponentBuffer = accelerator.Allocate1D<GpuUInt128>(length);
MemoryBuffer1D<GpuUInt128, Stride1D.Dense> remainderBuffer = accelerator.Allocate1D<GpuUInt128>(length); MemoryBuffer1D<GpuUInt128, Stride1D.Dense> remainderBuffer = accelerator.Allocate1D<GpuUInt128>(length);
AcceleratorStream? stream = AcceleratorStreamPool.Rent(accelerator); AcceleratorStream? stream = AcceleratorStreamPool.Rent(acceleratorIndex);
exponentBuffer.View.CopyFromCPU(stream, exponentSpan); exponentBuffer.View.CopyFromCPU(stream, exponentSpan);
gpu.Pow2ModWideKernel.Launch(stream, length, exponentBuffer.View, modulus, remainderBuffer.View); gpu.Pow2ModWideKernel.Launch(stream, length, exponentBuffer.View, modulus, remainderBuffer.View);
remainderBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(resultSpan), length); remainderBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(resultSpan), length);
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
{ {
results[i] = (UInt128)resultSpan[i]; results[i] = (UInt128)resultSpan[i];
+4 -1
View File
@@ -791,6 +791,8 @@ public class MersenneDivisorCycles
[ThreadStatic] [ThreadStatic]
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>? _divisorCycleKernel; private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>? _divisorCycleKernel;
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
public static void GenerateGpu(string path, ulong maxDivisor, int batchSize = 1_000_000, long skipCount = 0L, long nextPosition = 0L) public static void GenerateGpu(string path, ulong maxDivisor, int batchSize = 1_000_000, long skipCount = 0L, long nextPosition = 0L)
{ {
// Prepare output file with header // Prepare output file with header
@@ -805,7 +807,8 @@ public class MersenneDivisorCycles
divisors = pool.Rent(count); divisors = pool.Rent(count);
outCycles = pool.Rent(count); outCycles = pool.Rent(count);
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
var accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
var stream = accelerator.CreateStream(); var stream = accelerator.CreateStream();
using Stream outputStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, BufferSize10M, useAsync: true); using Stream outputStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, BufferSize10M, useAsync: true);
+4 -1
View File
@@ -49,6 +49,8 @@ public sealed class MersenneNumberTester(
// TODO: Ensure Program passes useResidue correctly and that residue-vs-incremental-vs-LL selection respects CLI flags. // TODO: Ensure Program passes useResidue correctly and that residue-vs-incremental-vs-LL selection respects CLI flags.
// Also consider injecting a shared cycles cache (MersenneDivisorCycles.Shared) to both CPU and GPU testers. // Also consider injecting a shared cycles cache (MersenneDivisorCycles.Shared) to both CPU and GPU testers.
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
public void WarmUpOrders(ulong exponent, ulong limit = 5_000_000UL) public void WarmUpOrders(ulong exponent, ulong limit = 5_000_000UL)
{ {
bool cacheEnabled = useOrderCache; bool cacheEnabled = useOrderCache;
@@ -148,7 +150,8 @@ public sealed class MersenneNumberTester(
UInt128[] qs = qsBuffer; UInt128[] qs = qsBuffer;
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
var accelerator = AcceleratorPool.Shared.Rent(); var acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
var stream = accelerator.CreateStream(); var stream = accelerator.CreateStream();
var orderKernel = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.OrderKernelScan).Order!; var orderKernel = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.OrderKernelScan).Order!;
@@ -14,8 +14,8 @@ public static class PerfectNumberConstants
public static readonly uint PrimesLimit = 1_000_000; //(ulong)Array.MaxLength;// 1_000_000; public static readonly uint PrimesLimit = 1_000_000; //(ulong)Array.MaxLength;// 1_000_000;
public const int MaxOddPowersCount = 128; public const int MaxOddPowersCount = 128;
public const int PooledArrayThreshold = 64; public const int PooledArrayThreshold = 64;
public static readonly int RollingAccelerators = 256; //SharedGpuContext.Device.MaxNumThreadsPerGroup; public static readonly int RollingAccelerators = 298; //SharedGpuContext.Device.MaxNumThreadsPerGroup;
public const int ThreadsByStream = 1; public const int ThreadsByAccelerator = 4096;
// TODO: Load these limits from the benchmark-driven configuration so CPU and GPU scans stay aligned with the optimal // TODO: Load these limits from the benchmark-driven configuration so CPU and GPU scans stay aligned with the optimal
// divisor-cycle datasets we generate offline. // divisor-cycle datasets we generate offline.
// TODO: Promote these magic numbers into a runtime profile derived from EvenPerfectBitScanner.Benchmarks so we can retune // TODO: Promote these magic numbers into a runtime profile derived from EvenPerfectBitScanner.Benchmarks so we can retune
@@ -65,7 +65,10 @@ internal static partial class PrimeOrderCalculator
ulong result; ulong result;
if (phiFactors.Factors is null) if (phiFactors.Factors is null)
{ {
result = CalculateByFactorizationGpu(gpu, prime, divisorData); result = prime <= PerfectNumberConstants.MaxQForDivisorCycles
? CalculateByFactorizationGpu(gpu, prime, divisorData)
: CalculateByFactorizationCpu(gpu, prime, divisorData);
phiFactors.Dispose(); phiFactors.Dispose();
PrimeOrderCalculatorAccelerator.Return(gpu); PrimeOrderCalculatorAccelerator.Return(gpu);
return result; return result;
@@ -36,11 +36,12 @@ internal static partial class PrimeOrderCalculator
entries.Sort(entries, static (a, b) => a.Key.CompareTo(b.Key)); entries.Sort(entries, static (a, b) => a.Key.CompareTo(b.Key));
ulong order = phi; ulong order = phi;
var acceleratorIndex = gpu.AcceleratorIndex;
var kernel = gpu.CheckFactorsKernel; var kernel = gpu.CheckFactorsKernel;
gpu.EnsureCapacity(entryCount, 1); gpu.EnsureCapacity(entryCount, 1);
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
AcceleratorStream stream = AcceleratorStreamPool.Rent(gpu.Accelerator); AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
gpu.Pow2ModEntriesToTestOnDevice.View.CopyFromCPU(stream, entries); gpu.Pow2ModEntriesToTestOnDevice.View.CopyFromCPU(stream, entries);
var kernelLauncher = kernel.CreateLauncherDelegate<Action<AcceleratorStream, int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>(); var kernelLauncher = kernel.CreateLauncherDelegate<Action<AcceleratorStream, int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
@@ -49,7 +50,7 @@ internal static partial class PrimeOrderCalculator
gpu.OutputUlong.View.CopyToCPU(stream, ref order, 1); gpu.OutputUlong.View.CopyToCPU(stream, ref order, 1);
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
// GpuPrimeWorkLimiter.Release(); // GpuPrimeWorkLimiter.Release();
return order; return order;
@@ -64,10 +65,11 @@ internal static partial class PrimeOrderCalculator
remaining = value; remaining = value;
gpu.EnsureSmallPrimeFactorSlotsCapacity(GpuSmallPrimeFactorSlots); gpu.EnsureSmallPrimeFactorSlotsCapacity(GpuSmallPrimeFactorSlots);
int acceleratorIndex = gpu.AcceleratorIndex;
var accelerator = gpu.Accelerator; var accelerator = gpu.Accelerator;
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
var stream = AcceleratorStreamPool.Rent(accelerator); var stream = AcceleratorStreamPool.Rent(acceleratorIndex);
KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.SmallPrimeFactorKernelScan); KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.SmallPrimeFactorKernelScan);
SmallPrimeFactorViews tables = GpuKernelPool.GetSmallPrimeFactorTables(kernels); SmallPrimeFactorViews tables = GpuKernelPool.GetSmallPrimeFactorTables(kernels);
@@ -112,7 +114,7 @@ internal static partial class PrimeOrderCalculator
smallPrimeFactorRemainingSlotView.CopyToCPU(stream, ref remaining, 1); smallPrimeFactorRemainingSlotView.CopyToCPU(stream, ref remaining, 1);
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
// GpuPrimeWorkLimiter.Release(); // GpuPrimeWorkLimiter.Release();
for (int i = 0; i < factorCount; i++) for (int i = 0; i < factorCount; i++)
@@ -135,9 +137,10 @@ internal static partial class PrimeOrderCalculator
int factorCount = factors.Length; int factorCount = factors.Length;
gpu.EnsureSpecialMaxFactorsCapacity(factorCount); gpu.EnsureSpecialMaxFactorsCapacity(factorCount);
int acceleratorIndex = gpu.AcceleratorIndex;
Accelerator accelerator = gpu.Accelerator; Accelerator accelerator = gpu.Accelerator;
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
var stream = AcceleratorStreamPool.Rent(accelerator); var stream = AcceleratorStreamPool.Rent(acceleratorIndex);
var kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.EvaluateSpecialMaxCandidatesKernel); var kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.EvaluateSpecialMaxCandidatesKernel);
ArrayView1D<ulong, Stride1D.Dense> specialMaxFactorsView = gpu.SpecialMaxFactors.View; ArrayView1D<ulong, Stride1D.Dense> specialMaxFactorsView = gpu.SpecialMaxFactors.View;
@@ -161,7 +164,7 @@ internal static partial class PrimeOrderCalculator
specialMaxResultView.CopyToCPU(stream, ref result, 1); specialMaxResultView.CopyToCPU(stream, ref result, 1);
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
// GpuPrimeWorkLimiter.Release(); // GpuPrimeWorkLimiter.Release();
return result != 0; return result != 0;
} }
@@ -174,7 +177,8 @@ internal static partial class PrimeOrderCalculator
Span<byte> factoredSpan = stackalloc byte[1]; Span<byte> factoredSpan = stackalloc byte[1];
Span<ulong> factorSpan = stackalloc ulong[1]; Span<ulong> factorSpan = stackalloc ulong[1];
AcceleratorStream stream = AcceleratorStreamPool.Rent(gpu.Accelerator); int acceleratorIndex = gpu.AcceleratorIndex;
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
gpu.Input.View.CopyFromCPU(stream, randomStateSpan); gpu.Input.View.CopyFromCPU(stream, randomStateSpan);
var kernelLauncher = gpu.PollardRhoKernel.CreateLauncherDelegate<Action<AcceleratorStream, ulong, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>(); var kernelLauncher = gpu.PollardRhoKernel.CreateLauncherDelegate<Action<AcceleratorStream, ulong, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
@@ -186,7 +190,7 @@ internal static partial class PrimeOrderCalculator
gpu.Input.View.CopyToCPU(stream, randomStateSpan); gpu.Input.View.CopyToCPU(stream, randomStateSpan);
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
ThreadStaticDeterministicRandomGpu.Exclusive.SetState(randomStateSpan[0]); ThreadStaticDeterministicRandomGpu.Exclusive.SetState(randomStateSpan[0]);
bool factored = factoredSpan[0] != 0; bool factored = factoredSpan[0] != 0;
+12 -8
View File
@@ -95,12 +95,13 @@ public sealed class PrimeTester
byte flag = 0; byte flag = 0;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
int acceleratorIndex = gpu.AcceleratorIndex;
var inputView = gpu.PrimeTestInput.View; var inputView = gpu.PrimeTestInput.View;
var outputView = gpu.OutputByte.View; var outputView = gpu.OutputByte.View;
var kernel = gpu.SmallPrimeSieveKernel!; var kernel = gpu.SmallPrimeSieveKernel!;
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
AcceleratorStream stream = AcceleratorStreamPool.Rent(gpu.Accelerator); AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
inputView.CopyFromCPU(stream, ref n, 1); inputView.CopyFromCPU(stream, ref n, 1);
var kernelLauncher = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>(); var kernelLauncher = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>();
@@ -122,7 +123,7 @@ public sealed class PrimeTester
outputView.CopyToCPU(stream, ref flag, 1); outputView.CopyToCPU(stream, ref flag, 1);
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
PrimeOrderCalculatorAccelerator.Return(gpu); PrimeOrderCalculatorAccelerator.Return(gpu);
// GpuPrimeWorkLimiter.Release(); // GpuPrimeWorkLimiter.Release();
@@ -139,7 +140,8 @@ public sealed class PrimeTester
var outputView = output.View; var outputView = output.View;
Kernel kernel = gpu.SmallPrimeSieveKernel!; Kernel kernel = gpu.SmallPrimeSieveKernel!;
AcceleratorStream stream = AcceleratorStreamPool.Rent(gpu.Accelerator); int acceleratorIndex = gpu.AcceleratorIndex;
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
inputView.CopyFromCPU(stream, ref n, 1); inputView.CopyFromCPU(stream, ref n, 1);
var kernelLauncher = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>(); var kernelLauncher = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>();
@@ -160,7 +162,7 @@ public sealed class PrimeTester
outputView.CopyToCPU(stream, ref flag, 1); outputView.CopyToCPU(stream, ref flag, 1);
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
return flag != 0; return flag != 0;
} }
@@ -195,6 +197,7 @@ public sealed class PrimeTester
{ {
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
var gpu = PrimeOrderCalculatorAccelerator.Rent(GpuBatchSize); var gpu = PrimeOrderCalculatorAccelerator.Rent(GpuBatchSize);
int acceleratorIndex = gpu.AcceleratorIndex;
var accelerator = gpu.Accelerator; var accelerator = gpu.Accelerator;
var kernel = gpu.SmallPrimeSieveKernel!; var kernel = gpu.SmallPrimeSieveKernel!;
int totalLength = values.Length; int totalLength = values.Length;
@@ -206,7 +209,7 @@ public sealed class PrimeTester
var outputView = output.View; var outputView = output.View;
int pos = 0; int pos = 0;
AcceleratorStream stream = AcceleratorStreamPool.Rent(accelerator); AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
while (pos < totalLength) while (pos < totalLength)
{ {
int remaining = totalLength - pos; int remaining = totalLength - pos;
@@ -236,7 +239,7 @@ public sealed class PrimeTester
} }
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
PrimeOrderCalculatorAccelerator.Return(gpu); PrimeOrderCalculatorAccelerator.Return(gpu);
// GpuPrimeWorkLimiter.Release(); // GpuPrimeWorkLimiter.Release();
} }
@@ -280,13 +283,14 @@ public sealed class PrimeTester
int length = values.Length; int length = values.Length;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
int acceleratorIndex = gpu.AcceleratorIndex;
var accelerator = gpu.Accelerator; var accelerator = gpu.Accelerator;
var kernel = gpu.SharesFactorKernel; var kernel = gpu.SharesFactorKernel;
gpu.EnsureCapacity(0, length); gpu.EnsureCapacity(0, length);
MemoryBuffer1D<ulong, Stride1D.Dense>? inputBuffer = gpu.PrimeTestInput; MemoryBuffer1D<ulong, Stride1D.Dense>? inputBuffer = gpu.PrimeTestInput;
MemoryBuffer1D<byte, Stride1D.Dense>? resultBuffer = gpu.OutputByte; MemoryBuffer1D<byte, Stride1D.Dense>? resultBuffer = gpu.OutputByte;
AcceleratorStream stream = AcceleratorStreamPool.Rent(accelerator); AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
inputBuffer.View.CopyFromCPU(stream, values); inputBuffer.View.CopyFromCPU(stream, values);
kernel.Launch(stream, length, inputBuffer.View, resultBuffer.View); kernel.Launch(stream, length, inputBuffer.View, resultBuffer.View);
@@ -294,7 +298,7 @@ public sealed class PrimeTester
resultBuffer.View.CopyToCPU(stream, in results); resultBuffer.View.CopyToCPU(stream, in results);
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
PrimeOrderCalculatorAccelerator.Return(gpu); PrimeOrderCalculatorAccelerator.Return(gpu);
} }