Improve by-divisor scanning concurrency

This commit is contained in:
Marek Lemańczyk committed 2025-09-21 14:38:16 +02:00
1 parent d117921c3a
commit bee6b69793
1 file changed
+148 -120
+148 -120
View File
@@ -3,6 +3,7 @@ using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using Open.Collections;
using Open.Numeric.Primes;
using PerfectNumbers.Core;
@@ -54,6 +55,11 @@ internal static class Program
private static double _zeroFracConj = -1.0; // disabled when < 0
private static int _maxZeroConj = -1; // disabled when < 0
private const int ByDivisorStateActive = 0;
private const int ByDivisorStateComposite = 1;
private const int ByDivisorStateCompleted = 2;
private const int ByDivisorStateCompletedDetailed = 3;
private struct ByDivisorPrimeState
{
internal ulong Prime;
@@ -793,39 +799,73 @@ internal static class Program
return;
}
object stateLock = new();
int stateCount = states.Count;
ulong[] primeValues = new ulong[stateCount];
ulong[] allowedMaxValues = new ulong[stateCount];
int[] stateFlags = new int[stateCount];
for (int i = 0; i < stateCount; i++)
{
var state = states[i];
primeValues[i] = state.Prime;
allowedMaxValues[i] = state.AllowedMax;
}
states.Clear();
ulong divisorLimit = _byDivisorTester!.DivisorLimit;
ulong nextDivisor = 3UL;
bool divisorsExhausted = false;
ulong finalDivisorValue = 3UL;
int remainingStates = states.Count;
long finalDivisorBits = unchecked((long)3UL);
int divisorsExhaustedFlag = 0;
int finalizerState = 0;
int finalizationCompleted = 0;
int remainingStates = stateCount;
Task[] workers = new Task[Math.Max(1, threadCount)];
for (int i = 0; i < workers.Length; i++)
for (int workerIndex = 0; workerIndex < workers.Length; workerIndex++)
{
workers[i] = Task.Run(() =>
workers[workerIndex] = Task.Run(() =>
{
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);
byte[] hitsBuffer = ArrayPool<byte>.Shared.Rent(stateCount);
ulong[] primeBuffer = ArrayPool<ulong>.Shared.Rent(stateCount);
int[] indexBuffer = ArrayPool<int>.Shared.Rent(stateCount);
List<PendingResult> completions = new(stateCount);
List<PendingResult> composites = new(stateCount);
try
{
while (true)
{
ulong divisor;
int activeCount;
bool shouldTerminate;
bool hasWork;
lock (stateLock)
if (Volatile.Read(ref remainingStates) == 0)
{
hasWork = TryAcquireDivisorWork(states, divisorLimit, primeBuffer, indexBuffer, completions, ref nextDivisor, ref divisorsExhausted, ref finalDivisorValue, ref remainingStates, out divisor, out activeCount, out shouldTerminate);
if (Volatile.Read(ref finalizationCompleted) != 0 || Volatile.Read(ref divisorsExhaustedFlag) == 0)
{
break;
}
}
bool exhausted;
ulong divisor = AcquireNextDivisor(ref nextDivisor, divisorLimit, ref divisorsExhaustedFlag, ref finalDivisorBits, out exhausted);
if (divisor == 0UL)
{
if (!exhausted)
{
if (Volatile.Read(ref remainingStates) == 0)
{
break;
}
Thread.Yield();
continue;
}
if (Interlocked.CompareExchange(ref finalizerState, 1, 0) == 0)
{
completions.Clear();
FinalizeRemainingStates(primeValues, allowedMaxValues, stateFlags, ref remainingStates, ref finalDivisorBits, completions);
if (completions.Count > 0)
{
@@ -837,9 +877,17 @@ internal static class Program
completions.Clear();
}
if (!hasWork)
Volatile.Write(ref finalizationCompleted, 1);
}
else
{
if (shouldTerminate)
while (Volatile.Read(ref finalizationCompleted) == 0 && Volatile.Read(ref remainingStates) > 0)
{
Thread.Yield();
}
}
if (Volatile.Read(ref remainingStates) == 0)
{
break;
}
@@ -847,15 +895,27 @@ internal static class Program
continue;
}
int activeCount = BuildPrimeBuffer(divisor, primeValues, allowedMaxValues, stateFlags, primeBuffer, indexBuffer, completions, ref remainingStates);
if (completions.Count > 0)
{
foreach (PendingResult result in completions)
{
PrintResult(result.Prime, searchedMersenne: true, detailedCheck: result.DetailedCheck, passedAllTests: result.PassedAllTests);
}
completions.Clear();
}
if (activeCount == 0)
{
continue;
}
Span<byte> hitsSpan = hitsBuffer.AsSpan(0, activeCount);
hitsSpan.Clear();
session.CheckDivisor(divisor, primeBuffer.AsSpan(0, activeCount), hitsSpan);
bool exitAfterIteration;
composites.Clear();
lock (stateLock)
{
for (int j = 0; j < activeCount; j++)
{
if (hitsSpan[j] == 0)
@@ -864,25 +924,10 @@ internal static class Program
}
int index = indexBuffer[j];
var state = states[index];
if (state.Completed)
if (Interlocked.CompareExchange(ref stateFlags[index], ByDivisorStateComposite, ByDivisorStateActive) == ByDivisorStateActive)
{
continue;
}
state.Completed = true;
state.Composite = true;
state.DetailedCheck = true;
states[index] = state;
remainingStates--;
composites.Add(new PendingResult(state.Prime, state.DetailedCheck, false));
}
exitAfterIteration = remainingStates == 0;
if (exitAfterIteration && !divisorsExhausted)
{
divisorsExhausted = true;
finalDivisorValue = Math.Max(finalDivisorValue, nextDivisor);
Interlocked.Decrement(ref remainingStates);
composites.Add(new PendingResult(primeValues[index], detailedCheck: true, passedAllTests: false));
}
}
@@ -892,11 +937,8 @@ internal static class Program
{
PrintResult(result.Prime, searchedMersenne: true, detailedCheck: result.DetailedCheck, passedAllTests: result.PassedAllTests);
}
}
if (exitAfterIteration)
{
break;
composites.Clear();
}
}
}
@@ -912,106 +954,92 @@ internal static class Program
Task.WaitAll(workers);
}
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)
private static ulong AcquireNextDivisor(ref ulong nextDivisor, ulong divisorLimit, ref int divisorsExhaustedFlag, ref long finalDivisorBits, out bool exhausted)
{
completions.Clear();
ref long nextDivisorBits = ref Unsafe.As<ulong, long>(ref nextDivisor);
while (true)
{
if (remainingStates == 0)
if (Volatile.Read(ref divisorsExhaustedFlag) != 0)
{
divisor = 0UL;
activeCount = 0;
shouldTerminate = true;
return false;
exhausted = true;
return 0UL;
}
if (divisorsExhausted || nextDivisor > divisorLimit)
long currentBits = Volatile.Read(ref nextDivisorBits);
ulong currentValue = unchecked((ulong)currentBits);
if (currentValue > divisorLimit)
{
if (!divisorsExhausted)
if (Interlocked.CompareExchange(ref divisorsExhaustedFlag, 1, 0) == 0)
{
divisorsExhausted = true;
finalDivisorValue = nextDivisor;
Volatile.Write(ref finalDivisorBits, currentBits);
}
ulong completionDivisor = finalDivisorValue;
for (int i = 0; i < states.Count; i++)
exhausted = true;
return 0UL;
}
ulong nextValue = currentValue + 2UL;
long nextBits = unchecked((long)nextValue);
if (Interlocked.CompareExchange(ref nextDivisorBits, nextBits, currentBits) == currentBits)
{
var state = states[i];
if (state.Completed)
exhausted = false;
return currentValue;
}
}
}
private static int BuildPrimeBuffer(ulong divisor, ulong[] primeValues, ulong[] allowedMaxValues, int[] stateFlags, ulong[] primeBuffer, int[] indexBuffer, List<PendingResult> completions, ref int remainingStates)
{
int activeCount = 0;
for (int i = 0; i < primeValues.Length; i++)
{
if (allowedMaxValues[i] < divisor)
{
if (Interlocked.CompareExchange(ref stateFlags[i], ByDivisorStateCompletedDetailed, ByDivisorStateActive) == ByDivisorStateActive)
{
Interlocked.Decrement(ref remainingStates);
completions.Add(new PendingResult(primeValues[i], detailedCheck: true, passedAllTests: true));
}
continue;
}
if (Volatile.Read(ref stateFlags[i]) != ByDivisorStateActive)
{
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;
primeBuffer[activeCount] = primeValues[i];
indexBuffer[activeCount] = i;
activeCount++;
}
if (activeCount == 0)
return activeCount;
}
private static void FinalizeRemainingStates(ulong[] primeValues, ulong[] allowedMaxValues, int[] stateFlags, ref int remainingStates, ref long finalDivisorBits, List<PendingResult> completions)
{
if (completions.Count == 0)
ulong finalDivisorValue = unchecked((ulong)Volatile.Read(ref finalDivisorBits));
for (int i = 0; i < primeValues.Length; i++)
{
if (Volatile.Read(ref stateFlags[i]) != ByDivisorStateActive)
{
continue;
}
divisor = 0UL;
shouldTerminate = remainingStates == 0 && (divisorsExhausted || nextDivisor > divisorLimit);
return false;
bool detailed = finalDivisorValue > allowedMaxValues[i];
int newState = detailed ? ByDivisorStateCompletedDetailed : ByDivisorStateCompleted;
if (Interlocked.CompareExchange(ref stateFlags[i], newState, ByDivisorStateActive) != ByDivisorStateActive)
{
continue;
}
divisor = divisorValue;
shouldTerminate = false;
return true;
Interlocked.Decrement(ref remainingStates);
completions.Add(new PendingResult(primeValues[i], detailedCheck: detailed, passedAllTests: true));
}
}