Merge pull request #233 from mlemanczyk/codex/modify-primetester-for-batch-heuristics
Interleave heuristic prime tester batches across groups
This commit is contained in:
1 file changed
+235
-88
@@ -203,22 +203,44 @@ public sealed class PrimeTester(bool useInternal = false)
|
|||||||
private static bool HeuristicTrialDivisionCpu(ulong n, ulong sqrtLimit, byte nMod10, bool includeGroupB)
|
private static bool HeuristicTrialDivisionCpu(ulong n, ulong sqrtLimit, byte nMod10, bool includeGroupB)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<int> groupADivisors = HeuristicPrimeSieves.GroupADivisors;
|
ReadOnlySpan<int> groupADivisors = HeuristicPrimeSieves.GroupADivisors;
|
||||||
for (int i = 0; i < groupADivisors.Length; i++)
|
int interleaveBatchSize = Math.Max(1, HeuristicDivisorInterleaveBatchSize);
|
||||||
{
|
int groupAIndex = 0;
|
||||||
ulong divisor = (ulong)groupADivisors[i];
|
|
||||||
if (divisor > sqrtLimit)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n % divisor == 0UL)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!includeGroupB)
|
if (!includeGroupB)
|
||||||
{
|
{
|
||||||
|
while (groupAIndex < groupADivisors.Length)
|
||||||
|
{
|
||||||
|
int processed = 0;
|
||||||
|
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
||||||
|
{
|
||||||
|
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
||||||
|
if (divisor > sqrtLimit)
|
||||||
|
{
|
||||||
|
groupAIndex = groupADivisors.Length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n % divisor == 0UL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
groupAIndex++;
|
||||||
|
processed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupAIndex >= groupADivisors.Length)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ulong)groupADivisors[groupAIndex] > sqrtLimit)
|
||||||
|
{
|
||||||
|
groupAIndex = groupADivisors.Length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return EvaluateWithOpenNumericFallback(n);
|
return EvaluateWithOpenNumericFallback(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,53 +254,82 @@ public sealed class PrimeTester(bool useInternal = false)
|
|||||||
? stackalloc int[endingOrder.Length]
|
? stackalloc int[endingOrder.Length]
|
||||||
: new int[endingOrder.Length];
|
: new int[endingOrder.Length];
|
||||||
|
|
||||||
while (true)
|
bool groupAHasMore = groupAIndex < groupADivisors.Length && (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
||||||
|
bool groupBHasMore = HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
||||||
|
|
||||||
|
while (groupAHasMore || groupBHasMore)
|
||||||
{
|
{
|
||||||
ulong bestCandidate = ulong.MaxValue;
|
if (groupAHasMore)
|
||||||
int bestEndingIndex = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < endingOrder.Length; i++)
|
|
||||||
{
|
{
|
||||||
ReadOnlySpan<int> divisors = GetGroupBDivisors(endingOrder[i]);
|
int processed = 0;
|
||||||
int index = indices[i];
|
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
||||||
if ((uint)index >= (uint)divisors.Length)
|
|
||||||
{
|
{
|
||||||
continue;
|
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
||||||
|
if (divisor > sqrtLimit)
|
||||||
|
{
|
||||||
|
groupAIndex = groupADivisors.Length;
|
||||||
|
groupAHasMore = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n % divisor == 0UL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
groupAIndex++;
|
||||||
|
processed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong candidate = (ulong)divisors[index];
|
if (groupAIndex >= groupADivisors.Length)
|
||||||
if (candidate > sqrtLimit)
|
|
||||||
{
|
{
|
||||||
indices[i] = divisors.Length;
|
groupAHasMore = false;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (bestEndingIndex == -1 || candidate < bestCandidate)
|
|
||||||
{
|
{
|
||||||
bestCandidate = candidate;
|
groupAHasMore = (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
||||||
bestEndingIndex = i;
|
if (!groupAHasMore)
|
||||||
|
{
|
||||||
|
groupAIndex = groupADivisors.Length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bestEndingIndex == -1)
|
if (groupBHasMore)
|
||||||
{
|
{
|
||||||
break;
|
int processed = 0;
|
||||||
}
|
while (processed < interleaveBatchSize)
|
||||||
|
{
|
||||||
|
if (!TrySelectNextGroupBDivisor(endingOrder, indices, sqrtLimit, out ulong divisor))
|
||||||
|
{
|
||||||
|
groupBHasMore = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (n % bestCandidate == 0UL)
|
if (n % divisor == 0UL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
indices[bestEndingIndex]++;
|
processed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupBHasMore)
|
||||||
|
{
|
||||||
|
groupBHasMore = HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong sqrtLimit, byte nMod10)
|
private bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong sqrtLimit, byte nMod10)
|
||||||
{
|
{
|
||||||
int batchCapacity = Math.Max(1, HeuristicGpuDivisorBatchSize);
|
int batchCapacity = Math.Max(1, HeuristicGpuDivisorBatchSize);
|
||||||
|
int interleaveBatchSize = Math.Max(1, HeuristicDivisorInterleaveBatchSize);
|
||||||
var divisorPool = ThreadStaticPools.UlongPool;
|
var divisorPool = ThreadStaticPools.UlongPool;
|
||||||
var hitPool = ThreadStaticPools.BytePool;
|
var hitPool = ThreadStaticPools.BytePool;
|
||||||
|
|
||||||
@@ -353,61 +404,87 @@ public sealed class PrimeTester(bool useInternal = false)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<int> groupADivisors = HeuristicPrimeSieves.GroupADivisors;
|
ReadOnlySpan<int> groupADivisors = HeuristicPrimeSieves.GroupADivisors;
|
||||||
for (int i = 0; i < groupADivisors.Length && !compositeDetected; i++)
|
int groupAIndex = 0;
|
||||||
|
ReadOnlySpan<byte> endingOrder = GetGroupBEndingOrder(nMod10);
|
||||||
|
Span<int> indices = endingOrder.Length <= 8
|
||||||
|
? stackalloc int[endingOrder.Length]
|
||||||
|
: new int[endingOrder.Length];
|
||||||
|
|
||||||
|
bool groupAHasMore = groupAIndex < groupADivisors.Length && (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
||||||
|
bool groupBHasMore = !endingOrder.IsEmpty && HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
||||||
|
|
||||||
|
while (!compositeDetected && (groupAHasMore || groupBHasMore))
|
||||||
{
|
{
|
||||||
ulong divisor = (ulong)groupADivisors[i];
|
if (groupAHasMore)
|
||||||
if (divisor > sqrtLimit)
|
|
||||||
{
|
{
|
||||||
break;
|
int processed = 0;
|
||||||
}
|
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
||||||
|
|
||||||
compositeDetected = QueueDivisor(divisor);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!compositeDetected)
|
|
||||||
{
|
|
||||||
ReadOnlySpan<byte> endingOrder = GetGroupBEndingOrder(nMod10);
|
|
||||||
if (!endingOrder.IsEmpty)
|
|
||||||
{
|
|
||||||
Span<int> indices = endingOrder.Length <= 8
|
|
||||||
? stackalloc int[endingOrder.Length]
|
|
||||||
: new int[endingOrder.Length];
|
|
||||||
|
|
||||||
while (!compositeDetected)
|
|
||||||
{
|
{
|
||||||
ulong bestCandidate = ulong.MaxValue;
|
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
||||||
int bestEndingIndex = -1;
|
if (divisor > sqrtLimit)
|
||||||
|
|
||||||
for (int i = 0; i < endingOrder.Length; i++)
|
|
||||||
{
|
{
|
||||||
ReadOnlySpan<int> divisors = GetGroupBDivisors(endingOrder[i]);
|
groupAIndex = groupADivisors.Length;
|
||||||
int index = indices[i];
|
groupAHasMore = false;
|
||||||
if ((uint)index >= (uint)divisors.Length)
|
break;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong candidate = (ulong)divisors[index];
|
|
||||||
if (candidate > sqrtLimit)
|
|
||||||
{
|
|
||||||
indices[i] = divisors.Length;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bestEndingIndex == -1 || candidate < bestCandidate)
|
|
||||||
{
|
|
||||||
bestCandidate = candidate;
|
|
||||||
bestEndingIndex = i;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bestEndingIndex == -1)
|
compositeDetected = QueueDivisor(divisor);
|
||||||
|
if (compositeDetected)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
compositeDetected = QueueDivisor(bestCandidate);
|
groupAIndex++;
|
||||||
indices[bestEndingIndex]++;
|
processed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compositeDetected)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupAIndex >= groupADivisors.Length)
|
||||||
|
{
|
||||||
|
groupAHasMore = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
groupAHasMore = (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
||||||
|
if (!groupAHasMore)
|
||||||
|
{
|
||||||
|
groupAIndex = groupADivisors.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupBHasMore)
|
||||||
|
{
|
||||||
|
int processed = 0;
|
||||||
|
while (processed < interleaveBatchSize)
|
||||||
|
{
|
||||||
|
if (!TrySelectNextGroupBDivisor(endingOrder, indices, sqrtLimit, out ulong divisor))
|
||||||
|
{
|
||||||
|
groupBHasMore = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
compositeDetected = QueueDivisor(divisor);
|
||||||
|
if (compositeDetected)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
processed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compositeDetected)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupBHasMore)
|
||||||
|
{
|
||||||
|
groupBHasMore = HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -425,16 +502,16 @@ public sealed class PrimeTester(bool useInternal = false)
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (hitFlags is not null)
|
|
||||||
{
|
|
||||||
hitPool.Return(hitFlags);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (divisorArray is not null)
|
if (divisorArray is not null)
|
||||||
{
|
{
|
||||||
divisorPool.Return(divisorArray);
|
divisorPool.Return(divisorArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hitFlags is not null)
|
||||||
|
{
|
||||||
|
hitPool.Return(hitFlags);
|
||||||
|
}
|
||||||
|
|
||||||
gpu.Dispose();
|
gpu.Dispose();
|
||||||
limiter.Dispose();
|
limiter.Dispose();
|
||||||
}
|
}
|
||||||
@@ -442,6 +519,8 @@ public sealed class PrimeTester(bool useInternal = false)
|
|||||||
return compositeDetected;
|
return compositeDetected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static bool EvaluateWithOpenNumericFallback(ulong n)
|
private static bool EvaluateWithOpenNumericFallback(ulong n)
|
||||||
{
|
{
|
||||||
return Prime.Numbers.IsPrime(n);
|
return Prime.Numbers.IsPrime(n);
|
||||||
@@ -479,6 +558,72 @@ public sealed class PrimeTester(bool useInternal = false)
|
|||||||
_ => ReadOnlySpan<byte>.Empty,
|
_ => ReadOnlySpan<byte>.Empty,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static bool TrySelectNextGroupBDivisor(ReadOnlySpan<byte> endingOrder, Span<int> indices, ulong sqrtLimit, out ulong divisor)
|
||||||
|
{
|
||||||
|
ulong bestCandidate = ulong.MaxValue;
|
||||||
|
int bestEndingIndex = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < endingOrder.Length; i++)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<int> divisors = GetGroupBDivisors(endingOrder[i]);
|
||||||
|
int index = indices[i];
|
||||||
|
if ((uint)index >= (uint)divisors.Length)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong candidate = (ulong)divisors[index];
|
||||||
|
if (candidate > sqrtLimit)
|
||||||
|
{
|
||||||
|
indices[i] = divisors.Length;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestEndingIndex == -1 || candidate < bestCandidate)
|
||||||
|
{
|
||||||
|
bestCandidate = candidate;
|
||||||
|
bestEndingIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestEndingIndex == -1)
|
||||||
|
{
|
||||||
|
divisor = 0UL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
divisor = bestCandidate;
|
||||||
|
indices[bestEndingIndex]++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static bool HasGroupBCandidates(ReadOnlySpan<byte> endingOrder, Span<int> indices, ulong sqrtLimit)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < endingOrder.Length; i++)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<int> divisors = GetGroupBDivisors(endingOrder[i]);
|
||||||
|
int index = indices[i];
|
||||||
|
if ((uint)index >= (uint)divisors.Length)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong candidate = (ulong)divisors[index];
|
||||||
|
if (candidate > sqrtLimit)
|
||||||
|
{
|
||||||
|
indices[i] = divisors.Length;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static ReadOnlySpan<int> GetGroupBDivisors(byte ending) => ending switch
|
private static ReadOnlySpan<int> GetGroupBDivisors(byte ending) => ending switch
|
||||||
{
|
{
|
||||||
1 => HeuristicPrimeSieves.GroupBDivisorsEnding1,
|
1 => HeuristicPrimeSieves.GroupBDivisorsEnding1,
|
||||||
@@ -906,6 +1051,8 @@ public sealed class PrimeTester(bool useInternal = false)
|
|||||||
|
|
||||||
public static int HeuristicGpuDivisorBatchSize { get; set; } = 4_096;
|
public static int HeuristicGpuDivisorBatchSize { get; set; } = 4_096;
|
||||||
|
|
||||||
|
public static int HeuristicDivisorInterleaveBatchSize { get; set; } = 64;
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void IsPrimeBatchGpu(ReadOnlySpan<ulong> values, Span<byte> results)
|
public static void IsPrimeBatchGpu(ReadOnlySpan<ulong> values, Span<byte> results)
|
||||||
|
|||||||
Reference in new issue
Block a user