Tiny cleanup

This commit is contained in:
Marek Lemańczyk committed 2025-09-21 09:51:05 +02:00
1 parent 2c2bc62cb2
commit 860d64baba
1 file changed
+121 -121
@@ -9,11 +9,11 @@ namespace PerfectNumbers.Core.Gpu;
public sealed class MersenneNumberDivisorGpuTester public sealed class MersenneNumberDivisorGpuTester
{ {
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, GpuUInt128, ArrayView<byte>>> _kernelCache = new(); private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, GpuUInt128, ArrayView<byte>>> _kernelCache = new();
private readonly ConcurrentDictionary<Accelerator, MemoryBuffer1D<byte, Stride1D.Dense>> _resultBuffers = new(); private readonly ConcurrentDictionary<Accelerator, MemoryBuffer1D<byte, Stride1D.Dense>> _resultBuffers = new();
private Action<Index1D, ulong, GpuUInt128, ArrayView<byte>> GetKernel(Accelerator accelerator) => private Action<Index1D, ulong, GpuUInt128, ArrayView<byte>> GetKernel(Accelerator accelerator) =>
_kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, ArrayView<byte>>(Kernel)); _kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, ArrayView<byte>>(Kernel));
public static void BuildDivisorCandidates() public static void BuildDivisorCandidates()
{ {
@@ -32,137 +32,137 @@ public sealed class MersenneNumberDivisorGpuTester
list[count++] = ((ulong)i, cycle); list[count++] = ((ulong)i, cycle);
} }
_divisorCandidates = count == 0 ? Array.Empty<(ulong divisor, uint cycle)>() : list[..count].ToArray(); _divisorCandidates = count == 0 ? [] : list[..count];
} }
public bool IsDivisible(ulong exponent, UInt128 divisor) public bool IsDivisible(ulong exponent, UInt128 divisor)
{ {
var gpu = GpuContextPool.RentPreferred(preferCpu: false); var gpu = GpuContextPool.RentPreferred(preferCpu: false);
var accelerator = gpu.Accelerator; var accelerator = gpu.Accelerator;
var kernel = GetKernel(accelerator); var kernel = GetKernel(accelerator);
var resultBuffer = _resultBuffers.GetOrAdd(accelerator, acc => acc.Allocate1D<byte>(1)); var resultBuffer = _resultBuffers.GetOrAdd(accelerator, acc => acc.Allocate1D<byte>(1));
resultBuffer.MemSetToZero(); resultBuffer.MemSetToZero();
kernel(1, exponent, (GpuUInt128)divisor, resultBuffer.View); kernel(1, exponent, (GpuUInt128)divisor, resultBuffer.View);
accelerator.Synchronize(); accelerator.Synchronize();
Span<byte> result = stackalloc byte[1]; Span<byte> result = stackalloc byte[1];
resultBuffer.View.CopyToCPU(ref result[0], 1); resultBuffer.View.CopyToCPU(ref result[0], 1);
bool divisible = result[0] != 0; bool divisible = result[0] != 0;
result[0] = 0; result[0] = 0;
resultBuffer.View.CopyFromCPU(ref result[0], 1); resultBuffer.View.CopyFromCPU(ref result[0], 1);
accelerator.Synchronize(); accelerator.Synchronize();
gpu.Dispose(); gpu.Dispose();
return divisible; return divisible;
} }
private static (ulong divisor, uint cycle)[]? _divisorCandidates = Array.Empty<(ulong divisor, uint cycle)>(); private static (ulong divisor, uint cycle)[]? _divisorCandidates = Array.Empty<(ulong divisor, uint cycle)>();
public bool IsPrime(ulong p, UInt128 d, ulong divisorCyclesSearchLimit, out bool divisorsExhausted) public bool IsPrime(ulong p, UInt128 d, ulong divisorCyclesSearchLimit, out bool divisorsExhausted)
{ {
if (d != UInt128.Zero) if (d != UInt128.Zero)
{ {
if (IsDivisible(p, d)) if (IsDivisible(p, d))
{ {
divisorsExhausted = true; divisorsExhausted = true;
return false; return false;
} }
divisorsExhausted = false; divisorsExhausted = false;
return true; return true;
} }
if (_divisorCandidates is { Length: > 0 } candidates) if (_divisorCandidates is { Length: > 0 } candidates)
{ {
int len = candidates.Length; int len = candidates.Length;
uint cycle; uint cycle;
for (int k = 0; k < len; k++) for (int k = 0; k < len; k++)
{ {
(ulong dSmall, cycle) = candidates[k]; (ulong dSmall, cycle) = candidates[k];
if (p % cycle != 0UL) if (p % cycle != 0UL)
{ {
continue; continue;
} }
if (IsDivisible(p, dSmall)) if (IsDivisible(p, dSmall))
{ {
divisorsExhausted = true; divisorsExhausted = true;
return false; return false;
} }
} }
} }
UInt128 kMul2; UInt128 kMul2;
var divisorCycles = MersenneDivisorCycles.Shared; var divisorCycles = MersenneDivisorCycles.Shared;
UInt128 maxK2 = UInt128.MaxValue / ((UInt128)p << 1); UInt128 maxK2 = UInt128.MaxValue / ((UInt128)p << 1);
ulong limit = divisorCyclesSearchLimit; ulong limit = divisorCyclesSearchLimit;
if ((UInt128)limit > maxK2) if ((UInt128)limit > maxK2)
{ {
limit = (ulong)maxK2; limit = (ulong)maxK2;
} }
ulong k2; ulong k2;
for (k2 = 1UL; k2 <= limit; k2++) for (k2 = 1UL; k2 <= limit; k2++)
{ {
kMul2 = (UInt128)k2 << 1; kMul2 = (UInt128)k2 << 1;
UInt128 candidate = checked(kMul2 * p); UInt128 candidate = checked(kMul2 * p);
d = checked(candidate + UInt128.One); d = checked(candidate + UInt128.One);
if (p < 64UL && d == ((UInt128)1 << (int)p) - UInt128.One) if (p < 64UL && d == ((UInt128)1 << (int)p) - UInt128.One)
{ {
continue; continue;
} }
UInt128 cycle128 = MersenneDivisorCycles.GetCycle(d); UInt128 cycle128 = MersenneDivisorCycles.GetCycle(d);
if ((UInt128)p % cycle128 != UInt128.Zero) if ((UInt128)p % cycle128 != UInt128.Zero)
{ {
continue; continue;
} }
if (IsDivisible(p, d)) if (IsDivisible(p, d))
{ {
divisorsExhausted = true; divisorsExhausted = true;
return false; return false;
} }
} }
divisorsExhausted = (UInt128)divisorCyclesSearchLimit >= maxK2; divisorsExhausted = (UInt128)divisorCyclesSearchLimit >= maxK2;
return true; return true;
} }
private static void Kernel(Index1D _, ulong exponent, GpuUInt128 divisor, ArrayView<byte> result) private static void Kernel(Index1D _, ulong exponent, GpuUInt128 divisor, ArrayView<byte> result)
{ {
if (divisor.High == 0UL && divisor.Low <= 1UL) if (divisor.High == 0UL && divisor.Low <= 1UL)
{ {
result[0] = 0; result[0] = 0;
return; return;
} }
GpuUInt128 baseVal, mod; GpuUInt128 baseVal, mod;
int x = divisor.High == 0UL int x = divisor.High == 0UL
? 63 - XMath.LeadingZeroCount(divisor.Low) ? 63 - XMath.LeadingZeroCount(divisor.Low)
: 127 - XMath.LeadingZeroCount(divisor.High); : 127 - XMath.LeadingZeroCount(divisor.High);
mod = divisor; mod = divisor;
if (x == 0) if (x == 0)
{ {
baseVal = GpuUInt128.Pow2Mod(exponent, mod); baseVal = GpuUInt128.Pow2Mod(exponent, mod);
result[0] = baseVal.High == 0UL && baseVal.Low == 1UL ? (byte)1 : (byte)0; result[0] = baseVal.High == 0UL && baseVal.Low == 1UL ? (byte)1 : (byte)0;
return; return;
} }
ulong ux = (ulong)x; ulong ux = (ulong)x;
if (x >= 64) if (x >= 64)
{ {
baseVal = new GpuUInt128(1UL << (x - 64), 0UL); baseVal = new GpuUInt128(1UL << (x - 64), 0UL);
} }
else else
{ {
baseVal = new GpuUInt128(0UL, 1UL << x); baseVal = new GpuUInt128(0UL, 1UL << x);
} }
GpuUInt128 pow = baseVal; GpuUInt128 pow = baseVal;
pow.ModPow(exponent / ux, mod); pow.ModPow(exponent / ux, mod);
var part2 = GpuUInt128.Pow2Mod(exponent % ux, mod); var part2 = GpuUInt128.Pow2Mod(exponent % ux, mod);
GpuUInt128 product = pow; GpuUInt128 product = pow;
product.MulMod(part2, mod); product.MulMod(part2, mod);
result[0] = product.High == 0UL && product.Low == 1UL ? (byte)1 : (byte)0; result[0] = product.High == 0UL && product.Low == 1UL ? (byte)1 : (byte)0;
} }
} }