Merge pull request #89 from mlemanczyk/codex/optimize-buildprimebuffer-method

Reduce synchronization when building divisor prime buffer
This commit is contained in:
Marek Lemańczyk authored and GitHub committed 2025-09-25 14:28:06 +02:00
commit d91507c52e
1 file changed
+34 -19
+34 -19
View File
@@ -1125,45 +1125,56 @@ internal static class Program
private static int BuildPrimeBuffer(ulong divisor, in ulong[] primeValues, in ulong[] allowedMaxValues, int[] stateFlags, ulong[] primeBuffer, int[] indexBuffer, PendingResult[] completionsBuffer, ref int completionsCount, ref int remainingStates, long[] activeStateMask, ref int activeStartIndex)
{
int length = primeValues.Length;
int startIndex = Volatile.Read(ref activeStartIndex);
int index = startIndex;
int startIndex;
while (true)
{
startIndex = Volatile.Read(ref activeStartIndex);
if (startIndex >= length || allowedMaxValues[startIndex] >= divisor)
{
break;
}
while (index < length && allowedMaxValues[index] < divisor)
// Claim the prefix slot before finalizing to avoid double-counting the same candidate.
if (Interlocked.CompareExchange(ref activeStartIndex, startIndex + 1, startIndex) != startIndex)
{
if (Interlocked.CompareExchange(ref stateFlags[index], ByDivisorStateCompletedDetailed, ByDivisorStateActive) == ByDivisorStateActive)
continue;
}
int state = Volatile.Read(ref stateFlags[startIndex]);
if (state == ByDivisorStateActive)
{
ClearActiveMask(activeStateMask, index);
Volatile.Write(ref stateFlags[startIndex], ByDivisorStateCompletedDetailed);
ClearActiveMask(activeStateMask, startIndex);
Interlocked.Decrement(ref remainingStates);
if (completionsCount == completionsBuffer.Length)
{
FlushPendingResults(completionsBuffer, ref completionsCount);
}
completionsBuffer[completionsCount++] = new PendingResult(primeValues[index], detailedCheck: true, passedAllTests: true);
completionsBuffer[completionsCount++] = new PendingResult(primeValues[startIndex], detailedCheck: true, passedAllTests: true);
}
else
{
ClearActiveMask(activeStateMask, index);
ClearActiveMask(activeStateMask, startIndex);
}
index++;
}
if (index != startIndex)
{
Volatile.Write(ref activeStartIndex, index);
}
int index = startIndex;
int activeCount = 0;
while (index < length)
{
int wordIndex = index >> 6;
ulong word = unchecked((ulong)Volatile.Read(ref activeStateMask[wordIndex]));
ulong word = unchecked((ulong)activeStateMask[wordIndex]);
if (word == 0UL)
{
// Fallback to a volatile read only when the optimistic read reports no work to
// minimize the number of costly fences on the hot path.
word = unchecked((ulong)Volatile.Read(ref activeStateMask[wordIndex]));
if (word == 0UL)
{
index = (wordIndex + 1) << 6;
continue;
}
}
int bitOffset = index & 63;
if (bitOffset != 0)
{
@@ -1183,7 +1194,10 @@ internal static class Program
return activeCount;
}
if (Volatile.Read(ref stateFlags[candidateIndex]) == ByDivisorStateActive)
// Intentionally avoid Volatile.Read here; seeing a stale Active value only causes
// redundant work, while seeing a stale non-active value would require the mask bit
// to be set, which means another worker has not finished clearing it yet.
if (stateFlags[candidateIndex] == ByDivisorStateActive)
{
primeBuffer[activeCount] = primeValues[candidateIndex];
indexBuffer[activeCount] = candidateIndex;
@@ -1209,12 +1223,13 @@ internal static class Program
bool detailed = false;
for (; finalizeIndex < primeValues.Length; finalizeIndex++)
{
if (Volatile.Read(ref stateFlags[finalizeIndex]) != ByDivisorStateActive)
if (stateFlags[finalizeIndex] != ByDivisorStateActive)
{
continue;
}
detailed = unchecked((ulong)Volatile.Read(ref finalDivisorBits)) > allowedMaxValues[finalizeIndex];
ulong finalDivisor = unchecked((ulong)Volatile.Read(ref finalDivisorBits));
detailed = finalDivisor > allowedMaxValues[finalizeIndex];
if (Interlocked.CompareExchange(ref stateFlags[finalizeIndex], detailed ? ByDivisorStateCompletedDetailed : ByDivisorStateCompleted, ByDivisorStateActive) != ByDivisorStateActive)
{