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.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading;
using Open.Collections; using Open.Collections;
using Open.Numeric.Primes; using Open.Numeric.Primes;
using PerfectNumbers.Core; using PerfectNumbers.Core;
@@ -54,6 +55,11 @@ 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 const int ByDivisorStateActive = 0;
private const int ByDivisorStateComposite = 1;
private const int ByDivisorStateCompleted = 2;
private const int ByDivisorStateCompletedDetailed = 3;
private struct ByDivisorPrimeState private struct ByDivisorPrimeState
{ {
internal ulong Prime; internal ulong Prime;
@@ -793,39 +799,73 @@ internal static class Program
return; 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 divisorLimit = _byDivisorTester!.DivisorLimit;
ulong nextDivisor = 3UL; ulong nextDivisor = 3UL;
bool divisorsExhausted = false; long finalDivisorBits = unchecked((long)3UL);
ulong finalDivisorValue = 3UL; int divisorsExhaustedFlag = 0;
int remainingStates = states.Count; int finalizerState = 0;
int finalizationCompleted = 0;
int remainingStates = stateCount;
Task[] workers = new Task[Math.Max(1, threadCount)]; 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(); using var session = _byDivisorTester!.CreateDivisorSession();
byte[] hitsBuffer = ArrayPool<byte>.Shared.Rent(states.Count); byte[] hitsBuffer = ArrayPool<byte>.Shared.Rent(stateCount);
ulong[] primeBuffer = ArrayPool<ulong>.Shared.Rent(states.Count); ulong[] primeBuffer = ArrayPool<ulong>.Shared.Rent(stateCount);
int[] indexBuffer = ArrayPool<int>.Shared.Rent(states.Count); int[] indexBuffer = ArrayPool<int>.Shared.Rent(stateCount);
List<PendingResult> completions = new(states.Count); List<PendingResult> completions = new(stateCount);
List<PendingResult> composites = new(states.Count); List<PendingResult> composites = new(stateCount);
try try
{ {
while (true) while (true)
{ {
ulong divisor; if (Volatile.Read(ref remainingStates) == 0)
int activeCount;
bool shouldTerminate;
bool hasWork;
lock (stateLock)
{ {
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) if (completions.Count > 0)
{ {
@@ -837,9 +877,17 @@ internal static class Program
completions.Clear(); 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; break;
} }
@@ -847,15 +895,27 @@ internal static class Program
continue; 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); Span<byte> hitsSpan = hitsBuffer.AsSpan(0, activeCount);
hitsSpan.Clear(); hitsSpan.Clear();
session.CheckDivisor(divisor, primeBuffer.AsSpan(0, activeCount), hitsSpan); session.CheckDivisor(divisor, primeBuffer.AsSpan(0, activeCount), hitsSpan);
bool exitAfterIteration;
composites.Clear();
lock (stateLock)
{
for (int j = 0; j < activeCount; j++) for (int j = 0; j < activeCount; j++)
{ {
if (hitsSpan[j] == 0) if (hitsSpan[j] == 0)
@@ -864,25 +924,10 @@ internal static class Program
} }
int index = indexBuffer[j]; int index = indexBuffer[j];
var state = states[index]; if (Interlocked.CompareExchange(ref stateFlags[index], ByDivisorStateComposite, ByDivisorStateActive) == ByDivisorStateActive)
if (state.Completed)
{ {
continue; Interlocked.Decrement(ref remainingStates);
} composites.Add(new PendingResult(primeValues[index], detailedCheck: true, passedAllTests: false));
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);
} }
} }
@@ -892,11 +937,8 @@ internal static class Program
{ {
PrintResult(result.Prime, searchedMersenne: true, detailedCheck: result.DetailedCheck, passedAllTests: result.PassedAllTests); PrintResult(result.Prime, searchedMersenne: true, detailedCheck: result.DetailedCheck, passedAllTests: result.PassedAllTests);
} }
}
if (exitAfterIteration) composites.Clear();
{
break;
} }
} }
} }
@@ -912,106 +954,92 @@ internal static class Program
Task.WaitAll(workers); 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) while (true)
{ {
if (remainingStates == 0) if (Volatile.Read(ref divisorsExhaustedFlag) != 0)
{ {
divisor = 0UL; exhausted = true;
activeCount = 0; return 0UL;
shouldTerminate = true;
return false;
} }
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; Volatile.Write(ref finalDivisorBits, currentBits);
finalDivisorValue = nextDivisor;
} }
ulong completionDivisor = finalDivisorValue; exhausted = true;
for (int i = 0; i < states.Count; i++) return 0UL;
}
ulong nextValue = currentValue + 2UL;
long nextBits = unchecked((long)nextValue);
if (Interlocked.CompareExchange(ref nextDivisorBits, nextBits, currentBits) == currentBits)
{ {
var state = states[i]; exhausted = false;
if (state.Completed) 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; continue;
} }
state.Completed = true; primeBuffer[activeCount] = primeValues[i];
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; indexBuffer[activeCount] = i;
activeCount++; 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; continue;
} }
divisor = 0UL; bool detailed = finalDivisorValue > allowedMaxValues[i];
shouldTerminate = remainingStates == 0 && (divisorsExhausted || nextDivisor > divisorLimit); int newState = detailed ? ByDivisorStateCompletedDetailed : ByDivisorStateCompleted;
return false;
if (Interlocked.CompareExchange(ref stateFlags[i], newState, ByDivisorStateActive) != ByDivisorStateActive)
{
continue;
} }
divisor = divisorValue; Interlocked.Decrement(ref remainingStates);
shouldTerminate = false; completions.Add(new PendingResult(primeValues[i], detailedCheck: detailed, passedAllTests: true));
return true;
} }
} }