Add dedicated GPU residue evaluator for by-divisor deltas
This commit is contained in:
1 parent
0902b18721
commit
41e6703926
2 files changed
+140
-53
No files matched your search
@@ -18,8 +18,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
|
||||
private ulong _configuredMaxPrime;
|
||||
private ByDivisorDeltasDevice _deltasDevice = ByDivisorDeltasDevice.Cpu;
|
||||
private MersenneNumberDivisorByDivisorGpuTester? _gpuDeltasTester;
|
||||
private bool _gpuTesterConfigured;
|
||||
|
||||
|
||||
public int BatchSize
|
||||
@@ -29,14 +27,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
{
|
||||
int sanitized = Math.Max(1, value);
|
||||
_batchSize = sanitized;
|
||||
|
||||
lock (_sync)
|
||||
{
|
||||
if (_gpuDeltasTester is not null)
|
||||
{
|
||||
_gpuDeltasTester.GpuBatchSize = sanitized;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +50,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
_divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime);
|
||||
_lastStatusDivisor = 0UL;
|
||||
_isConfigured = true;
|
||||
_gpuTesterConfigured = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,31 +156,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
}
|
||||
}
|
||||
|
||||
internal IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateGpuDeltasSession()
|
||||
{
|
||||
MersenneNumberDivisorByDivisorGpuTester tester;
|
||||
|
||||
lock (_sync)
|
||||
{
|
||||
if (!_isConfigured)
|
||||
{
|
||||
throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
|
||||
}
|
||||
|
||||
tester = _gpuDeltasTester ??= new MersenneNumberDivisorByDivisorGpuTester();
|
||||
|
||||
if (!_gpuTesterConfigured)
|
||||
{
|
||||
tester.ConfigureFromMaxPrime(_configuredMaxPrime);
|
||||
_gpuTesterConfigured = true;
|
||||
}
|
||||
|
||||
tester.GpuBatchSize = _batchSize;
|
||||
}
|
||||
|
||||
return tester.CreateDivisorSession();
|
||||
}
|
||||
|
||||
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession()
|
||||
{
|
||||
lock (_sync)
|
||||
@@ -514,7 +478,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
private ulong[]? _cycleRemainders;
|
||||
private ulong[]? _montgomeryDeltas;
|
||||
private int _bufferCapacity;
|
||||
private IMersenneNumberDivisorByDivisorTester.IDivisorScanSession? _gpuSession;
|
||||
private MersenneNumberDivisorResidueGpuEvaluator? _gpuResidueEvaluator;
|
||||
|
||||
internal DivisorScanSession(MersenneNumberDivisorByDivisorCpuTester owner)
|
||||
{
|
||||
@@ -572,13 +536,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
}
|
||||
}
|
||||
|
||||
if (_owner._deltasDevice == ByDivisorDeltasDevice.Gpu)
|
||||
{
|
||||
EnsureGpuSession();
|
||||
_gpuSession!.CheckDivisor(divisor, cachedData, divisorCycle, primes, hits);
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureCapacity(length);
|
||||
|
||||
Span<ulong> deltaSpan = _primeDeltas!.AsSpan(0, length);
|
||||
@@ -587,11 +544,27 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
Span<ulong> remainderSpan = _cycleRemainders!.AsSpan(0, length);
|
||||
ComputeCycleRemainders(divisorCycle, primes, deltaSpan, remainderSpan);
|
||||
|
||||
ulong montgomeryOne = cachedData.MontgomeryOne;
|
||||
|
||||
if (_owner._deltasDevice == ByDivisorDeltasDevice.Gpu)
|
||||
{
|
||||
EnsureGpuEvaluator();
|
||||
Span<ulong> residueSpan = _montgomeryDeltas!.AsSpan(0, length);
|
||||
// Reuse the Montgomery delta buffer to hold the final residues returned by the GPU evaluator.
|
||||
_gpuResidueEvaluator!.ComputeResidues(primes, cachedData, residueSpan);
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
hits[i] = remainderSpan[i] == 0UL && residueSpan[i] == montgomeryOne ? (byte)1 : (byte)0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Span<ulong> montgomerySpan = _montgomeryDeltas!.AsSpan(0, length);
|
||||
ComputeMontgomeryDeltas(cachedData, primes, deltaSpan, montgomerySpan);
|
||||
|
||||
ulong nPrime = cachedData.NPrime;
|
||||
ulong montgomeryOne = cachedData.MontgomeryOne;
|
||||
ulong currentMontgomery = montgomerySpan[0];
|
||||
|
||||
hits[0] = remainderSpan[0] == 0UL && currentMontgomery == montgomeryOne ? (byte)1 : (byte)0;
|
||||
@@ -603,23 +576,23 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureGpuSession()
|
||||
private void EnsureGpuEvaluator()
|
||||
{
|
||||
if (_gpuSession is null)
|
||||
if (_gpuResidueEvaluator is null)
|
||||
{
|
||||
_gpuSession = _owner.CreateGpuDeltasSession();
|
||||
_gpuResidueEvaluator = new MersenneNumberDivisorResidueGpuEvaluator(_owner._batchSize);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReleaseGpuSession()
|
||||
private void ReleaseGpuEvaluator()
|
||||
{
|
||||
if (_gpuSession is null)
|
||||
if (_gpuResidueEvaluator is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_gpuSession.Dispose();
|
||||
_gpuSession = null;
|
||||
_gpuResidueEvaluator.Dispose();
|
||||
_gpuResidueEvaluator = null;
|
||||
}
|
||||
|
||||
private void EnsureCapacity(int requiredLength)
|
||||
@@ -767,7 +740,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
ReleaseGpuSession();
|
||||
ReleaseGpuEvaluator();
|
||||
_owner.ReturnSession(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Runtime.InteropServices;
|
||||
using PerfectNumbers.Core;
|
||||
using ILGPU;
|
||||
using ILGPU.Runtime;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
internal sealed class MersenneNumberDivisorResidueGpuEvaluator : IDisposable
|
||||
{
|
||||
private readonly GpuContextPool.GpuContextLease _lease;
|
||||
private readonly Accelerator _accelerator;
|
||||
private readonly Action<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<ulong>> _kernel;
|
||||
private MemoryBuffer1D<ulong, Stride1D.Dense> _exponentBuffer;
|
||||
private MemoryBuffer1D<ulong, Stride1D.Dense> _resultBuffer;
|
||||
private ulong[] _hostBuffer;
|
||||
private int _capacity;
|
||||
private bool _disposed;
|
||||
|
||||
internal MersenneNumberDivisorResidueGpuEvaluator(int batchSize)
|
||||
{
|
||||
_lease = GpuContextPool.RentPreferred(preferCpu: false);
|
||||
_accelerator = _lease.Accelerator;
|
||||
_kernel = _accelerator.LoadAutoGroupedStreamKernel<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<ulong>>(ComputeResiduesKernel);
|
||||
_capacity = Math.Max(1, batchSize);
|
||||
_exponentBuffer = _accelerator.Allocate1D<ulong>(_capacity);
|
||||
_resultBuffer = _accelerator.Allocate1D<ulong>(_capacity);
|
||||
_hostBuffer = ArrayPool<ulong>.Shared.Rent(_capacity);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
_exponentBuffer.Dispose();
|
||||
_resultBuffer.Dispose();
|
||||
ArrayPool<ulong>.Shared.Return(_hostBuffer, clearArray: false);
|
||||
_lease.Dispose();
|
||||
}
|
||||
|
||||
internal void ComputeResidues(ReadOnlySpan<ulong> exponents, in MontgomeryDivisorData divisorData, Span<ulong> destination)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(MersenneNumberDivisorResidueGpuEvaluator));
|
||||
}
|
||||
|
||||
int length = exponents.Length;
|
||||
if (length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureCapacity(length);
|
||||
|
||||
Span<ulong> hostSpan = _hostBuffer.AsSpan(0, length);
|
||||
exponents.CopyTo(hostSpan);
|
||||
|
||||
ArrayView1D<ulong, Stride1D.Dense> exponentView = _exponentBuffer.View.SubView(0, length);
|
||||
exponentView.CopyFromCPU(ref MemoryMarshal.GetReference(hostSpan), length);
|
||||
|
||||
ArrayView1D<ulong, Stride1D.Dense> resultView = _resultBuffer.View.SubView(0, length);
|
||||
_kernel(length, divisorData, exponentView, resultView);
|
||||
resultView.CopyToCPU(ref MemoryMarshal.GetReference(hostSpan), length);
|
||||
|
||||
hostSpan.CopyTo(destination);
|
||||
}
|
||||
|
||||
private void EnsureCapacity(int required)
|
||||
{
|
||||
if (required <= _capacity)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int newCapacity = _capacity;
|
||||
if (newCapacity == 0)
|
||||
{
|
||||
newCapacity = 1;
|
||||
}
|
||||
|
||||
while (newCapacity < required)
|
||||
{
|
||||
newCapacity <<= 1;
|
||||
}
|
||||
|
||||
_exponentBuffer.Dispose();
|
||||
_resultBuffer.Dispose();
|
||||
ArrayPool<ulong>.Shared.Return(_hostBuffer, clearArray: false);
|
||||
|
||||
_capacity = newCapacity;
|
||||
_exponentBuffer = _accelerator.Allocate1D<ulong>(_capacity);
|
||||
_resultBuffer = _accelerator.Allocate1D<ulong>(_capacity);
|
||||
_hostBuffer = ArrayPool<ulong>.Shared.Rent(_capacity);
|
||||
}
|
||||
|
||||
private static void ComputeResiduesKernel(Index1D index, MontgomeryDivisorData divisorData, ArrayView<ulong> exponents, ArrayView<ulong> results)
|
||||
{
|
||||
int idx = index;
|
||||
int length = (int)exponents.Length;
|
||||
if (idx >= length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ulong exponent = exponents[idx];
|
||||
results[idx] = exponent.Pow2MontgomeryModWindowedGpu(divisorData, keepMontgomery: true);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user