Enable multi-threaded divisor scanning

This commit is contained in:
Marek Lemańczyk committed 2025-09-21 13:44:18 +02:00
1 parent 198b31f380
commit bf02826f7c
1 file changed
+238 -132
+238 -132
View File
@@ -54,14 +54,30 @@ 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 private struct ByDivisorPrimeState
{ {
internal ulong Prime; internal ulong Prime;
internal ulong AllowedMax; internal ulong AllowedMax;
internal bool Completed; internal bool Completed;
internal bool Composite; internal bool Composite;
internal bool DetailedCheck; internal bool DetailedCheck;
} }
private readonly struct PendingResult
{
internal PendingResult(ulong prime, bool detailedCheck, bool passedAllTests)
{
Prime = prime;
DetailedCheck = detailedCheck;
PassedAllTests = passedAllTests;
}
internal ulong Prime { get; }
internal bool DetailedCheck { get; }
internal bool PassedAllTests { get; }
}
private static unsafe void Main(string[] args) private static unsafe void Main(string[] args)
{ {
@@ -644,9 +660,9 @@ internal static class Program
Console.WriteLine("Starting scan..."); Console.WriteLine("Starting scan...");
if (_useByDivisorMode) if (_useByDivisorMode)
{ {
RunByDivisorMode(filterList, divisorCyclesSearchLimit); RunByDivisorMode(filterList, divisorCyclesSearchLimit, threadCount);
FlushBuffer(); FlushBuffer();
StringBuilderPool.Return(_outputBuilder!); StringBuilderPool.Return(_outputBuilder!);
return; return;
@@ -734,12 +750,12 @@ internal static class Program
StringBuilderPool.Return(_outputBuilder!); StringBuilderPool.Return(_outputBuilder!);
} }
private static void RunByDivisorMode(List<ulong> primes, ulong divisorCyclesSearchLimit) private static void RunByDivisorMode(List<ulong> primes, ulong divisorCyclesSearchLimit, int threadCount)
{ {
if (primes.Count == 0) if (primes.Count == 0)
{ {
return; return;
} }
List<ByDivisorPrimeState> states = new(primes.Count); List<ByDivisorPrimeState> states = new(primes.Count);
@@ -772,142 +788,232 @@ internal static class Program
_byDivisorPrecheckOnly = false; _byDivisorPrecheckOnly = false;
if (states.Count == 0) if (states.Count == 0)
{ {
return; return;
} }
using var session = _byDivisorTester!.CreateDivisorSession(); object stateLock = new();
byte[] hitsBuffer = ArrayPool<byte>.Shared.Rent(states.Count); ulong divisorLimit = _byDivisorTester!.DivisorLimit;
ulong[] primeBuffer = ArrayPool<ulong>.Shared.Rent(states.Count); ulong nextDivisor = 3UL;
int[] indexBuffer = ArrayPool<int>.Shared.Rent(states.Count); bool divisorsExhausted = false;
ulong finalDivisorValue = 3UL;
int remainingStates = states.Count;
try Task[] workers = new Task[Math.Max(1, threadCount)];
{
ulong divisor = 3UL;
ulong divisorLimit = _byDivisorTester!.DivisorLimit;
while (true) for (int i = 0; i < workers.Length; i++)
{ {
bool anyPending = false; workers[i] = Task.Run(() =>
int activeCount = 0; {
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);
List<PendingResult> completions = new(states.Count);
List<PendingResult> composites = new(states.Count);
for (int i = 0; i < states.Count; i++) try
{ {
var state = states[i]; while (true)
if (state.Completed) {
{ ulong divisor;
continue; int activeCount;
} bool shouldTerminate;
bool hasWork;
if (state.AllowedMax < divisor) lock (stateLock)
{ {
state.Completed = true; hasWork = TryAcquireDivisorWork(states, divisorLimit, primeBuffer, indexBuffer, completions, ref nextDivisor, ref divisorsExhausted, ref finalDivisorValue, ref remainingStates, out divisor, out activeCount, out shouldTerminate);
state.DetailedCheck = true; }
states[i] = state;
PrintResult(state.Prime, searchedMersenne: true, detailedCheck: true, passedAllTests: true);
continue;
}
anyPending = true; if (completions.Count > 0)
{
foreach (PendingResult result in completions)
{
PrintResult(result.Prime, searchedMersenne: true, detailedCheck: result.DetailedCheck, passedAllTests: result.PassedAllTests);
}
if (divisor > divisorLimit) completions.Clear();
{ }
states[i] = state;
continue;
}
primeBuffer[activeCount] = state.Prime; if (!hasWork)
indexBuffer[activeCount] = i; {
activeCount++; if (shouldTerminate)
states[i] = state; {
} break;
}
if (!anyPending || divisor > divisorLimit) continue;
{ }
break;
}
if (activeCount == 0) Span<byte> hitsSpan = hitsBuffer.AsSpan(0, activeCount);
{ hitsSpan.Clear();
ulong nextDivisor = divisor + 2UL; session.CheckDivisor(divisor, primeBuffer.AsSpan(0, activeCount), hitsSpan);
if (nextDivisor <= divisor)
{
break;
}
divisor = nextDivisor; bool exitAfterIteration;
continue; composites.Clear();
}
session.CheckDivisor(divisor, primeBuffer.AsSpan(0, activeCount), hitsBuffer.AsSpan(0, activeCount)); lock (stateLock)
{
for (int j = 0; j < activeCount; j++)
{
if (hitsSpan[j] == 0)
{
continue;
}
for (int i = 0; i < activeCount; i++) int index = indexBuffer[j];
{ var state = states[index];
if (hitsBuffer[i] == 0) if (state.Completed)
{ {
continue; continue;
} }
int index = indexBuffer[i]; state.Completed = true;
var state = states[index]; state.Composite = true;
if (state.Completed) state.DetailedCheck = true;
{ states[index] = state;
continue; remainingStates--;
} composites.Add(new PendingResult(state.Prime, state.DetailedCheck, false));
}
state.Completed = true; exitAfterIteration = remainingStates == 0;
state.Composite = true; if (exitAfterIteration && !divisorsExhausted)
state.DetailedCheck = true; {
states[index] = state; divisorsExhausted = true;
PrintResult(state.Prime, searchedMersenne: true, detailedCheck: true, passedAllTests: false); finalDivisorValue = Math.Max(finalDivisorValue, nextDivisor);
} }
}
bool remaining = false; if (composites.Count > 0)
for (int i = 0; i < states.Count; i++) {
{ foreach (PendingResult result in composites)
if (!states[i].Completed) {
{ PrintResult(result.Prime, searchedMersenne: true, detailedCheck: result.DetailedCheck, passedAllTests: result.PassedAllTests);
remaining = true; }
break; }
}
}
if (!remaining) if (exitAfterIteration)
{ {
break; break;
} }
}
}
finally
{
ArrayPool<int>.Shared.Return(indexBuffer, clearArray: true);
ArrayPool<ulong>.Shared.Return(primeBuffer, clearArray: true);
ArrayPool<byte>.Shared.Return(hitsBuffer, clearArray: true);
}
});
}
ulong next = divisor + 2UL; Task.WaitAll(workers);
if (next <= divisor) }
{
break;
}
divisor = next; private static bool TryAcquireDivisorWork(List<ByDivisorPrimeState> states, ulong divisorLimit, ulong[] primeBuffer, int[] indexBuffer, List<PendingResult> completions, ref ulong nextDivisor, ref bool divisorsExhausted, ref ulong finalDivisorValue, ref int remainingStates, out ulong divisor, out int activeCount, out bool shouldTerminate)
} {
completions.Clear();
for (int i = 0; i < states.Count; i++) while (true)
{ {
var state = states[i]; if (remainingStates == 0)
if (state.Completed) {
{ divisor = 0UL;
continue; activeCount = 0;
} shouldTerminate = true;
return false;
}
state.Completed = true; if (divisorsExhausted || nextDivisor > divisorLimit)
state.DetailedCheck = divisor > state.AllowedMax; {
states[i] = state; if (!divisorsExhausted)
PrintResult(state.Prime, searchedMersenne: true, detailedCheck: state.DetailedCheck, passedAllTests: true); {
} divisorsExhausted = true;
} finalDivisorValue = nextDivisor;
finally }
{
ArrayPool<int>.Shared.Return(indexBuffer, clearArray: true); ulong completionDivisor = finalDivisorValue;
ArrayPool<ulong>.Shared.Return(primeBuffer, clearArray: true); for (int i = 0; i < states.Count; i++)
ArrayPool<byte>.Shared.Return(hitsBuffer, clearArray: true); {
} var state = states[i];
} if (state.Completed)
{
continue;
}
state.Completed = true;
state.DetailedCheck = completionDivisor > state.AllowedMax;
states[i] = state;
remainingStates--;
completions.Add(new PendingResult(state.Prime, state.DetailedCheck, true));
}
divisor = 0UL;
activeCount = 0;
shouldTerminate = true;
return false;
}
ulong divisorValue = nextDivisor;
ulong candidate = divisorValue + 2UL;
if (candidate <= divisorValue)
{
divisorsExhausted = true;
finalDivisorValue = divisorValue;
}
else
{
nextDivisor = candidate;
if (nextDivisor > divisorLimit)
{
divisorsExhausted = true;
finalDivisorValue = nextDivisor;
}
}
activeCount = 0;
for (int i = 0; i < states.Count; i++)
{
var state = states[i];
if (state.Completed)
{
continue;
}
if (state.AllowedMax < divisorValue)
{
state.Completed = true;
state.DetailedCheck = true;
states[i] = state;
remainingStates--;
completions.Add(new PendingResult(state.Prime, state.DetailedCheck, true));
continue;
}
primeBuffer[activeCount] = state.Prime;
indexBuffer[activeCount] = i;
activeCount++;
}
if (activeCount == 0)
{
if (completions.Count == 0)
{
continue;
}
divisor = 0UL;
shouldTerminate = remainingStates == 0 && (divisorsExhausted || nextDivisor > divisorLimit);
return false;
}
divisor = divisorValue;
shouldTerminate = false;
return 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)
{ {