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
+74 -59
+74 -59
View File
@@ -1122,49 +1122,60 @@ internal static class Program
return true; return true;
} }
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;
ClearActiveMask(activeStateMask, index); }
Interlocked.Decrement(ref remainingStates);
if (completionsCount == completionsBuffer.Length)
{
FlushPendingResults(completionsBuffer, ref completionsCount);
}
completionsBuffer[completionsCount++] = new PendingResult(primeValues[index], detailedCheck: true, passedAllTests: true);
}
else
{
ClearActiveMask(activeStateMask, index);
}
index++; int state = Volatile.Read(ref stateFlags[startIndex]);
} if (state == ByDivisorStateActive)
{
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[startIndex], detailedCheck: true, passedAllTests: true);
}
else
{
ClearActiveMask(activeStateMask, startIndex);
}
}
if (index != startIndex) int index = startIndex;
{ int activeCount = 0;
Volatile.Write(ref activeStartIndex, index); while (index < length)
} {
int wordIndex = index >> 6;
int activeCount = 0; ulong word = unchecked((ulong)activeStateMask[wordIndex]);
while (index < length) if (word == 0UL)
{ {
int wordIndex = index >> 6; // Fallback to a volatile read only when the optimistic read reports no work to
ulong word = unchecked((ulong)Volatile.Read(ref activeStateMask[wordIndex])); // minimize the number of costly fences on the hot path.
if (word == 0UL) word = unchecked((ulong)Volatile.Read(ref activeStateMask[wordIndex]));
{ if (word == 0UL)
index = (wordIndex + 1) << 6; {
continue; index = (wordIndex + 1) << 6;
} continue;
}
int bitOffset = index & 63; }
int bitOffset = index & 63;
if (bitOffset != 0) if (bitOffset != 0)
{ {
word &= ulong.MaxValue << bitOffset; word &= ulong.MaxValue << bitOffset;
@@ -1183,16 +1194,19 @@ 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
primeBuffer[activeCount] = primeValues[candidateIndex]; // to be set, which means another worker has not finished clearing it yet.
indexBuffer[activeCount] = candidateIndex; if (stateFlags[candidateIndex] == ByDivisorStateActive)
activeCount++; {
} primeBuffer[activeCount] = primeValues[candidateIndex];
else indexBuffer[activeCount] = candidateIndex;
{ activeCount++;
ClearActiveMask(activeStateMask, candidateIndex); }
} else
{
ClearActiveMask(activeStateMask, candidateIndex);
}
word &= word - 1UL; word &= word - 1UL;
} }
@@ -1209,17 +1223,18 @@ 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)
{ {
continue; continue;
} }
ClearActiveMask(activeStateMask, finalizeIndex); ClearActiveMask(activeStateMask, finalizeIndex);
Interlocked.Decrement(ref remainingStates); Interlocked.Decrement(ref remainingStates);