Convert divisor session results to hits on the GPU

This commit is contained in:
Marek Lemańczyk committed 2025-10-23 00:34:17 +02:00
1 parent faab60c2cd
commit ea01585892
3 files changed
+56 -42

No files matched your search

@@ -197,17 +197,7 @@ public class MersenneNumberDivisorGpuTesterTests
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
var hostBufferField = typeof(MersenneNumberDivisorByDivisorGpuTester.DivisorScanSession)
.GetField("_hostBuffer", BindingFlags.NonPublic | BindingFlags.Instance)!;
ulong[] hostBuffer = (ulong[])hostBufferField.GetValue(session)!;
Span<ulong> residues = hostBuffer.AsSpan(0, exponents.Length);
byte[] computedHits = new byte[exponents.Length];
for (int i = 0; i < residues.Length; i++)
{
computedHits[i] = residues[i] == divisorData.MontgomeryOne ? (byte)1 : (byte)0;
}
computedHits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
}
[Fact]
@@ -229,17 +219,7 @@ public class MersenneNumberDivisorGpuTesterTests
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
var hostBufferField = typeof(MersenneNumberDivisorByDivisorGpuTester.DivisorScanSession)
.GetField("_hostBuffer", BindingFlags.NonPublic | BindingFlags.Instance)!;
ulong[] hostBuffer = (ulong[])hostBufferField.GetValue(session)!;
Span<ulong> residues = hostBuffer.AsSpan(0, exponents.Length);
byte[] computedHits = new byte[exponents.Length];
for (int i = 0; i < residues.Length; i++)
{
computedHits[i] = residues[i] == divisorData.MontgomeryOne ? (byte)1 : (byte)0;
}
computedHits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
}
[Fact]
@@ -255,6 +255,24 @@ internal static class DivisorByDivisorKernels
Atomic.Min(ref firstHit[0], candidate);
}
public static void ConvertMontgomeryResultsToHitsKernel(
Index1D index,
ArrayView<ulong> results,
ArrayView<byte> hits)
{
int globalIndex = index;
// EvenPerfectBitScanner launches this kernel with an extent matching the result and hit spans,
// so the defensive bounds guard stays commented out to keep the conversion branch-free.
// int length = (int)results.Length;
// if (globalIndex >= length)
// {
// return;
// }
ulong montgomeryResult = results[globalIndex];
hits[globalIndex] = montgomeryResult == 1UL ? (byte)1 : (byte)0;
}
public static void ComputeMontgomeryExponentKernel(Index1D index, MontgomeryDivisorData divisor, ArrayView<ulong> exponents, ArrayView<ulong> results)
{
ulong modulus = divisor.Modulus;
@@ -30,6 +30,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<byte>, ArrayView<byte>, ArrayView<byte>, ArrayView<byte>, byte, ArrayView<byte>>> _candidateMaskKernelCache = new(AcceleratorReferenceComparer.Instance);
private readonly ConcurrentDictionary<Accelerator, Action<KernelConfig, Index1D, ArrayView<byte>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<int>>> _candidateCompactionKernelCache = new(AcceleratorReferenceComparer.Instance);
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<byte>, ArrayView<int>>> _hitDetectionKernelCache = new(AcceleratorReferenceComparer.Instance);
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<ulong>, ArrayView<byte>>> _hitConversionKernelCache = new(AcceleratorReferenceComparer.Instance);
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<ulong>, ArrayView<ulong>, ArrayView<GpuUInt128>, GpuUInt128, GpuUInt128>> _candidateGenerationKernelCache = new(AcceleratorReferenceComparer.Instance);
private readonly ConcurrentDictionary<Accelerator, ConcurrentBag<BatchResources>> _resourcePools = new(AcceleratorReferenceComparer.Instance);
private readonly ConcurrentBag<GpuContextPool.GpuContextLease> _acceleratorPool = new();
@@ -64,6 +65,11 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
accelerator,
acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<byte>, ArrayView<int>>(DivisorByDivisorKernels.PublishFirstHitKernel));
private Action<Index1D, ArrayView<ulong>, ArrayView<byte>> GetResultConversionKernel(Accelerator accelerator) =>
_hitConversionKernelCache.GetOrAdd(
accelerator,
acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<byte>>(DivisorByDivisorKernels.ConvertMontgomeryResultsToHitsKernel));
private Action<Index1D, ArrayView<ulong>, ArrayView<ulong>, ArrayView<GpuUInt128>, GpuUInt128, GpuUInt128> GetCandidateGenerationKernel(Accelerator accelerator) =>
_candidateGenerationKernelCache.GetOrAdd(
accelerator,
@@ -686,10 +692,12 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
private readonly MersenneNumberDivisorByDivisorGpuTester _owner;
private readonly GpuContextPool.GpuContextLease _lease;
private readonly Accelerator _accelerator;
private Action<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<ulong>> _kernel;
private MemoryBuffer1D<ulong, Stride1D.Dense> _exponentsBuffer;
private MemoryBuffer1D<ulong, Stride1D.Dense> _resultsBuffer;
private ulong[] _hostBuffer;
private Action<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<ulong>> _kernel = null!;
private Action<Index1D, ArrayView<ulong>, ArrayView<byte>> _hitConversionKernel = null!;
private MemoryBuffer1D<ulong, Stride1D.Dense> _exponentsBuffer = null!;
private MemoryBuffer1D<ulong, Stride1D.Dense> _resultsBuffer = null!;
private MemoryBuffer1D<byte, Stride1D.Dense> _hitBuffer = null!;
private ulong[] _hostBuffer = null!;
private int _capacity;
private bool _disposed;
@@ -709,8 +717,10 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
private void EnsureExecutionResourcesLocked()
{
_kernel = _owner.GetKernelByPrimeExponent(_accelerator);
_hitConversionKernel = _owner.GetResultConversionKernel(_accelerator);
_exponentsBuffer = _accelerator.Allocate1D<ulong>(_capacity);
_resultsBuffer = _accelerator.Allocate1D<ulong>(_capacity);
_hitBuffer = _accelerator.Allocate1D<byte>(_capacity);
_hostBuffer = ThreadStaticPools.UlongPool.Rent(_capacity);
}
@@ -722,26 +732,33 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
}
int length = primes.Length;
if (length == 0)
{
return;
}
// EvenPerfectBitScanner always supplies at least one exponent per divisor check, so the previous zero-length guard stays commented out.
// if (length == 0)
// {
// return;
// }
if (divisorData.Modulus <= 1UL || (divisorData.Modulus & 1UL) == 0UL)
{
hits.Clear();
return;
}
// The GPU divisor sessions only materialize odd moduli greater than one (q = 2kp + 1),
// so the defensive modulus guard stays commented out to keep the hot path branch-free.
// if (divisorData.Modulus <= 1UL || (divisorData.Modulus & 1UL) == 0UL)
// {
// hits.Clear();
// return;
// }
Monitor.Enter(_lease.ExecutionLock);
if (_kernel == null)
if (_kernel == null || _hitConversionKernel == null)
{
EnsureExecutionResourcesLocked();
}
var kernel = _kernel!;
var hitConversionKernel = _hitConversionKernel!;
ArrayView1D<ulong, Stride1D.Dense> exponentsView = _exponentsBuffer.View;
ArrayView1D<ulong, Stride1D.Dense> resultsView = _resultsBuffer.View;
ArrayView1D<byte, Stride1D.Dense> hitView = _hitBuffer.View;
Span<ulong> hostSpan = _hostBuffer.AsSpan(0, _capacity);
int offset = 0;
@@ -755,14 +772,13 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
exponentSlice.CopyFromCPU(ref MemoryMarshal.GetReference(hostSpan), batchSize);
ArrayView1D<ulong, Stride1D.Dense> resultSlice = resultsView.SubView(0, batchSize);
_kernel(batchSize, divisorData, exponentSlice, resultSlice);
resultSlice.CopyToCPU(ref MemoryMarshal.GetReference(hostSpan), batchSize);
kernel(batchSize, divisorData, exponentSlice, resultSlice);
ArrayView1D<byte, Stride1D.Dense> hitSliceView = hitView.SubView(0, batchSize);
hitConversionKernel(batchSize, resultSlice, hitSliceView);
Span<byte> hitSlice = hits.Slice(offset, batchSize);
for (int i = 0; i < batchSize; i++)
{
hitSlice[i] = hostSpan[i] == 1UL ? (byte)1 : (byte)0;
}
hitSliceView.CopyToCPU(ref MemoryMarshal.GetReference(hitSlice), batchSize);
offset += batchSize;
}