GPU logic updated to use prime % divisor result precomputed on CPU instead of re-calculating it on GPU side. Cycle lease removed.

This commit is contained in:
Marek Lemańczyk committed 2025-09-25 05:28:23 +02:00
1 parent a171e57353
commit 49fcd93747
2 files changed
+31 -95

No files matched your search

+9 -55
View File
@@ -12,60 +12,15 @@ namespace PerfectNumbers.Core;
public sealed class DivisorCycleCache public sealed class DivisorCycleCache
{ {
internal sealed class CycleBlock public sealed class CycleBlock(int index, ulong start, ulong[] cycles)
{ {
internal CycleBlock(int index, ulong start, ulong[] cycles) internal readonly int Index = index;
{ internal readonly ulong Start = start;
Index = index; internal readonly ulong End = start + (ulong)cycles.Length - 1UL;
Start = start; internal readonly ulong[] Cycles = cycles;
Cycles = cycles;
End = start + (ulong)cycles.Length - 1UL;
}
internal int Index { get; }
internal ulong Start { get; }
internal ulong End { get; }
internal ulong[] Cycles { get; }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ulong GetCycle(ulong divisor) public ulong GetCycle(ulong divisor) => Cycles[(int)(divisor - Start)];
{
if (divisor < Start || divisor > End)
{
return 0UL;
}
return Cycles[(int)(divisor - Start)];
}
}
public struct Lease : IDisposable
{
private CycleBlock? _block;
internal Lease(CycleBlock block)
{
_block = block;
}
public readonly bool IsValid => _block is not null;
public readonly ulong Start => _block?.Start ?? 0UL;
public readonly ulong End => _block?.End ?? 0UL;
public readonly ulong[]? Values => _block?.Cycles;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly ulong GetCycle(ulong divisor) => _block?.GetCycle(divisor) ?? 0UL;
public void Dispose()
{
_block = null;
}
} }
private const int CycleGenerationBatchSize = 262_144; private const int CycleGenerationBatchSize = 262_144;
@@ -115,15 +70,14 @@ public sealed class DivisorCycleCache
} }
} }
public Lease Acquire(ulong divisor) public CycleBlock Acquire(ulong divisor)
{ {
if (divisor <= _baseBlock.End) if (divisor <= _baseBlock.End)
{ {
return new Lease(_baseBlock); return _baseBlock;
} }
CycleBlock block = AcquireDynamicBlock(divisor); return AcquireDynamicBlock(divisor);
return new Lease(block);
} }
private CycleBlock AcquireDynamicBlock(ulong divisor) private CycleBlock AcquireDynamicBlock(ulong divisor)
@@ -25,14 +25,14 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
public readonly ulong MontgomeryTwo = montgomeryTwo; public readonly ulong MontgomeryTwo = montgomeryTwo;
} }
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, byte, ArrayView<MontgomeryDivisorData>, ArrayView<byte>>> _kernelCache = new(); private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, byte, ulong, ArrayView<MontgomeryDivisorData>, ArrayView<byte>>> _kernelCache = new();
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<ulong>>> _kernelExponentCache = new(); private readonly ConcurrentDictionary<Accelerator, Action<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<ulong>>> _kernelExponentCache = new();
private readonly ConcurrentDictionary<Accelerator, ConcurrentBag<BatchResources>> _resourcePools = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentDictionary<Accelerator, ConcurrentBag<BatchResources>> _resourcePools = new(AcceleratorReferenceComparer.Instance);
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ArrayView<ulong>, ArrayView<byte>>> _cycleKernelCache = new(); private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ArrayView<ulong>, ArrayView<byte>>> _cycleKernelCache = new();
private readonly ConcurrentBag<DivisorScanSession> _sessionPool = new(); private readonly ConcurrentBag<DivisorScanSession> _sessionPool = new();
private Action<Index1D, ulong, byte, ArrayView<MontgomeryDivisorData>, ArrayView<byte>> GetKernel(Accelerator accelerator) => private Action<Index1D, ulong, byte, ulong, ArrayView<MontgomeryDivisorData>, ArrayView<byte>> GetKernel(Accelerator accelerator) =>
_kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, byte, ArrayView<MontgomeryDivisorData>, ArrayView<byte>>(CheckKernel)); _kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, byte, ulong, ArrayView<MontgomeryDivisorData>, ArrayView<byte>>(CheckKernel));
private Action<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<ulong>> GetKernelByPrimeExponent(Accelerator accelerator) => private Action<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<ulong>> GetKernelByPrimeExponent(Accelerator accelerator) =>
_kernelExponentCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<ulong>>(ComputePrimeExponentKernel)); _kernelExponentCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<ulong>>(ComputePrimeExponentKernel));
private Action<Index1D, ulong, ArrayView<ulong>, ArrayView<byte>> GetCycleKernel(Accelerator accelerator) => private Action<Index1D, ulong, ArrayView<ulong>, ArrayView<byte>> GetCycleKernel(Accelerator accelerator) =>
@@ -138,7 +138,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
ulong allowedMax, ulong allowedMax,
bool useCycles, bool useCycles,
Accelerator accelerator, Accelerator accelerator,
Action<Index1D, ulong, byte, ArrayView<MontgomeryDivisorData>, ArrayView<byte>> kernel, Action<Index1D, ulong, byte, ulong, ArrayView<MontgomeryDivisorData>, ArrayView<byte>> kernel,
MemoryBuffer1D<MontgomeryDivisorData, Stride1D.Dense> divisorsBuffer, MemoryBuffer1D<MontgomeryDivisorData, Stride1D.Dense> divisorsBuffer,
MemoryBuffer1D<byte, Stride1D.Dense> hitsBuffer, MemoryBuffer1D<byte, Stride1D.Dense> hitsBuffer,
ulong[] divisors, ulong[] divisors,
@@ -161,10 +161,9 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
bool composite = false; bool composite = false;
bool processedAll = false; bool processedAll = false;
ulong currentDivisor, divisor = 3UL, next, nextDivisor; ulong currentDivisor = 3UL, cycle = 0UL, next, nextDivisor;
int batchSize, i; int batchSize, i;
bool reachedEndInBatch; bool reachedEndInBatch;
bool requiresCycleKernelCheck;
Span<MontgomeryDivisorData> divisorDataSpan; Span<MontgomeryDivisorData> divisorDataSpan;
Span<ulong> divisorSpan; Span<ulong> divisorSpan;
@@ -172,34 +171,31 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
ArrayView1D<MontgomeryDivisorData, Stride1D.Dense> divisorView; ArrayView1D<MontgomeryDivisorData, Stride1D.Dense> divisorView;
ArrayView1D<byte, Stride1D.Dense> hitsView; ArrayView1D<byte, Stride1D.Dense> hitsView;
DivisorCycleCache divisorCyclesCache = DivisorCycleCache.Shared; DivisorCycleCache divisorCyclesCache = DivisorCycleCache.Shared;
DivisorCycleCache.CycleBlock? cycleLease = useCycles ? divisorCyclesCache.Acquire(currentDivisor) : null;
while (divisor <= allowedMax) while (currentDivisor <= allowedMax)
{ {
batchSize = 0; batchSize = 0;
reachedEndInBatch = false; reachedEndInBatch = false;
requiresCycleKernelCheck = false;
while (batchSize < batchCapacity && divisor <= allowedMax) while (batchSize < batchCapacity && currentDivisor <= allowedMax)
{ {
currentDivisor = divisor;
nextDivisor = currentDivisor + 2UL; nextDivisor = currentDivisor + 2UL;
processedCount++; processedCount++;
bool includeDivisor = true; bool includeDivisor = true;
if (useCycles) if (useCycles)
{ {
DivisorCycleCache.Lease cycleLease = divisorCyclesCache.Acquire(currentDivisor); if (currentDivisor > cycleLease!.End)
ulong cycle = cycleLease.GetCycle(currentDivisor);
if (cycle == 0UL)
{ {
requiresCycleKernelCheck = true; cycleLease = divisorCyclesCache.Acquire(currentDivisor);
} }
else if (prime % cycle != 0UL)
cycle = prime % cycleLease.GetCycle(currentDivisor);
if (cycle != 0UL)
{ {
includeDivisor = false; includeDivisor = false;
} }
cycleLease.Dispose();
} }
if (includeDivisor) if (includeDivisor)
@@ -210,7 +206,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
lastProcessed = currentDivisor; lastProcessed = currentDivisor;
next = nextDivisor; next = nextDivisor;
if (next <= divisor) if (next <= currentDivisor)
{ {
reachedEndInBatch = true; reachedEndInBatch = true;
break; break;
@@ -218,12 +214,12 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
if (next > allowedMax) if (next > allowedMax)
{ {
divisor = next; currentDivisor = next;
reachedEndInBatch = true; reachedEndInBatch = true;
break; break;
} }
divisor = next; currentDivisor = next;
} }
if (batchSize == 0) if (batchSize == 0)
@@ -250,8 +246,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
} }
divisorView.CopyFromCPU(ref MemoryMarshal.GetReference(divisorDataSpan), batchSize); divisorView.CopyFromCPU(ref MemoryMarshal.GetReference(divisorDataSpan), batchSize);
byte useCycleChecks = useCycles && requiresCycleKernelCheck ? (byte)1 : (byte)0; byte useCycleChecks = useCycles ? (byte)1 : (byte)0;
kernel(batchSize, prime, useCycleChecks, divisorView, hitsView); kernel(batchSize, prime, useCycleChecks, cycle, divisorView, hitsView);
accelerator.Synchronize(); accelerator.Synchronize();
hitsView.CopyToCPU(ref MemoryMarshal.GetReference(hitsSpan), batchSize); hitsView.CopyToCPU(ref MemoryMarshal.GetReference(hitsSpan), batchSize);
@@ -281,7 +277,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
} }
} }
coveredRange = composite || processedAll || divisor > allowedMax; coveredRange = composite || processedAll || currentDivisor > allowedMax;
return composite; return composite;
} }
@@ -316,7 +312,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
return Math.Min((1UL << (int)(prime - 1UL)) - 1UL, divisorLimit); return Math.Min((1UL << (int)(prime - 1UL)) - 1UL, divisorLimit);
} }
private static void CheckKernel(Index1D index, ulong prime, byte useCycleChecks, ArrayView<MontgomeryDivisorData> divisors, ArrayView<byte> hits) private static void CheckKernel(Index1D index, ulong prime, byte useCycleChecks, ulong cycle, ArrayView<MontgomeryDivisorData> divisors, ArrayView<byte> hits)
{ {
MontgomeryDivisorData divisor = divisors[index]; MontgomeryDivisorData divisor = divisors[index];
ulong modulus = divisor.Modulus; ulong modulus = divisor.Modulus;
@@ -326,26 +322,12 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
return; return;
} }
ulong exponent = prime;
if (useCycleChecks != 0) if (useCycleChecks != 0)
{ {
ulong cycle = MersenneDivisorCycles.CalculateCycleLengthGpu(modulus); prime = cycle;
if (cycle == 0UL)
{
hits[index] = 0;
return;
}
ulong remainder = prime % cycle;
if (remainder != 0UL)
{
hits[index] = 0;
return;
} }
exponent = remainder; hits[index] = prime.Pow2MontgomeryMod(divisor) == 1UL ? (byte)1 : (byte)0;
}
hits[index] = exponent.Pow2MontgomeryMod(divisor) == 1UL ? (byte)1 : (byte)0;
} }
public sealed class DivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession public sealed class DivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession