Merge pull request #60 from mlemanczyk/codex/optimize-evenperfectbitscanner-for-thread-independence
Improve by-divisor scanning concurrency
This commit is contained in:
1 file changed
+148
-120
+148
-120
@@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user