Reduce synchronization when building divisor prime buffer

This commit is contained in:
Marek Lemańczyk committed 2025-09-25 14:27:28 +02:00
1 parent b40c61b527
commit 0f3b354a93
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) 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 length = primeValues.Length;
int startIndex = Volatile.Read(ref activeStartIndex); int startIndex;
int index = 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); Interlocked.Decrement(ref remainingStates);
if (completionsCount == completionsBuffer.Length) if (completionsCount == completionsBuffer.Length)
{ {
FlushPendingResults(completionsBuffer, ref completionsCount); 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 else
{ {
ClearActiveMask(activeStateMask, index); ClearActiveMask(activeStateMask, startIndex);
} }
index++;
}
if (index != startIndex)
{
Volatile.Write(ref activeStartIndex, index);
} }
int index = startIndex;
int activeCount = 0; int activeCount = 0;
while (index < length) while (index < length)
{ {
int wordIndex = index >> 6; 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) if (word == 0UL)
{ {
index = (wordIndex + 1) << 6; index = (wordIndex + 1) << 6;
continue; continue;
} }
}
int bitOffset = index & 63; int bitOffset = index & 63;
if (bitOffset != 0) if (bitOffset != 0)
{ {
@@ -1183,7 +1194,10 @@ internal static class Program
return activeCount; 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]; primeBuffer[activeCount] = primeValues[candidateIndex];
indexBuffer[activeCount] = candidateIndex; indexBuffer[activeCount] = candidateIndex;
@@ -1209,12 +1223,13 @@ internal static class Program
bool detailed = false; bool detailed = false;
for (; finalizeIndex < primeValues.Length; finalizeIndex++) for (; finalizeIndex < primeValues.Length; finalizeIndex++)
{ {
if (Volatile.Read(ref stateFlags[finalizeIndex]) != ByDivisorStateActive) if (stateFlags[finalizeIndex] != ByDivisorStateActive)
{ {
continue; 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) if (Interlocked.CompareExchange(ref stateFlags[finalizeIndex], detailed ? ByDivisorStateCompletedDetailed : ByDivisorStateCompleted, ByDivisorStateActive) != ByDivisorStateActive)
{ {