Iterate candidate exponents via prime merge walk

This commit is contained in:
Marek Lemańczyk committed 2025-09-15 23:17:49 +02:00
1 parent 1d578f0e4b
commit 3223a2fbc0
2 files changed
+127 -51

No files matched your search

@@ -12,4 +12,8 @@
<_Parameter1>EvenPerfectBitScanner.Tests</_Parameter1> <_Parameter1>EvenPerfectBitScanner.Tests</_Parameter1>
</AssemblyAttribute> </AssemblyAttribute>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Open.Numeric" Version="3.0.1" />
<PackageReference Include="Open.Numeric.Primes" Version="4.0.4" />
</ItemGroup>
</Project> </Project>
+123 -51
View File
@@ -1,6 +1,7 @@
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using Open.Numeric.Primes;
using PerfectNumbers.Core; using PerfectNumbers.Core;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
@@ -9,9 +10,9 @@ namespace EvenPerfectBitScanner;
internal static class Program internal static class Program
{ {
private static ThreadLocal<PrimeTester> PrimeTesters = null!; private static ThreadLocal<PrimeTester> PrimeTesters = null!;
private static ThreadLocal<MersenneNumberTester> MersenneTesters = null!; private static ThreadLocal<MersenneNumberTester> MersenneTesters = null!;
private static ThreadLocal<ModResidueTracker> PResidue = null!; // p mod d tracker (per-thread) private static ThreadLocal<ModResidueTracker> PResidue = null!; // p mod d tracker (per-thread)
private const ulong InitialP = PerfectNumberConstants.BiggestKnownEvenPerfectP; private const ulong InitialP = PerfectNumberConstants.BiggestKnownEvenPerfectP;
private const int ConsoleInterval = 100_000; private const int ConsoleInterval = 100_000;
private const int WriteBatchSize = 100; private const int WriteBatchSize = 100;
@@ -27,10 +28,16 @@ internal static class Program
private static bool _useGcdFilter; private static bool _useGcdFilter;
private static bool _useDivisor; private static bool _useDivisor;
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 readonly object PrimeMergeSync = new();
private static readonly TrialDivision.U64.Memoized PrimeGenerator = new();
private static IEnumerator<ulong>? _primeEnumerator;
private static bool _hasNextPrime;
private static ulong _nextPrime;
private static ulong _currentCandidate;
private static ulong _currentRemainder;
private static bool _limitReached; private static bool _limitReached;
private static string? _resultsDir; private static string? _resultsDir;
private static string? _resultsPrefix; private static string? _resultsPrefix;
@@ -568,9 +575,9 @@ internal static class Program
} }
} }
Console.WriteLine("Starting scan..."); Console.WriteLine("Starting scan...");
_state = ((long)currentP << 3) | (long)remainder; InitializePrimeMerge(currentP, remainder);
Task[] tasks = new Task[threadCount]; Task[] tasks = new Task[threadCount];
for (int i = 0; i < threadCount; i++) for (int i = 0; i < threadCount; i++)
{ {
@@ -724,51 +731,116 @@ internal static class Program
Console.WriteLine(" --help, -help, --?, -?, /? show this help message"); Console.WriteLine(" --help, -help, --?, -?, /? show this help message");
} }
private static string BuildResultsFileName(bool bitInc, int threads, int block, GpuKernelType kernelType, bool useLucasFlag, bool useDivisorFlag, bool mersenneOnGpu, bool useOrder, bool useModWorkaround, bool useGcd, NttBackend nttBackend, int gpuPrimeThreads, int llSlice, int gpuScanBatch, ulong warmupLimit, ModReductionMode reduction, string mersenneDevice, string primesDevice, string orderDevice) private static string BuildResultsFileName(bool bitInc, int threads, int block, GpuKernelType kernelType, bool useLucasFlag, bool useDivisorFlag, bool mersenneOnGpu, bool useOrder, bool useModWorkaround, bool useGcd, NttBackend nttBackend, int gpuPrimeThreads, int llSlice, int gpuScanBatch, ulong warmupLimit, ModReductionMode reduction, string mersenneDevice, string primesDevice, string orderDevice)
{ {
string inc = bitInc ? "bit" : "add"; string inc = bitInc ? "bit" : "add";
string mers = useDivisorFlag ? "divisor" : (useLucasFlag ? "lucas" : (kernelType == GpuKernelType.Pow2Mod ? "pow2mod" : "incremental")); string mers = useDivisorFlag ? "divisor" : (useLucasFlag ? "lucas" : (kernelType == GpuKernelType.Pow2Mod ? "pow2mod" : "incremental"));
string ntt = nttBackend == NttBackend.Staged ? "staged" : "reference"; string ntt = nttBackend == NttBackend.Staged ? "staged" : "reference";
string red = reduction switch { ModReductionMode.Mont64 => "mont64", ModReductionMode.Barrett128 => "barrett128", ModReductionMode.GpuUInt128 => "uint128", _ => "auto" }; string red = reduction switch { ModReductionMode.Mont64 => "mont64", ModReductionMode.Barrett128 => "barrett128", ModReductionMode.GpuUInt128 => "uint128", _ => "auto" };
string order = useOrder ? "order-on" : "order-off"; string order = useOrder ? "order-on" : "order-off";
string gcd = useGcd ? "gcd-on" : "gcd-off"; string gcd = useGcd ? "gcd-on" : "gcd-off";
string work = useModWorkaround ? "modfix-on" : "modfix-off"; string work = useModWorkaround ? "modfix-on" : "modfix-off";
return $"even_perfect_bit_scan_inc-{inc}_thr-{threads}_blk-{block}_mers-{mers}_mersdev-{mersenneDevice}_ntt-{ntt}_red-{red}_primesdev-{primesDevice}_{order}_orderdev-{orderDevice}_{gcd}_{work}_gputh-{gpuPrimeThreads}_llslice-{llSlice}_scanb-{gpuScanBatch}_warm-{warmupLimit}.csv"; return $"even_perfect_bit_scan_inc-{inc}_thr-{threads}_blk-{block}_mers-{mers}_mersdev-{mersenneDevice}_ntt-{ntt}_red-{red}_primesdev-{primesDevice}_{order}_orderdev-{orderDevice}_{gcd}_{work}_gputh-{gpuPrimeThreads}_llslice-{llSlice}_scanb-{gpuScanBatch}_warm-{warmupLimit}.csv";
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe int ReserveBlock(ulong[] buffer, int blockSize) private static void InitializePrimeMerge(ulong startCandidate, ulong startRemainder)
{ {
Span<ulong> bufferSpan = new(buffer); lock (PrimeMergeSync)
ulong p, remainder; {
long state, newState, original; _currentCandidate = startCandidate;
int count; _currentRemainder = startRemainder;
while (true) InitializePrimeEnumerator(startCandidate);
{ }
state = Volatile.Read(ref _state); }
p = (ulong)state >> 3;
remainder = ((ulong)state) & 7UL;
count = 0; [MethodImpl(MethodImplOptions.AggressiveInlining)]
while (count < blockSize && !Volatile.Read(ref _limitReached)) private static void InitializePrimeEnumerator(ulong startValue)
{ {
bufferSpan[count++] = p; (_primeEnumerator as IDisposable)?.Dispose();
p = _transformP(p, ref remainder); ulong begin = startValue;
} _primeEnumerator = PrimeGenerator.StartingAt(in begin).GetEnumerator();
_hasNextPrime = MoveToNextPrime();
}
if (count == 0) [MethodImpl(MethodImplOptions.AggressiveInlining)]
{ private static bool MoveToNextPrime()
return 0; {
} if (_primeEnumerator is null)
{
return false;
}
newState = ((long)p << 3) | (long)remainder; if (_primeEnumerator.MoveNext())
original = Interlocked.CompareExchange(ref _state, newState, state); {
if (original == state) _nextPrime = _primeEnumerator.Current;
{ return true;
return count; }
}
} Volatile.Write(ref _limitReached, true);
} return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool EnsurePrimeForCandidate(ulong candidate)
{
if (!_hasNextPrime)
{
return false;
}
while (_hasNextPrime && _nextPrime < candidate)
{
_hasNextPrime = MoveToNextPrime();
}
return _hasNextPrime;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe int ReserveBlock(ulong[] buffer, int blockSize)
{
Span<ulong> bufferSpan = new(buffer);
int count = 0;
lock (PrimeMergeSync)
{
if (!_hasNextPrime && !MoveToNextPrime())
{
return 0;
}
while (count < blockSize && !Volatile.Read(ref _limitReached))
{
ulong candidate = _currentCandidate;
if (!EnsurePrimeForCandidate(candidate))
{
break;
}
if (_hasNextPrime && _nextPrime == candidate)
{
bufferSpan[count++] = candidate;
_hasNextPrime = MoveToNextPrime();
if (!_hasNextPrime)
{
_currentCandidate = _transformP(candidate, ref _currentRemainder);
break;
}
}
_currentCandidate = _transformP(candidate, ref _currentRemainder);
if (Volatile.Read(ref _limitReached))
{
break;
}
}
}
return count;
}
[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)