Various small optimizations and cleanups. Methods made static, where possible. Duplicated & slow PrimeTester.BinaryGcd implementation removed. Optimized version was used instead.

This commit is contained in:
mlemanczyk committed 2025-11-20 01:43:29 +01:00
1 parent 9939c5d562
commit df04ba601c
4 files changed
+43 -105

No files matched your search

@@ -28,8 +28,8 @@ public class PrimeTesterBenchmarks
PrimeOrderCalculatorAccelerator.WarmUp();
// HeuristicGroupABPrimeTesterAccelerator.WarmUp();
_ = _heuristicTester.IsPrimeCpu(_candidates[0]);
_ = _heuristicTester.IsPrimeGpu(_candidates[0]);
_ = HeuristicPrimeTester.IsPrimeCpu(_candidates[0]);
_ = HeuristicPrimeTester.IsPrimeGpu(_candidates[0]);
_ = Prime.Numbers.IsPrime(_candidates[0]);
}
@@ -42,7 +42,7 @@ public class PrimeTesterBenchmarks
for (int i = 0; i < values.Length; i++)
{
if (tester.IsPrimeCpu(values[i]))
if (HeuristicPrimeTester.IsPrimeCpu(values[i]))
{
primeCount++;
}
@@ -60,7 +60,7 @@ public class PrimeTesterBenchmarks
for (int i = 0; i < values.Length; i++)
{
if (tester.IsPrimeGpu(values[i]))
if (HeuristicPrimeTester.IsPrimeGpu(values[i]))
{
primeCount++;
}
@@ -3,7 +3,6 @@ using ILGPU.Runtime;
using System.Runtime.CompilerServices;
using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
using ILGPU;
namespace PerfectNumbers.Core;
@@ -266,14 +265,10 @@ public sealed class HeuristicCombinedPrimeTester
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool EvaluateWithOpenNumericFallback(ulong n)
{
return Prime.Numbers.IsPrime(n);
}
internal static ulong ComputeHeuristicDivisorSquareLimit(ulong n)
{
return n;
}
private static bool EvaluateWithOpenNumericFallback(ulong n) => Prime.Numbers.IsPrime(n);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static ulong ComputeHeuristicDivisorSquareLimit(ulong n) => n;
private static ReadOnlySpan<ulong> GetCombinedDivisors(byte nMod10) => nMod10 switch
{
@@ -470,9 +465,7 @@ public sealed class HeuristicCombinedPrimeTester
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static HeuristicDivisorEnumerator CreateHeuristicDivisorEnumerator(ulong maxDivisorSquare, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
{
return new HeuristicDivisorEnumerator(maxDivisorSquare, nMod10, groupBBuffer);
}
=> new(maxDivisorSquare, nMod10, groupBBuffer);
internal struct HeuristicGroupBSequenceState
{
@@ -480,9 +473,7 @@ public sealed class HeuristicCombinedPrimeTester
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static MersenneHeuristicDivisorEnumerator CreateMersenneDivisorEnumerator(ulong exponent, ulong maxDivisor)
{
return new MersenneHeuristicDivisorEnumerator(exponent, maxDivisor);
}
=> new(exponent, maxDivisor);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static HeuristicDivisorPreparation PrepareHeuristicDivisor(in HeuristicDivisorCandidate candidate)
@@ -490,7 +481,7 @@ public sealed class HeuristicCombinedPrimeTester
ulong divisor = candidate.Value;
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
bool hasCycleHint = TryGetCycleLengthHint(divisor, out ulong cycleLength);
return new HeuristicDivisorPreparation(candidate, divisorData, cycleLength, hasCycleHint);
return new(candidate, divisorData, cycleLength, hasCycleHint);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -588,7 +579,7 @@ public sealed class HeuristicCombinedPrimeTester
ulong value = entry;
byte ending = (byte)(value % 10UL);
ushort residue = (ushort)(value % Wheel210);
candidate = new HeuristicDivisorCandidate(value, group, ending, 0, residue);
candidate = new(value, group, ending, 0, residue);
return true;
}
@@ -691,7 +682,7 @@ public sealed class HeuristicCombinedPrimeTester
}
ushort residue = (ushort)(value % Wheel210);
return new HeuristicDivisorCandidate(value, group, ending, 0, residue);
return new(value, group, ending, 0, residue);
}
}
}
+29 -47
View File
@@ -68,7 +68,8 @@ public sealed class HeuristicPrimeTester
public static HeuristicPrimeTester Exclusive => _tester ??= new();
public bool IsPrimeCpu(ulong n)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsPrimeCpu(ulong n)
{
byte nMod10 = (byte)n.Mod10();
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
@@ -82,19 +83,18 @@ public sealed class HeuristicPrimeTester
return HeuristicTrialDivisionCpu(n, maxDivisorSquare, nMod10, includeGroupB);
}
public bool IsPrimeGpu(ulong n)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsPrimeGpu(ulong n)
{
byte nMod10 = (byte)n.Mod10();
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
return IsPrimeGpu(n, maxDivisorSquare, nMod10);
}
public bool IsPrimeGpu(ulong n, ulong maxDivisorSquare, byte nMod10)
{
return HeuristicIsPrimeGpuCore(n, maxDivisorSquare, nMod10);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsPrimeGpu(ulong n, ulong maxDivisorSquare, byte nMod10) => HeuristicIsPrimeGpuCore(n, maxDivisorSquare, nMod10);
private bool HeuristicIsPrimeGpuCore(ulong n, ulong maxDivisorSquare, byte nMod10)
private static bool HeuristicIsPrimeGpuCore(ulong n, ulong maxDivisorSquare, byte nMod10)
{
// TODO: Is this condition ever met on the execution path in EvenPerfectBitScanner?
if (maxDivisorSquare < 9UL)
@@ -133,7 +133,7 @@ public sealed class HeuristicPrimeTester
break;
}
ulong divisor = (ulong)groupADivisors[groupAIndex];
ulong divisor = groupADivisors[groupAIndex];
if (n % divisor == 0UL)
{
return false;
@@ -186,7 +186,7 @@ public sealed class HeuristicPrimeTester
break;
}
ulong divisor = (ulong)groupADivisors[groupAIndex];
ulong divisor = groupADivisors[groupAIndex];
if (n % divisor == 0UL)
{
return false;
@@ -240,7 +240,7 @@ public sealed class HeuristicPrimeTester
}
private bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10)
private static bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10)
{
// GpuPrimeWorkLimiter.Acquire();
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
@@ -321,17 +321,11 @@ public sealed class HeuristicPrimeTester
return compositeDetected;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool EvaluateWithOpenNumericFallback(ulong n) => Prime.Numbers.IsPrime(n);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool EvaluateWithOpenNumericFallback(ulong n)
{
return Prime.Numbers.IsPrime(n);
}
internal static ulong ComputeHeuristicDivisorSquareLimit(ulong n)
{
return n;
}
internal static ulong ComputeHeuristicDivisorSquareLimit(ulong n) => n;
private static ReadOnlySpan<byte> GetGroupBEndingOrder(byte nMod10) => nMod10 switch
{
@@ -339,7 +333,7 @@ public sealed class HeuristicPrimeTester
3 => GroupBEndingOrderMod3,
7 => GroupBEndingOrderMod7,
9 => GroupBEndingOrderMod9,
_ => ReadOnlySpan<byte>.Empty,
_ => [],
};
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -415,19 +409,19 @@ public sealed class HeuristicPrimeTester
private static ReadOnlySpan<uint> GetGroupBDivisors(byte ending) => ending switch
{
1 => DivisorGenerator.SmallPrimesLastOneWithoutLastThree,
3 => ReadOnlySpan<uint>.Empty,
3 => [],
7 => DivisorGenerator.SmallPrimesLastSevenWithoutLastThree,
9 => DivisorGenerator.SmallPrimesLastNineWithoutLastThree,
_ => ReadOnlySpan<uint>.Empty,
_ => [],
};
private static ReadOnlySpan<ulong> GetGroupBDivisorSquares(byte ending) => ending switch
{
1 => DivisorGenerator.SmallPrimesPow2LastOneWithoutLastThree,
3 => ReadOnlySpan<ulong>.Empty,
3 => [],
7 => DivisorGenerator.SmallPrimesPow2LastSevenWithoutLastThree,
9 => DivisorGenerator.SmallPrimesPow2LastNineWithoutLastThree,
_ => ReadOnlySpan<ulong>.Empty,
_ => [],
};
private static int InitializeGroupBStates(byte nMod10, Span<HeuristicGroupBSequenceState> buffer)
@@ -444,16 +438,11 @@ public sealed class HeuristicPrimeTester
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static HeuristicDivisorEnumerator CreateHeuristicDivisorEnumerator(ulong maxDivisorSquare, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
{
return new HeuristicDivisorEnumerator(maxDivisorSquare, nMod10, groupBBuffer);
}
internal static HeuristicDivisorEnumerator CreateHeuristicDivisorEnumerator(ulong maxDivisorSquare, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer) => new(maxDivisorSquare, nMod10, groupBBuffer);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static MersenneHeuristicDivisorEnumerator CreateMersenneDivisorEnumerator(ulong exponent, ulong maxDivisor)
{
return new MersenneHeuristicDivisorEnumerator(exponent, maxDivisor);
}
=> new(exponent, maxDivisor);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static HeuristicDivisorPreparation PrepareHeuristicDivisor(in HeuristicDivisorCandidate candidate)
@@ -611,25 +600,18 @@ public sealed class HeuristicPrimeTester
internal struct HeuristicGroupBSequenceState
internal struct HeuristicGroupBSequenceState(byte ending, byte priorityIndex)
{
public byte Ending;
public byte PriorityIndex;
public int Index;
public HeuristicGroupBSequenceState(byte ending, byte priorityIndex)
{
Ending = ending;
PriorityIndex = priorityIndex;
Index = 0;
}
public byte Ending = ending;
public byte PriorityIndex = priorityIndex;
public int Index = 0;
public bool TryPeek(ulong maxDivisorSquare, out ulong divisor)
{
ReadOnlySpan<uint> divisors = GetGroupBDivisors(Ending);
ReadOnlySpan<ulong> squares = GetGroupBDivisorSquares(Ending);
int index = Index;
if ((uint)index >= (uint)divisors.Length)
if (index >= divisors.Length)
{
divisor = 0UL;
return false;
@@ -642,7 +624,7 @@ public sealed class HeuristicPrimeTester
return false;
}
divisor = (ulong)divisors[index];
divisor = divisors[index];
return true;
}
@@ -731,11 +713,11 @@ public sealed class HeuristicPrimeTester
residueStepper.Advance();
}
public ulong ProcessedCount => processedCount;
public readonly ulong ProcessedCount => processedCount;
public ulong LastDivisor => lastDivisor;
public readonly ulong LastDivisor => lastDivisor;
public bool Exhausted => !active;
public readonly bool Exhausted => !active;
private static HeuristicDivisorCandidate CreateCandidate(ulong value)
{
+1 -36
View File
@@ -265,7 +265,7 @@ public sealed class PrimeTester
// ResidueComputationBenchmarks so divisor-cycle metadata can short-circuit the test
// instead of recomputing binary GCD for every candidate.
ulong m = (ulong)BitOperations.Log2(n);
return BinaryGcd(n, m) != 1UL;
return n.BinaryGcd(m) != 1UL;
}
internal static void SharesFactorWithMaxExponentBatch(ReadOnlySpan<ulong> values, Span<byte> results)
@@ -298,39 +298,4 @@ public sealed class PrimeTester
PrimeOrderCalculatorAccelerator.Return(gpu);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong BinaryGcd(ulong u, ulong v)
{
// TODO: Swap this handwritten binary GCD for the optimized helper measured in
// GpuUInt128BinaryGcdBenchmarks so CPU callers share the faster subtract-less
// ladder once the common implementation is promoted into PerfectNumbers.Core.
// You have an example in the benchmarks and you already implemented it in
// GpuUInt128, iirc.
if (u == 0UL)
{
return v;
}
if (v == 0UL)
{
return u;
}
int shift = BitOperations.TrailingZeroCount(u | v);
u >>= BitOperations.TrailingZeroCount(u);
do
{
v >>= BitOperations.TrailingZeroCount(v);
if (u > v)
{
(u, v) = (v, u);
}
v -= u;
}
while (v != 0UL);
return u << shift;
}
}