Tiny cleanup

This commit is contained in:
Marek Lemańczyk committed 2025-09-17 19:08:43 +02:00
1 parent 2d024cc708
commit 9a7382fd80
3 files changed
+187 -187

No files matched your search

+97 -97
View File
@@ -26,20 +26,20 @@ internal static class Program
private static int _primeCount; private static int _primeCount;
private static bool _primeFoundAfterInit; private static bool _primeFoundAfterInit;
private static bool _useGcdFilter; private static bool _useGcdFilter;
private static bool _useDivisor; private static bool _useDivisor;
private static bool _useResidueMode; private static bool _useResidueMode;
private static UInt128 _divisor; private static UInt128 _divisor;
private static MersenneNumberDivisorGpuTester? _divisorTester; private static MersenneNumberDivisorGpuTester? _divisorTester;
private static ulong? _orderWarmupLimitOverride; private static ulong? _orderWarmupLimitOverride;
private static unsafe delegate*<ulong, ref ulong, ulong> _transformP; private static unsafe delegate*<ulong, ref ulong, ulong> _transformP;
private static long _state; private static long _state;
private static bool _limitReached; private static bool _limitReached;
private static string? _resultsDir; private static string? _resultsDir;
private static string? _resultsPrefix; private static string? _resultsPrefix;
private static readonly Optimized PrimeIterator = new(); private static readonly Optimized PrimeIterator = new();
[ThreadStatic] [ThreadStatic]
private static bool _lastCompositeP; private static bool _lastCompositeP;
// RLE/bit-pattern filtering options // RLE/bit-pattern filtering options
private static string? _rleBlacklistPath; private static string? _rleBlacklistPath;
@@ -414,20 +414,20 @@ internal static class Program
Console.WriteLine("Divisor cycles are ready"); Console.WriteLine("Divisor cycles are ready");
_useDivisor = useDivisor; _useDivisor = useDivisor;
_useResidueMode = useResidue; _useResidueMode = useResidue;
_divisor = divisor; _divisor = divisor;
if (useBitTransform) if (useBitTransform)
{ {
_transformP = &TransformPBit; _transformP = &TransformPBit;
} }
else if (useResidue) else if (useResidue)
{ {
_transformP = &TransformPAdd; _transformP = &TransformPAdd;
} }
else else
{ {
_transformP = &TransformPAddPrimes; _transformP = &TransformPAddPrimes;
} }
PResidue = new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, initialNumber: currentP, initialized: true), trackAllValues: true); PResidue = new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, initialNumber: currentP, initialized: true), trackAllValues: true);
PrimeTesters = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true); PrimeTesters = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
@@ -435,20 +435,20 @@ internal static class Program
// Initialize per-thread p residue tracker (Identity model) at currentP // Initialize per-thread p residue tracker (Identity model) at currentP
if (!useDivisor) if (!useDivisor)
{ {
MersenneTesters = new ThreadLocal<MersenneNumberTester>(() => MersenneTesters = new ThreadLocal<MersenneNumberTester>(() =>
{ {
var tester = new MersenneNumberTester( var tester = new MersenneNumberTester(
useIncremental: !useLucas, useIncremental: !useLucas,
useOrderCache: false, useOrderCache: false,
kernelType: kernelType, kernelType: kernelType,
useModuloWorkaround: useModuloWorkaround, useModuloWorkaround: useModuloWorkaround,
useOrder: useOrder, useOrder: useOrder,
useGpuLucas: mersenneOnGpu, useGpuLucas: mersenneOnGpu,
useGpuScan: mersenneOnGpu, useGpuScan: mersenneOnGpu,
useGpuOrder: orderOnGpu, useGpuOrder: orderOnGpu,
useResidue: useResidue, useResidue: useResidue,
maxK: residueKMax, maxK: residueKMax,
residueDivisorSets: divisorCyclesSearchLimit); residueDivisorSets: divisorCyclesSearchLimit);
if (!useLucas) if (!useLucas)
{ {
tester.WarmUpOrders(currentP, _orderWarmupLimitOverride ?? 5_000_000UL); tester.WarmUpOrders(currentP, _orderWarmupLimitOverride ?? 5_000_000UL);
@@ -732,7 +732,7 @@ internal static class Program
Console.WriteLine(" --divisor-cycles-device=cpu|gpu device for cycles generation (default gpu)"); Console.WriteLine(" --divisor-cycles-device=cpu|gpu device for cycles generation (default gpu)");
Console.WriteLine(" --divisor-cycles-batch-size=<value> batch size for cycles generation (default 512)"); Console.WriteLine(" --divisor-cycles-batch-size=<value> batch size for cycles generation (default 512)");
Console.WriteLine(" --divisor-cycles-continue continue divisor cycles generation"); Console.WriteLine(" --divisor-cycles-continue continue divisor cycles generation");
Console.WriteLine(" --divisor-cycles-limit=<value> cycle search iterations when --mersenne=divisor or residue"); Console.WriteLine(" --divisor-cycles-limit=<value> cycle search iterations when --mersenne=divisor or residue");
Console.WriteLine(" --use-order test primality via q order"); Console.WriteLine(" --use-order test primality via q order");
Console.WriteLine(" --workaround-mod avoid '%' operator on the GPU"); Console.WriteLine(" --workaround-mod avoid '%' operator on the GPU");
@@ -790,30 +790,30 @@ internal static class Program
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void PrintResult(ulong currentP, bool searchedMersenne, bool detailedCheck, bool isPerfect) private static void PrintResult(ulong currentP, bool searchedMersenne, bool detailedCheck, bool isPerfect)
{ {
if (isPerfect) if (isPerfect)
{ {
int newCount = Interlocked.Increment(ref _primeCount); int newCount = Interlocked.Increment(ref _primeCount);
if (newCount >= 2) if (newCount >= 2)
{ {
Volatile.Write(ref _primeFoundAfterInit, true); Volatile.Write(ref _primeFoundAfterInit, true);
} }
} }
if (_useResidueMode && _lastCompositeP) if (_useResidueMode && _lastCompositeP)
{ {
return; return;
} }
bool primeFlag = false, printToConsole = false; bool primeFlag = false, printToConsole = false;
StringBuilder localBuilder = StringBuilderPool.Rent(); StringBuilder localBuilder = StringBuilderPool.Rent();
_ = localBuilder _ = localBuilder
.Append(currentP).Append(',') .Append(currentP).Append(',')
.Append(searchedMersenne).Append(',') .Append(searchedMersenne).Append(',')
.Append(detailedCheck).Append(',') .Append(detailedCheck).Append(',')
.Append(isPerfect).Append('\n'); .Append(isPerfect).Append('\n');
lock (Sync) lock (Sync)
{ {
@@ -1011,20 +1011,20 @@ internal static class Program
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool IsEvenPerfectCandidate(ulong p, ulong divisorCyclesSearchLimit) => IsEvenPerfectCandidate(p, divisorCyclesSearchLimit, out _, out _); internal static bool IsEvenPerfectCandidate(ulong p, ulong divisorCyclesSearchLimit) => IsEvenPerfectCandidate(p, divisorCyclesSearchLimit, out _, out _);
internal static bool IsEvenPerfectCandidate(ulong p, ulong divisorCyclesSearchLimit, out bool searchedMersenne, out bool detailedCheck) internal static bool IsEvenPerfectCandidate(ulong p, ulong divisorCyclesSearchLimit, out bool searchedMersenne, out bool detailedCheck)
{ {
searchedMersenne = false; searchedMersenne = false;
detailedCheck = false; detailedCheck = false;
_lastCompositeP = false; _lastCompositeP = false;
if (_useGcdFilter && IsCompositeByGcd(p)) if (_useGcdFilter && IsCompositeByGcd(p))
{ {
_lastCompositeP = true; _lastCompositeP = true;
return false; return false;
} }
// Optional: RLE blacklist and binary-threshold filters on p (safe only when configured) // Optional: RLE blacklist and binary-threshold filters on p (safe only when configured)
if (p <= _rleHardMaxP) if (p <= _rleHardMaxP)
{ {
if (!_rleOnlyLast7 || (p % 10UL) == 7UL) if (!_rleOnlyLast7 || (p % 10UL) == 7UL)
{ {
@@ -1058,36 +1058,36 @@ internal static class Program
} }
} }
// Fast residue-based composite check for p using small primes // Fast residue-based composite check for p using small primes
if (!_useDivisor && IsCompositeByResidues(p)) if (!_useDivisor && IsCompositeByResidues(p))
{ {
searchedMersenne = false; searchedMersenne = false;
detailedCheck = false; detailedCheck = false;
_lastCompositeP = true; _lastCompositeP = true;
return false; return false;
} }
// If primes-device=gpu, route p primality through GPU-assisted sieve (optional MR later) // If primes-device=gpu, route p primality through GPU-assisted sieve (optional MR later)
// TODO: Add deterministic Miller–Rabin rounds in GPU path for 64-bit range. // TODO: Add deterministic Miller–Rabin rounds in GPU path for 64-bit range.
bool isPrimeP = GpuContextPool.ForceCpu bool isPrimeP = GpuContextPool.ForceCpu
? PrimeTesters.Value!.IsPrime(p, CancellationToken.None) ? PrimeTesters.Value!.IsPrime(p, CancellationToken.None)
: PrimeTesters.Value!.IsPrimeGpu(p, CancellationToken.None); : PrimeTesters.Value!.IsPrimeGpu(p, CancellationToken.None);
if (!isPrimeP) if (!isPrimeP)
{ {
_lastCompositeP = true; _lastCompositeP = true;
return false; return false;
} }
searchedMersenne = true; searchedMersenne = true;
if (_useDivisor) if (_useDivisor)
{ {
return _divisorTester!.IsPrime(p, _divisor, divisorCyclesSearchLimit, out detailedCheck); return _divisorTester!.IsPrime(p, _divisor, divisorCyclesSearchLimit, out detailedCheck);
} }
bool mersennePrime = MersenneTesters.Value!.IsMersennePrime(p, out bool residueExhausted); bool mersennePrime = MersenneTesters.Value!.IsMersennePrime(p, out bool residueExhausted);
detailedCheck = residueExhausted; detailedCheck = residueExhausted;
return mersennePrime; return mersennePrime;
} }
// Use ModResidueTracker with a small set of primes to pre-filter composite p. // Use ModResidueTracker with a small set of primes to pre-filter composite p.
private static bool IsCompositeByResidues(ulong p) private static bool IsCompositeByResidues(ulong p)
@@ -11,103 +11,103 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
// candidates without launching heavy kernels. Consider a device-visible constant buffer per-accelerator // candidates without launching heavy kernels. Consider a device-visible constant buffer per-accelerator
// via GpuKernelPool to avoid host round-trips. // via GpuKernelPool to avoid host round-trips.
// 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( public void Scan(
ulong exponent, ulong exponent,
UInt128 twoP, UInt128 twoP,
bool lastIsSeven, bool lastIsSeven,
UInt128 perSetLimit, UInt128 perSetLimit,
ulong setCount, ulong setCount,
UInt128 overallLimit, UInt128 overallLimit,
ref bool isPrime, ref bool isPrime,
ref bool divisorsExhausted) ref bool divisorsExhausted)
{ {
if (setCount == 0UL || perSetLimit == UInt128.Zero || overallLimit == UInt128.Zero) if (setCount == 0UL || perSetLimit == UInt128.Zero || overallLimit == UInt128.Zero)
{ {
return; return;
} }
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder); var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
var accelerator = gpuLease.Accelerator; var accelerator = gpuLease.Accelerator;
// Ensure device has small cycles and primes tables for in-kernel lookup // Ensure device has small cycles and primes tables for in-kernel lookup
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator); var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
ResiduePrimeViews primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(accelerator); ResiduePrimeViews primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(accelerator);
int batchSize = GpuConstants.ScanBatchSize; int batchSize = GpuConstants.ScanBatchSize;
byte last = lastIsSeven ? (byte)1 : (byte)0; byte last = lastIsSeven ? (byte)1 : (byte)0;
var kernel = gpuLease.Pow2ModKernel; var kernel = gpuLease.Pow2ModKernel;
ulong step10 = ((exponent % 10UL) << 1) % 10UL; ulong step10 = ((exponent % 10UL) << 1) % 10UL;
ulong step8 = ((exponent & 7UL) << 1) & 7UL; ulong step8 = ((exponent & 7UL) << 1) & 7UL;
ulong step3 = ((exponent % 3UL) << 1) % 3UL; ulong step3 = ((exponent % 3UL) << 1) % 3UL;
ulong step5 = ((exponent % 5UL) << 1) % 5UL; ulong step5 = ((exponent % 5UL) << 1) % 5UL;
GpuUInt128 twoPGpu = (GpuUInt128)twoP; GpuUInt128 twoPGpu = (GpuUInt128)twoP;
var orderBuffer = accelerator.Allocate1D<ulong>(batchSize); var orderBuffer = accelerator.Allocate1D<ulong>(batchSize);
ulong[] orderArray = ArrayPool<ulong>.Shared.Rent(batchSize); ulong[] orderArray = ArrayPool<ulong>.Shared.Rent(batchSize);
UInt128 batchSize128 = (UInt128)batchSize; UInt128 batchSize128 = (UInt128)batchSize;
UInt128 limitInclusive = overallLimit + UInt128.One; UInt128 limitInclusive = overallLimit + UInt128.One;
Span<ulong> orders = orderArray.AsSpan(0, batchSize); Span<ulong> orders = orderArray.AsSpan(0, batchSize);
try try
{ {
for (ulong setIndex = 0; setIndex < setCount && Volatile.Read(ref isPrime); setIndex++) for (ulong setIndex = 0; setIndex < setCount && Volatile.Read(ref isPrime); setIndex++)
{ {
UInt128 setOffset = perSetLimit * (UInt128)setIndex; UInt128 setOffset = perSetLimit * (UInt128)setIndex;
UInt128 setStart = setOffset + UInt128.One; UInt128 setStart = setOffset + UInt128.One;
if (setStart >= limitInclusive) if (setStart >= limitInclusive)
{ {
break; break;
} }
UInt128 setLimitExclusive = setOffset + perSetLimit + UInt128.One; UInt128 setLimitExclusive = setOffset + perSetLimit + UInt128.One;
if (setLimitExclusive > limitInclusive) if (setLimitExclusive > limitInclusive)
{ {
setLimitExclusive = limitInclusive; setLimitExclusive = limitInclusive;
} }
UInt128 kStart = setStart; UInt128 kStart = setStart;
while (kStart < setLimitExclusive && Volatile.Read(ref isPrime)) while (kStart < setLimitExclusive && Volatile.Read(ref isPrime))
{ {
UInt128 remaining = setLimitExclusive - kStart; UInt128 remaining = setLimitExclusive - kStart;
int currentSize = remaining > batchSize128 ? batchSize : (int)remaining; int currentSize = remaining > batchSize128 ? batchSize : (int)remaining;
if (currentSize <= 0) if (currentSize <= 0)
{ {
break; break;
} }
orders = orderArray.AsSpan(0, currentSize); orders = orderArray.AsSpan(0, currentSize);
UInt128 q = twoP * kStart + UInt128.One; UInt128 q = twoP * kStart + UInt128.One;
q.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3); q.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
var ra = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5); var ra = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
kernel(currentSize, exponent, twoPGpu, (GpuUInt128)kStart, last, 0UL, kernel(currentSize, exponent, twoPGpu, (GpuUInt128)kStart, last, 0UL,
ra, orderBuffer.View, smallCyclesView, primeViews.LastOne, primeViews.LastSeven, primeViews.LastOnePow2, primeViews.LastSevenPow2); ra, orderBuffer.View, smallCyclesView, primeViews.LastOne, primeViews.LastSeven, primeViews.LastOnePow2, primeViews.LastSevenPow2);
accelerator.Synchronize(); accelerator.Synchronize();
orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize); orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize);
if (!Volatile.Read(ref isPrime)) if (!Volatile.Read(ref isPrime))
{ {
break; break;
} }
for (int i = 0; i < currentSize; i++) for (int i = 0; i < currentSize; i++)
{ {
if (orders[i] != 0UL) if (orders[i] != 0UL)
{ {
Volatile.Write(ref isPrime, false); Volatile.Write(ref isPrime, false);
break; break;
} }
} }
kStart += batchSize128; kStart += batchSize128;
} }
} }
} }
finally finally
{ {
ArrayPool<ulong>.Shared.Return(orderArray); ArrayPool<ulong>.Shared.Return(orderArray);
orderBuffer.Dispose(); orderBuffer.Dispose();
gpuLease.Dispose(); gpuLease.Dispose();
} }
divisorsExhausted = true; divisorsExhausted = true;
} }
} }
@@ -5,6 +5,6 @@ public static class PerfectNumberConstants
public const ulong BiggestKnownEvenPerfectP = 136279841UL; public const ulong BiggestKnownEvenPerfectP = 136279841UL;
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 MaxQForDivisorCycles = 4_000_000; public const int MaxQForDivisorCycles = 4_000_000;
public const ulong ExtraDivisorCycleSearchLimit = 64UL; public const ulong ExtraDivisorCycleSearchLimit = 64UL;
} }