Rework by-divisor scanning order
This commit is contained in:
1 parent
9a054542e6
commit
f91f526121
3 files changed
+354
-2
No files matched your search
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Buffers;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
@@ -31,6 +32,7 @@ internal static class Program
|
|||||||
private static bool _useDivisor;
|
private static bool _useDivisor;
|
||||||
private static bool _useResidueMode;
|
private static bool _useResidueMode;
|
||||||
private static bool _useByDivisorMode;
|
private static bool _useByDivisorMode;
|
||||||
|
private static bool _byDivisorPrecheckOnly;
|
||||||
private static UInt128 _divisor;
|
private static UInt128 _divisor;
|
||||||
private static MersenneNumberDivisorGpuTester? _divisorTester;
|
private static MersenneNumberDivisorGpuTester? _divisorTester;
|
||||||
private static MersenneNumberDivisorByDivisorGpuTester? _byDivisorTester;
|
private static MersenneNumberDivisorByDivisorGpuTester? _byDivisorTester;
|
||||||
@@ -54,6 +56,15 @@ internal static class Program
|
|||||||
private static double _zeroFracConj = -1.0; // disabled when < 0
|
private static double _zeroFracConj = -1.0; // disabled when < 0
|
||||||
private static int _maxZeroConj = -1; // disabled when < 0
|
private static int _maxZeroConj = -1; // disabled when < 0
|
||||||
|
|
||||||
|
private struct ByDivisorPrimeState
|
||||||
|
{
|
||||||
|
internal ulong Prime;
|
||||||
|
internal ulong AllowedMax;
|
||||||
|
internal bool Completed;
|
||||||
|
internal bool Composite;
|
||||||
|
internal bool DetailedCheck;
|
||||||
|
}
|
||||||
|
|
||||||
private static unsafe void Main(string[] args)
|
private static unsafe void Main(string[] args)
|
||||||
{
|
{
|
||||||
ulong currentP = InitialP;
|
ulong currentP = InitialP;
|
||||||
@@ -577,6 +588,7 @@ internal static class Program
|
|||||||
|
|
||||||
bool useFilter = !string.IsNullOrEmpty(filterFile);
|
bool useFilter = !string.IsNullOrEmpty(filterFile);
|
||||||
HashSet<ulong> filter = [];
|
HashSet<ulong> filter = [];
|
||||||
|
List<ulong> filterList = [];
|
||||||
ulong maxP = 0UL;
|
ulong maxP = 0UL;
|
||||||
if (useFilter)
|
if (useFilter)
|
||||||
{
|
{
|
||||||
@@ -606,8 +618,7 @@ internal static class Program
|
|||||||
{
|
{
|
||||||
filter.AddRange(localFilter[..count]);
|
filter.AddRange(localFilter[..count]);
|
||||||
}
|
}
|
||||||
// Restore this if you want to use List<ulong> instead of HashSet<ulong>
|
filterList = [..filter];
|
||||||
// filter.Sort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useByDivisor)
|
if (useByDivisor)
|
||||||
@@ -633,6 +644,14 @@ internal static class Program
|
|||||||
// Configure batch size for GPU primality sieve
|
// Configure batch size for GPU primality sieve
|
||||||
PrimeTester.GpuBatchSize = gpuPrimeBatch;
|
PrimeTester.GpuBatchSize = gpuPrimeBatch;
|
||||||
|
|
||||||
|
if (_useByDivisorMode)
|
||||||
|
{
|
||||||
|
RunByDivisorMode(filterList, divisorCyclesSearchLimit);
|
||||||
|
FlushBuffer();
|
||||||
|
StringBuilderPool.Return(_outputBuilder!);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!useDivisor && !useByDivisor)
|
if (!useDivisor && !useByDivisor)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Warming up orders...");
|
Console.WriteLine("Warming up orders...");
|
||||||
@@ -716,6 +735,182 @@ internal static class Program
|
|||||||
StringBuilderPool.Return(_outputBuilder!);
|
StringBuilderPool.Return(_outputBuilder!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void RunByDivisorMode(List<ulong> primes, ulong divisorCyclesSearchLimit)
|
||||||
|
{
|
||||||
|
if (primes.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
primes.Sort();
|
||||||
|
List<ByDivisorPrimeState> states = new(primes.Count);
|
||||||
|
|
||||||
|
_byDivisorPrecheckOnly = true;
|
||||||
|
foreach (ulong prime in primes)
|
||||||
|
{
|
||||||
|
bool passed = IsEvenPerfectCandidate(prime, divisorCyclesSearchLimit, out bool searched, out bool detailed);
|
||||||
|
if (!passed)
|
||||||
|
{
|
||||||
|
PrintResult(prime, searched, detailed, false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong allowedMax = _byDivisorTester!.GetAllowedMaxDivisor(prime);
|
||||||
|
if (allowedMax < 3UL)
|
||||||
|
{
|
||||||
|
PrintResult(prime, searchedMersenne: true, detailedCheck: true, passedAllTests: true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
states.Add(new ByDivisorPrimeState
|
||||||
|
{
|
||||||
|
Prime = prime,
|
||||||
|
AllowedMax = allowedMax,
|
||||||
|
Completed = false,
|
||||||
|
Composite = false,
|
||||||
|
DetailedCheck = false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_byDivisorPrecheckOnly = false;
|
||||||
|
|
||||||
|
if (states.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var session = _byDivisorTester!.CreateDivisorSession();
|
||||||
|
byte[] hitsBuffer = ArrayPool<byte>.Shared.Rent(states.Count);
|
||||||
|
ulong[] primeBuffer = ArrayPool<ulong>.Shared.Rent(states.Count);
|
||||||
|
int[] indexBuffer = ArrayPool<int>.Shared.Rent(states.Count);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ulong divisor = 3UL;
|
||||||
|
ulong divisorLimit = _byDivisorTester!.DivisorLimit;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
bool anyPending = false;
|
||||||
|
int activeCount = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < states.Count; i++)
|
||||||
|
{
|
||||||
|
var state = states[i];
|
||||||
|
if (state.Completed)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.AllowedMax < divisor)
|
||||||
|
{
|
||||||
|
state.Completed = true;
|
||||||
|
state.DetailedCheck = true;
|
||||||
|
states[i] = state;
|
||||||
|
PrintResult(state.Prime, searchedMersenne: true, detailedCheck: true, passedAllTests: true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
anyPending = true;
|
||||||
|
|
||||||
|
if (divisor > divisorLimit)
|
||||||
|
{
|
||||||
|
states[i] = state;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
primeBuffer[activeCount] = state.Prime;
|
||||||
|
indexBuffer[activeCount] = i;
|
||||||
|
activeCount++;
|
||||||
|
states[i] = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!anyPending || divisor > divisorLimit)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeCount == 0)
|
||||||
|
{
|
||||||
|
ulong nextDivisor = divisor + 2UL;
|
||||||
|
if (nextDivisor <= divisor)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
divisor = nextDivisor;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
session.CheckDivisor(divisor, primeBuffer.AsSpan(0, activeCount), hitsBuffer.AsSpan(0, activeCount));
|
||||||
|
|
||||||
|
for (int i = 0; i < activeCount; i++)
|
||||||
|
{
|
||||||
|
if (hitsBuffer[i] == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = indexBuffer[i];
|
||||||
|
var state = states[index];
|
||||||
|
if (state.Completed)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.Completed = true;
|
||||||
|
state.Composite = true;
|
||||||
|
state.DetailedCheck = true;
|
||||||
|
states[index] = state;
|
||||||
|
PrintResult(state.Prime, searchedMersenne: true, detailedCheck: true, passedAllTests: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool remaining = false;
|
||||||
|
for (int i = 0; i < states.Count; i++)
|
||||||
|
{
|
||||||
|
if (!states[i].Completed)
|
||||||
|
{
|
||||||
|
remaining = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!remaining)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong next = divisor + 2UL;
|
||||||
|
if (next <= divisor)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
divisor = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < states.Count; i++)
|
||||||
|
{
|
||||||
|
var state = states[i];
|
||||||
|
if (state.Completed)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.Completed = true;
|
||||||
|
state.DetailedCheck = divisor > state.AllowedMax;
|
||||||
|
states[i] = state;
|
||||||
|
PrintResult(state.Prime, searchedMersenne: true, detailedCheck: state.DetailedCheck, passedAllTests: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ArrayPool<int>.Shared.Return(indexBuffer, clearArray: true);
|
||||||
|
ArrayPool<ulong>.Shared.Return(primeBuffer, clearArray: true);
|
||||||
|
ArrayPool<byte>.Shared.Return(hitsBuffer, clearArray: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static unsafe void LoadResultsFile(string resultsFileName, Action<ulong, bool, bool> lineProcessorAction)
|
private static unsafe void LoadResultsFile(string resultsFileName, Action<ulong, bool, bool> lineProcessorAction)
|
||||||
{
|
{
|
||||||
using FileStream readStream = new(resultsFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
using FileStream readStream = new(resultsFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||||
@@ -1163,6 +1358,13 @@ internal static class Program
|
|||||||
searchedMersenne = true;
|
searchedMersenne = true;
|
||||||
if (_useByDivisorMode)
|
if (_useByDivisorMode)
|
||||||
{
|
{
|
||||||
|
if (_byDivisorPrecheckOnly)
|
||||||
|
{
|
||||||
|
searchedMersenne = false;
|
||||||
|
detailedCheck = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return _byDivisorTester!.IsPrime(p, out detailedCheck);
|
return _byDivisorTester!.IsPrime(p, out detailedCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -103,5 +103,23 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
divisorsExhausted.Should().BeTrue();
|
divisorsExhausted.Should().BeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Category", "Fast")]
|
||||||
|
public void ByDivisor_session_checks_divisors_across_primes()
|
||||||
|
{
|
||||||
|
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||||
|
tester.ConfigureFromMaxPrime(13UL);
|
||||||
|
|
||||||
|
using var session = tester.CreateDivisorSession();
|
||||||
|
ulong[] primes = { 5UL, 7UL, 11UL, 13UL };
|
||||||
|
byte[] hits = new byte[primes.Length];
|
||||||
|
|
||||||
|
session.CheckDivisor(23UL, primes, hits);
|
||||||
|
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
|
||||||
|
|
||||||
|
Array.Fill(hits, (byte)0);
|
||||||
|
session.CheckDivisor(31UL, primes, hits);
|
||||||
|
hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,10 +17,14 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
|
|||||||
private ulong _lastStatusDivisor;
|
private ulong _lastStatusDivisor;
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ArrayView<ulong>, ArrayView<byte>>> _kernelCache = new();
|
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ArrayView<ulong>, ArrayView<byte>>> _kernelCache = new();
|
||||||
|
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ArrayView<ulong>, ArrayView<byte>>> _kernelByPrimeCache = new();
|
||||||
|
|
||||||
private Action<Index1D, ulong, ArrayView<ulong>, ArrayView<byte>> GetKernel(Accelerator accelerator) =>
|
private Action<Index1D, ulong, ArrayView<ulong>, ArrayView<byte>> GetKernel(Accelerator accelerator) =>
|
||||||
_kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<ulong>, ArrayView<byte>>(CheckKernel));
|
_kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<ulong>, ArrayView<byte>>(CheckKernel));
|
||||||
|
|
||||||
|
private Action<Index1D, ulong, ArrayView<ulong>, ArrayView<byte>> GetKernelByPrime(Accelerator accelerator) =>
|
||||||
|
_kernelByPrimeCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<ulong>, ArrayView<byte>>(CheckKernelByPrime));
|
||||||
|
|
||||||
public int GpuBatchSize
|
public int GpuBatchSize
|
||||||
{
|
{
|
||||||
get => _gpuBatchSize;
|
get => _gpuBatchSize;
|
||||||
@@ -37,6 +41,48 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ulong DivisorLimit
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_sync)
|
||||||
|
{
|
||||||
|
if (!_isConfigured)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return _divisorLimit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong GetAllowedMaxDivisor(ulong prime)
|
||||||
|
{
|
||||||
|
lock (_sync)
|
||||||
|
{
|
||||||
|
if (!_isConfigured)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ComputeAllowedMaxDivisor(prime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DivisorScanSession CreateDivisorSession()
|
||||||
|
{
|
||||||
|
lock (_sync)
|
||||||
|
{
|
||||||
|
if (!_isConfigured)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DivisorScanSession(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsPrime(ulong prime, out bool divisorsExhausted)
|
public bool IsPrime(ulong prime, out bool divisorsExhausted)
|
||||||
{
|
{
|
||||||
lock (_sync)
|
lock (_sync)
|
||||||
@@ -179,6 +225,79 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
|
|||||||
return composite;
|
return composite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class DivisorScanSession : IDisposable
|
||||||
|
{
|
||||||
|
private readonly MersenneNumberDivisorByDivisorGpuTester _owner;
|
||||||
|
private readonly GpuContextPool.GpuContextLease _lease;
|
||||||
|
private readonly Accelerator _accelerator;
|
||||||
|
private readonly Action<Index1D, ulong, ArrayView<ulong>, ArrayView<byte>> _kernel;
|
||||||
|
private readonly MemoryBuffer1D<ulong, Stride1D.Dense> _primesBuffer;
|
||||||
|
private readonly MemoryBuffer1D<byte, Stride1D.Dense> _hitsBuffer;
|
||||||
|
private readonly ulong[] _primesHost;
|
||||||
|
private readonly byte[] _hitsHost;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner)
|
||||||
|
{
|
||||||
|
_owner = owner;
|
||||||
|
_lease = GpuContextPool.RentPreferred(preferCpu: false);
|
||||||
|
_accelerator = _lease.Accelerator;
|
||||||
|
_kernel = owner.GetKernelByPrime(_accelerator);
|
||||||
|
_primesBuffer = _accelerator.Allocate1D<ulong>(owner._gpuBatchSize);
|
||||||
|
_hitsBuffer = _accelerator.Allocate1D<byte>(owner._gpuBatchSize);
|
||||||
|
_primesHost = ArrayPool<ulong>.Shared.Rent(owner._gpuBatchSize);
|
||||||
|
_hitsHost = ArrayPool<byte>.Shared.Rent(owner._gpuBatchSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CheckDivisor(ulong divisor, ReadOnlySpan<ulong> primes, Span<byte> hits)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(nameof(DivisorScanSession));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (primes.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
while (offset < primes.Length)
|
||||||
|
{
|
||||||
|
int batchSize = Math.Min(_owner._gpuBatchSize, primes.Length - offset);
|
||||||
|
Span<ulong> primeSpan = _primesHost.AsSpan(0, batchSize);
|
||||||
|
primes.Slice(offset, batchSize).CopyTo(primeSpan);
|
||||||
|
|
||||||
|
var primesView = _primesBuffer.View.SubView(0, batchSize);
|
||||||
|
var hitsView = _hitsBuffer.View.SubView(0, batchSize);
|
||||||
|
|
||||||
|
primesView.CopyFromCPU(ref MemoryMarshal.GetReference(primeSpan), batchSize);
|
||||||
|
_kernel(batchSize, divisor, primesView, hitsView);
|
||||||
|
_accelerator.Synchronize();
|
||||||
|
|
||||||
|
Span<byte> hitSpan = _hitsHost.AsSpan(0, batchSize);
|
||||||
|
hitsView.CopyToCPU(ref MemoryMarshal.GetReference(hitSpan), batchSize);
|
||||||
|
hitSpan.CopyTo(hits.Slice(offset, batchSize));
|
||||||
|
offset += batchSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
ArrayPool<ulong>.Shared.Return(_primesHost, clearArray: true);
|
||||||
|
ArrayPool<byte>.Shared.Return(_hitsHost, clearArray: true);
|
||||||
|
_primesBuffer.Dispose();
|
||||||
|
_hitsBuffer.Dispose();
|
||||||
|
_lease.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ReportStatus(ulong divisor)
|
private void ReportStatus(ulong divisor)
|
||||||
{
|
{
|
||||||
if (divisor <= _lastStatusDivisor)
|
if (divisor <= _lastStatusDivisor)
|
||||||
@@ -234,6 +353,19 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
|
|||||||
hits[index] = pow == 1UL ? (byte)1 : (byte)0;
|
hits[index] = pow == 1UL ? (byte)1 : (byte)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void CheckKernelByPrime(Index1D index, ulong divisor, ArrayView<ulong> primes, ArrayView<byte> hits)
|
||||||
|
{
|
||||||
|
ulong prime = primes[index];
|
||||||
|
if (divisor <= 1UL || (divisor & 1UL) == 0UL)
|
||||||
|
{
|
||||||
|
hits[index] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong pow = Pow2Mod(prime, divisor);
|
||||||
|
hits[index] = pow == 1UL ? (byte)1 : (byte)0;
|
||||||
|
}
|
||||||
|
|
||||||
private static ulong Pow2Mod(ulong exponent, ulong modulus)
|
private static ulong Pow2Mod(ulong exponent, ulong modulus)
|
||||||
{
|
{
|
||||||
if (modulus <= 1UL)
|
if (modulus <= 1UL)
|
||||||
|
|||||||
Reference in new issue
Block a user