Merge pull request #249 from mlemanczyk/codex/optimize-heuristiccombinedprimetester-calculations
Use divisor square tables in heuristic combined prime tester
This commit is contained in:
16 files changed
+612
-650
No files matched your search
@@ -29,8 +29,7 @@ public class PrimeTesterBenchmarks
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CancellationToken cancellationToken = CancellationToken.None;
|
_ = _heuristicTester.IsPrimeCpu(_candidates[0]);
|
||||||
_ = _heuristicTester.IsPrimeCpu(_candidates[0], cancellationToken);
|
|
||||||
_ = _heuristicTester.IsPrimeGpu(_candidates[0]);
|
_ = _heuristicTester.IsPrimeGpu(_candidates[0]);
|
||||||
_ = Prime.Numbers.IsPrime(_candidates[0]);
|
_ = Prime.Numbers.IsPrime(_candidates[0]);
|
||||||
}
|
}
|
||||||
@@ -40,12 +39,11 @@ public class PrimeTesterBenchmarks
|
|||||||
{
|
{
|
||||||
HeuristicPrimeTester tester = _heuristicTester;
|
HeuristicPrimeTester tester = _heuristicTester;
|
||||||
ulong[] values = _candidates;
|
ulong[] values = _candidates;
|
||||||
CancellationToken cancellationToken = CancellationToken.None;
|
|
||||||
int primeCount = 0;
|
int primeCount = 0;
|
||||||
|
|
||||||
for (int i = 0; i < values.Length; i++)
|
for (int i = 0; i < values.Length; i++)
|
||||||
{
|
{
|
||||||
if (tester.IsPrimeCpu(values[i], cancellationToken))
|
if (tester.IsPrimeCpu(values[i]))
|
||||||
{
|
{
|
||||||
primeCount++;
|
primeCount++;
|
||||||
}
|
}
|
||||||
@@ -75,7 +73,6 @@ public class PrimeTesterBenchmarks
|
|||||||
[Benchmark]
|
[Benchmark]
|
||||||
public int HeuristicCombinedGpu()
|
public int HeuristicCombinedGpu()
|
||||||
{
|
{
|
||||||
HeuristicCombinedPrimeTester tester = _heuristicCombinedTester;
|
|
||||||
ulong[] values = _candidates;
|
ulong[] values = _candidates;
|
||||||
int primeCount = 0;
|
int primeCount = 0;
|
||||||
|
|
||||||
@@ -93,7 +90,6 @@ public class PrimeTesterBenchmarks
|
|||||||
[Benchmark]
|
[Benchmark]
|
||||||
public int HeuristicCombinedCpu()
|
public int HeuristicCombinedCpu()
|
||||||
{
|
{
|
||||||
HeuristicCombinedPrimeTester tester = _heuristicCombinedTester;
|
|
||||||
ulong[] values = _candidates;
|
ulong[] values = _candidates;
|
||||||
int primeCount = 0;
|
int primeCount = 0;
|
||||||
|
|
||||||
@@ -115,12 +111,11 @@ public class PrimeTesterBenchmarks
|
|||||||
{
|
{
|
||||||
PrimeTester tester = _nonHeuristicTester;
|
PrimeTester tester = _nonHeuristicTester;
|
||||||
ulong[] values = _candidates;
|
ulong[] values = _candidates;
|
||||||
CancellationToken cancellationToken = CancellationToken.None;
|
|
||||||
int primeCount = 0;
|
int primeCount = 0;
|
||||||
|
|
||||||
for (int i = 0; i < values.Length; i++)
|
for (int i = 0; i < values.Length; i++)
|
||||||
{
|
{
|
||||||
if (tester.IsPrimeGpu(values[i], cancellationToken))
|
if (tester.IsPrimeGpu(values[i]))
|
||||||
{
|
{
|
||||||
primeCount++;
|
primeCount++;
|
||||||
}
|
}
|
||||||
@@ -133,12 +128,11 @@ public class PrimeTesterBenchmarks
|
|||||||
public int NonHeuristicCpu()
|
public int NonHeuristicCpu()
|
||||||
{
|
{
|
||||||
ulong[] values = _candidates;
|
ulong[] values = _candidates;
|
||||||
CancellationToken cancellationToken = CancellationToken.None;
|
|
||||||
int primeCount = 0;
|
int primeCount = 0;
|
||||||
|
|
||||||
for (int i = 0; i < values.Length; i++)
|
for (int i = 0; i < values.Length; i++)
|
||||||
{
|
{
|
||||||
if (PrimeTester.IsPrimeCpu(values[i], cancellationToken))
|
if (PrimeTester.IsPrime(values[i]))
|
||||||
{
|
{
|
||||||
primeCount++;
|
primeCount++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public class ProgramTests
|
|||||||
[InlineData(81UL, false)]
|
[InlineData(81UL, false)]
|
||||||
public void IsPrime_identifies_primes_correctly(ulong n, bool expected)
|
public void IsPrime_identifies_primes_correctly(ulong n, bool expected)
|
||||||
{
|
{
|
||||||
PrimeTester.IsPrime(n, CancellationToken.None).Should().Be(expected);
|
PrimeTester.IsPrime(n).Should().Be(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -622,8 +622,8 @@ internal static class Program
|
|||||||
if (!_cliArguments.UseByDivisor)
|
if (!_cliArguments.UseByDivisor)
|
||||||
{
|
{
|
||||||
if (!(GpuContextPool.ForceCpu
|
if (!(GpuContextPool.ForceCpu
|
||||||
? PrimeTester.IsPrime(p, CancellationToken.None)
|
? PrimeTester.IsPrime(p)
|
||||||
: PrimeTesters.Value!.IsPrimeGpu(p, CancellationToken.None)))
|
: PrimeTesters.Value!.IsPrimeGpu(p)))
|
||||||
{
|
{
|
||||||
_lastCompositeP = true;
|
_lastCompositeP = true;
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public class PrimeTesterGpuTests
|
|||||||
ulong[] primes = [31UL, 61UL, 89UL, 107UL, 127UL, 521UL];
|
ulong[] primes = [31UL, 61UL, 89UL, 107UL, 127UL, 521UL];
|
||||||
foreach (ulong prime in primes)
|
foreach (ulong prime in primes)
|
||||||
{
|
{
|
||||||
tester.IsPrimeGpu(prime, CancellationToken.None).Should().BeTrue();
|
tester.IsPrimeGpu(prime).Should().BeTrue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -37,7 +37,7 @@ public class PrimeTesterGpuTests
|
|||||||
ulong[] composites = [33UL, 39UL, 51UL, 77UL, 91UL];
|
ulong[] composites = [33UL, 39UL, 51UL, 77UL, 91UL];
|
||||||
foreach (ulong composite in composites)
|
foreach (ulong composite in composites)
|
||||||
{
|
{
|
||||||
tester.IsPrimeGpu(composite, CancellationToken.None).Should().BeFalse();
|
tester.IsPrimeGpu(composite).Should().BeFalse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class HeuristicCombinedPrimeTesterTests
|
|||||||
Span<HeuristicCombinedPrimeTester.HeuristicGroupBSequenceState> groupBState =
|
Span<HeuristicCombinedPrimeTester.HeuristicGroupBSequenceState> groupBState =
|
||||||
stackalloc HeuristicCombinedPrimeTester.HeuristicGroupBSequenceState[4];
|
stackalloc HeuristicCombinedPrimeTester.HeuristicGroupBSequenceState[4];
|
||||||
|
|
||||||
var enumerator = HeuristicCombinedPrimeTester.CreateHeuristicDivisorEnumerator(500, lastDigit, groupBState);
|
var enumerator = HeuristicCombinedPrimeTester.CreateHeuristicDivisorEnumerator(500UL * 500UL, lastDigit, groupBState);
|
||||||
var results = new List<HeuristicCombinedPrimeTester.HeuristicDivisorCandidate>(32);
|
var results = new List<HeuristicCombinedPrimeTester.HeuristicDivisorCandidate>(32);
|
||||||
|
|
||||||
while (results.Count < 20 && enumerator.TryGetNext(out var candidate))
|
while (results.Count < 20 && enumerator.TryGetNext(out var candidate))
|
||||||
@@ -230,14 +230,10 @@ public class HeuristicCombinedPrimeTesterTests
|
|||||||
[InlineData(18_446_744_073_709_551_615UL)]
|
[InlineData(18_446_744_073_709_551_615UL)]
|
||||||
[InlineData(18_446_744_073_709_551_614UL)]
|
[InlineData(18_446_744_073_709_551_614UL)]
|
||||||
[InlineData(18_446_744_073_709_551_613UL)]
|
[InlineData(18_446_744_073_709_551_613UL)]
|
||||||
public void ComputeHeuristicSqrt_returns_floor_square_root(ulong value)
|
public void ComputeHeuristicDivisorSquareLimit_returns_input_value(ulong value)
|
||||||
{
|
{
|
||||||
ulong sqrt = HeuristicCombinedPrimeTester.ComputeHeuristicSqrt(value);
|
ulong limit = HeuristicCombinedPrimeTester.ComputeHeuristicDivisorSquareLimit(value);
|
||||||
|
|
||||||
bool squareBelowOrEqual = (UInt128)sqrt * sqrt <= (UInt128)value;
|
limit.Should().Be(value);
|
||||||
bool nextSquareAbove = (UInt128)(sqrt + 1UL) * (UInt128)(sqrt + 1UL) > (UInt128)value;
|
|
||||||
|
|
||||||
squareBelowOrEqual.Should().BeTrue();
|
|
||||||
nextSquareAbove.Should().BeTrue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ public class PrimeTesterIsPrimeTests
|
|||||||
{
|
{
|
||||||
var tester = new PrimeTester();
|
var tester = new PrimeTester();
|
||||||
|
|
||||||
PrimeTester.IsPrime(n, CancellationToken.None).Should().Be(expected);
|
PrimeTester.IsPrime(n).Should().Be(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
@@ -40,7 +40,7 @@ public class PrimeTesterIsPrimeTests
|
|||||||
GpuContextPool.ForceCpu = false;
|
GpuContextPool.ForceCpu = false;
|
||||||
var tester = new PrimeTester();
|
var tester = new PrimeTester();
|
||||||
|
|
||||||
tester.IsPrimeGpu(n, CancellationToken.None).Should().Be(expected);
|
tester.IsPrimeGpu(n).Should().Be(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -50,9 +50,9 @@ public class PrimeTesterIsPrimeTests
|
|||||||
var tester = new PrimeTester();
|
var tester = new PrimeTester();
|
||||||
GpuContextPool.ForceCpu = true;
|
GpuContextPool.ForceCpu = true;
|
||||||
|
|
||||||
tester.IsPrimeGpu(137UL, CancellationToken.None).Should().BeTrue();
|
tester.IsPrimeGpu(137UL).Should().BeTrue();
|
||||||
|
|
||||||
tester.IsPrimeGpu(341UL, CancellationToken.None).Should().BeFalse();
|
tester.IsPrimeGpu(341UL).Should().BeFalse();
|
||||||
|
|
||||||
GpuContextPool.ForceCpu = false;
|
GpuContextPool.ForceCpu = false;
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ public class PrimeTesterIsPrimeTests
|
|||||||
var testers = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
var testers = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
||||||
Parallel.For(0, primes.Length, i =>
|
Parallel.For(0, primes.Length, i =>
|
||||||
{
|
{
|
||||||
results[i] = testers.Value!.IsPrimeGpu(primes[i], CancellationToken.None);
|
results[i] = testers.Value!.IsPrimeGpu(primes[i]);
|
||||||
});
|
});
|
||||||
|
|
||||||
results.Should().AllBeEquivalentTo(true);
|
results.Should().AllBeEquivalentTo(true);
|
||||||
@@ -80,7 +80,7 @@ public class PrimeTesterIsPrimeTests
|
|||||||
{
|
{
|
||||||
foreach (ulong prime in primes)
|
foreach (ulong prime in primes)
|
||||||
{
|
{
|
||||||
testers.Value!.IsPrimeGpu(prime, CancellationToken.None).Should().BeTrue();
|
testers.Value!.IsPrimeGpu(prime).Should().BeTrue();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ internal static class PrimeTesterKernels
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int length = XMath.Min((int)primes.Length, (int)primeSquares.Length);
|
int length = (int)primes.Length;
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
ulong prime = primes[i];
|
ulong prime = primes[i];
|
||||||
@@ -68,16 +68,10 @@ internal static class PrimeTesterKernels
|
|||||||
results[index] = result;
|
results[index] = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void HeuristicTrialDivisionKernel(Index1D index, ArrayView<ulong> divisors, ulong n, ArrayView<byte> results)
|
public static void HeuristicTrialDivisionKernel(Index1D index, ArrayView<ulong> divisors, ulong n, ArrayView<int> resultFlag)
|
||||||
{
|
{
|
||||||
ulong divisor = divisors[index];
|
ulong divisor = divisors[index];
|
||||||
if (divisor <= 1UL)
|
Atomic.Or(ref resultFlag[0], divisor <= 1UL ? 0 : (n % divisor == 0UL ? 1 : 0));
|
||||||
{
|
|
||||||
results[index] = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
results[index] = n % divisor == 0UL ? (byte)1 : (byte)0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SharesFactorKernel(Index1D index, ArrayView<ulong> numbers, ArrayView<byte> results)
|
public static void SharesFactorKernel(Index1D index, ArrayView<ulong> numbers, ArrayView<byte> results)
|
||||||
|
|||||||
@@ -292,6 +292,14 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
coveredRange = true;
|
coveredRange = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
divisorSpan[admissibleCount] = divisorValue;
|
||||||
|
divisorDataSpan[admissibleCount] = new GpuDivisorPartialData(divisorValue);
|
||||||
|
offsetSpan[admissibleCount] = admissibleCount;
|
||||||
|
countSpan[admissibleCount] = 1;
|
||||||
|
cycleSpan[admissibleCount] = divisorCycle;
|
||||||
|
exponentSpan[admissibleCount] = prime;
|
||||||
|
admissibleCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextDivisor128 += twoP128;
|
nextDivisor128 += twoP128;
|
||||||
@@ -444,13 +452,25 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
// {
|
// {
|
||||||
// return 0UL;
|
// return 0UL;
|
||||||
// }
|
// }
|
||||||
|
ulong cappedLimit = divisorLimit;
|
||||||
|
const ulong TestPrimeDivisorCap = 170_000_000UL;
|
||||||
|
if (prime < 10_000_000UL && cappedLimit > TestPrimeDivisorCap)
|
||||||
|
{
|
||||||
|
cappedLimit = TestPrimeDivisorCap;
|
||||||
|
}
|
||||||
|
|
||||||
if (prime - 1UL >= 64UL)
|
if (prime - 1UL >= 64UL)
|
||||||
{
|
{
|
||||||
return divisorLimit;
|
return cappedLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong computedLimit = (1UL << (int)(prime - 1UL)) - 1UL;
|
ulong computedLimit = (1UL << (int)(prime - 1UL)) - 1UL;
|
||||||
return computedLimit < divisorLimit ? computedLimit : divisorLimit;
|
if (prime < 10_000_000UL && computedLimit > cappedLimit)
|
||||||
|
{
|
||||||
|
computedLimit = cappedLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return computedLimit < cappedLimit ? computedLimit : cappedLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class DivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
|
public sealed class DivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
|
||||||
|
|||||||
@@ -17,29 +17,33 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
{
|
{
|
||||||
HeuristicPrimeSieves.EnsureInitialized();
|
HeuristicPrimeSieves.EnsureInitialized();
|
||||||
|
|
||||||
CombinedDivisorsEnding1TwoAOneB = BuildCombinedDivisors(1, CombinedDivisorPattern.TwoAOneB);
|
CombinedDivisorsEnding1TwoAOneB = BuildCombinedDivisors(1, CombinedDivisorPattern.TwoAOneB, out CombinedDivisorsEnding1TwoAOneBSquares);
|
||||||
CombinedDivisorsEnding3TwoAOneB = BuildCombinedDivisors(3, CombinedDivisorPattern.TwoAOneB);
|
CombinedDivisorsEnding3TwoAOneB = BuildCombinedDivisors(3, CombinedDivisorPattern.TwoAOneB, out CombinedDivisorsEnding3TwoAOneBSquares);
|
||||||
CombinedDivisorsEnding7TwoAOneB = BuildCombinedDivisors(7, CombinedDivisorPattern.TwoAOneB);
|
CombinedDivisorsEnding7TwoAOneB = BuildCombinedDivisors(7, CombinedDivisorPattern.TwoAOneB, out CombinedDivisorsEnding7TwoAOneBSquares);
|
||||||
CombinedDivisorsEnding9TwoAOneB = BuildCombinedDivisors(9, CombinedDivisorPattern.TwoAOneB);
|
CombinedDivisorsEnding9TwoAOneB = BuildCombinedDivisors(9, CombinedDivisorPattern.TwoAOneB, out CombinedDivisorsEnding9TwoAOneBSquares);
|
||||||
|
|
||||||
CombinedDivisorsEnding1OneAOneB = BuildCombinedDivisors(1, CombinedDivisorPattern.OneAOneB);
|
CombinedDivisorsEnding1OneAOneB = BuildCombinedDivisors(1, CombinedDivisorPattern.OneAOneB, out CombinedDivisorsEnding1OneAOneBSquares);
|
||||||
CombinedDivisorsEnding3OneAOneB = BuildCombinedDivisors(3, CombinedDivisorPattern.OneAOneB);
|
CombinedDivisorsEnding3OneAOneB = BuildCombinedDivisors(3, CombinedDivisorPattern.OneAOneB, out CombinedDivisorsEnding3OneAOneBSquares);
|
||||||
CombinedDivisorsEnding7OneAOneB = BuildCombinedDivisors(7, CombinedDivisorPattern.OneAOneB);
|
CombinedDivisorsEnding7OneAOneB = BuildCombinedDivisors(7, CombinedDivisorPattern.OneAOneB, out CombinedDivisorsEnding7OneAOneBSquares);
|
||||||
CombinedDivisorsEnding9OneAOneB = BuildCombinedDivisors(9, CombinedDivisorPattern.OneAOneB);
|
CombinedDivisorsEnding9OneAOneB = BuildCombinedDivisors(9, CombinedDivisorPattern.OneAOneB, out CombinedDivisorsEnding9OneAOneBSquares);
|
||||||
|
|
||||||
CombinedDivisorsEnding1ThreeAOneB = BuildCombinedDivisors(1, CombinedDivisorPattern.ThreeAOneB);
|
CombinedDivisorsEnding1ThreeAOneB = BuildCombinedDivisors(1, CombinedDivisorPattern.ThreeAOneB, out CombinedDivisorsEnding1ThreeAOneBSquares);
|
||||||
CombinedDivisorsEnding3ThreeAOneB = BuildCombinedDivisors(3, CombinedDivisorPattern.ThreeAOneB);
|
CombinedDivisorsEnding3ThreeAOneB = BuildCombinedDivisors(3, CombinedDivisorPattern.ThreeAOneB, out CombinedDivisorsEnding3ThreeAOneBSquares);
|
||||||
CombinedDivisorsEnding7ThreeAOneB = BuildCombinedDivisors(7, CombinedDivisorPattern.ThreeAOneB);
|
CombinedDivisorsEnding7ThreeAOneB = BuildCombinedDivisors(7, CombinedDivisorPattern.ThreeAOneB, out CombinedDivisorsEnding7ThreeAOneBSquares);
|
||||||
CombinedDivisorsEnding9ThreeAOneB = BuildCombinedDivisors(9, CombinedDivisorPattern.ThreeAOneB);
|
CombinedDivisorsEnding9ThreeAOneB = BuildCombinedDivisors(9, CombinedDivisorPattern.ThreeAOneB, out CombinedDivisorsEnding9ThreeAOneBSquares);
|
||||||
|
|
||||||
CombinedDivisorsEnding1ThreeATwoB = BuildCombinedDivisors(1, CombinedDivisorPattern.ThreeATwoB);
|
CombinedDivisorsEnding1ThreeATwoB = BuildCombinedDivisors(1, CombinedDivisorPattern.ThreeATwoB, out CombinedDivisorsEnding1ThreeATwoBSquares);
|
||||||
CombinedDivisorsEnding3ThreeATwoB = BuildCombinedDivisors(3, CombinedDivisorPattern.ThreeATwoB);
|
CombinedDivisorsEnding3ThreeATwoB = BuildCombinedDivisors(3, CombinedDivisorPattern.ThreeATwoB, out CombinedDivisorsEnding3ThreeATwoBSquares);
|
||||||
CombinedDivisorsEnding7ThreeATwoB = BuildCombinedDivisors(7, CombinedDivisorPattern.ThreeATwoB);
|
CombinedDivisorsEnding7ThreeATwoB = BuildCombinedDivisors(7, CombinedDivisorPattern.ThreeATwoB, out CombinedDivisorsEnding7ThreeATwoBSquares);
|
||||||
CombinedDivisorsEnding9ThreeATwoB = BuildCombinedDivisors(9, CombinedDivisorPattern.ThreeATwoB);
|
CombinedDivisorsEnding9ThreeATwoB = BuildCombinedDivisors(9, CombinedDivisorPattern.ThreeATwoB, out CombinedDivisorsEnding9ThreeATwoBSquares);
|
||||||
CombinedDivisorsEnding1 = CombinedDivisorsEnding1OneAOneB;
|
CombinedDivisorsEnding1 = CombinedDivisorsEnding1OneAOneB;
|
||||||
CombinedDivisorsEnding3 = CombinedDivisorsEnding3OneAOneB;
|
CombinedDivisorsEnding3 = CombinedDivisorsEnding3OneAOneB;
|
||||||
CombinedDivisorsEnding7 = CombinedDivisorsEnding7OneAOneB;
|
CombinedDivisorsEnding7 = CombinedDivisorsEnding7OneAOneB;
|
||||||
CombinedDivisorsEnding9 = CombinedDivisorsEnding9OneAOneB;
|
CombinedDivisorsEnding9 = CombinedDivisorsEnding9OneAOneB;
|
||||||
|
CombinedDivisorsEnding1Squares = CombinedDivisorsEnding1OneAOneBSquares;
|
||||||
|
CombinedDivisorsEnding3Squares = CombinedDivisorsEnding3OneAOneBSquares;
|
||||||
|
CombinedDivisorsEnding7Squares = CombinedDivisorsEnding7OneAOneBSquares;
|
||||||
|
CombinedDivisorsEnding9Squares = CombinedDivisorsEnding9OneAOneBSquares;
|
||||||
}
|
}
|
||||||
|
|
||||||
private const ulong Wheel210 = 210UL;
|
private const ulong Wheel210 = 210UL;
|
||||||
@@ -71,6 +75,31 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
private static readonly uint[] CombinedDivisorsEnding7;
|
private static readonly uint[] CombinedDivisorsEnding7;
|
||||||
private static readonly uint[] CombinedDivisorsEnding9;
|
private static readonly uint[] CombinedDivisorsEnding9;
|
||||||
|
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding1TwoAOneBSquares;
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding3TwoAOneBSquares;
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding7TwoAOneBSquares;
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding9TwoAOneBSquares;
|
||||||
|
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding1OneAOneBSquares;
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding3OneAOneBSquares;
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding7OneAOneBSquares;
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding9OneAOneBSquares;
|
||||||
|
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding1ThreeAOneBSquares;
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding3ThreeAOneBSquares;
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding7ThreeAOneBSquares;
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding9ThreeAOneBSquares;
|
||||||
|
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding1ThreeATwoBSquares;
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding3ThreeATwoBSquares;
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding7ThreeATwoBSquares;
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding9ThreeATwoBSquares;
|
||||||
|
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding1Squares;
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding3Squares;
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding7Squares;
|
||||||
|
private static readonly ulong[] CombinedDivisorsEnding9Squares;
|
||||||
|
|
||||||
private static readonly ulong[] HeuristicSmallCycleSnapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
private static readonly ulong[] HeuristicSmallCycleSnapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
||||||
|
|
||||||
internal enum CombinedDivisorPattern : byte
|
internal enum CombinedDivisorPattern : byte
|
||||||
@@ -123,53 +152,51 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
public static bool IsPrimeCpu(ulong n)
|
public static bool IsPrimeCpu(ulong n)
|
||||||
{
|
{
|
||||||
byte nMod10 = (byte)n.Mod10();
|
byte nMod10 = (byte)n.Mod10();
|
||||||
ulong sqrtLimit = ComputeHeuristicSqrt(n);
|
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
|
||||||
|
|
||||||
if (sqrtLimit < 3UL)
|
if (maxDivisorSquare < 9UL)
|
||||||
{
|
{
|
||||||
return EvaluateWithOpenNumericFallback(n);
|
return EvaluateWithOpenNumericFallback(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
return HeuristicTrialDivisionCpu(n, sqrtLimit, nMod10);
|
return HeuristicTrialDivisionCpu(n, maxDivisorSquare, nMod10);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsPrimeGpu(ulong n)
|
public static bool IsPrimeGpu(ulong n)
|
||||||
{
|
{
|
||||||
byte nMod10 = (byte)n.Mod10();
|
byte nMod10 = (byte)n.Mod10();
|
||||||
ulong sqrtLimit = ComputeHeuristicSqrt(n);
|
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
|
||||||
return IsPrimeGpu(n, sqrtLimit, nMod10);
|
return IsPrimeGpu(n, maxDivisorSquare, nMod10);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsPrimeGpu(ulong n, ulong sqrtLimit, byte nMod10)
|
public static bool IsPrimeGpu(ulong n, ulong maxDivisorSquare, byte nMod10)
|
||||||
{
|
{
|
||||||
// TODO: Is this condition ever met on the execution path in EvenPerfectBitScanner?
|
// TODO: Is this condition ever met on the execution path in EvenPerfectBitScanner?
|
||||||
if (sqrtLimit < 3UL)
|
if (maxDivisorSquare < 9UL)
|
||||||
{
|
{
|
||||||
return EvaluateWithOpenNumericFallback(n);
|
return EvaluateWithOpenNumericFallback(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(n, sqrtLimit, nMod10);
|
bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(n, maxDivisorSquare, nMod10);
|
||||||
return !compositeDetected;
|
return !compositeDetected;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HeuristicTrialDivisionCpu(ulong n, ulong sqrtLimit, byte nMod10)
|
private static bool HeuristicTrialDivisionCpu(ulong n, ulong maxDivisorSquare, byte nMod10)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<uint> combinedDivisors = GetCombinedDivisors(nMod10);
|
ReadOnlySpan<uint> combinedDivisors = GetCombinedDivisors(nMod10);
|
||||||
if (combinedDivisors.IsEmpty)
|
ReadOnlySpan<ulong> combinedDivisorSquares = GetCombinedDivisorSquares(nMod10);
|
||||||
{
|
|
||||||
return EvaluateWithOpenNumericFallback(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = combinedDivisors.Length;
|
int length = combinedDivisors.Length;
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
uint entry = combinedDivisors[i];
|
ulong divisorSquare = combinedDivisorSquares[i];
|
||||||
if (entry > sqrtLimit)
|
if (divisorSquare > maxDivisorSquare)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n % entry == 0UL)
|
if (n % combinedDivisors[i] == 0UL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -178,68 +205,50 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
return EvaluateWithOpenNumericFallback(n);
|
return EvaluateWithOpenNumericFallback(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong sqrtLimit, byte nMod10)
|
private static bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10)
|
||||||
{
|
{
|
||||||
int batchCapacity = HeuristicGpuDivisorBatchSize;
|
int batchCapacity = HeuristicGpuDivisorBatchSize;
|
||||||
var divisorPool = ThreadStaticPools.UlongPool;
|
var divisorPool = ThreadStaticPools.UlongPool;
|
||||||
var hitPool = ThreadStaticPools.BytePool;
|
|
||||||
|
|
||||||
ulong[] divisorArray = divisorPool.Rent(batchCapacity);
|
ulong[] divisorArray = divisorPool.Rent(batchCapacity);
|
||||||
byte[] hitFlags = hitPool.Rent(batchCapacity);
|
|
||||||
|
|
||||||
var limiter = GpuPrimeWorkLimiter.Acquire();
|
var limiter = GpuPrimeWorkLimiter.Acquire();
|
||||||
var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent();
|
var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(batchCapacity);
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = gpu.Accelerator;
|
||||||
var state = PrimeTester.GpuKernelState.GetOrCreate(accelerator);
|
var kernel = gpu.HeuristicTrialDivisionKernel;
|
||||||
var scratch = state.RentScratch(batchCapacity, accelerator);
|
|
||||||
|
|
||||||
bool compositeDetected = false;
|
bool compositeDetected = false;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
var input = scratch.Input;
|
var inputView = gpu.Input.View;
|
||||||
var output = scratch.Output;
|
var flagView = gpu.HeuristicFlag.View;
|
||||||
|
|
||||||
bool ProcessBatch(int length)
|
bool ProcessBatch(int length)
|
||||||
{
|
{
|
||||||
input.View.CopyFromCPU(ref divisorArray[0], length);
|
inputView.CopyFromCPU(ref divisorArray[0], length);
|
||||||
state.HeuristicTrialDivisionKernel(length, input.View, n, output.View);
|
int compositeFlag = 0;
|
||||||
|
flagView.CopyFromCPU(ref compositeFlag, 1);
|
||||||
|
kernel(length, inputView, n, flagView);
|
||||||
accelerator.Synchronize();
|
accelerator.Synchronize();
|
||||||
output.View.CopyToCPU(ref hitFlags[0], length);
|
flagView.CopyToCPU(ref compositeFlag, 1);
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
return compositeFlag != 0;
|
||||||
{
|
|
||||||
if (hitFlags[i] == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
// ulong divisor = divisorArray[i];
|
|
||||||
// if (divisor > 1UL && n % divisor == 0UL)
|
|
||||||
// {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<uint> combinedDivisors = GetCombinedDivisors(nMod10);
|
ReadOnlySpan<uint> combinedDivisors = GetCombinedDivisors(nMod10);
|
||||||
if (combinedDivisors.IsEmpty)
|
ReadOnlySpan<ulong> combinedDivisorSquares = GetCombinedDivisorSquares(nMod10);
|
||||||
{
|
|
||||||
goto Cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = combinedDivisors.Length;
|
int length = combinedDivisors.Length;
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
uint entry = combinedDivisors[i];
|
ulong divisorSquare = combinedDivisorSquares[i];
|
||||||
if (entry > sqrtLimit)
|
if (divisorSquare > maxDivisorSquare)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
divisorArray[count] = entry;
|
divisorArray[count] = combinedDivisors[i];
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
if (count == batchCapacity)
|
if (count == batchCapacity)
|
||||||
@@ -254,50 +263,28 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!compositeDetected && count > 0)
|
if (count > 0 && ProcessBatch(count))
|
||||||
{
|
|
||||||
if (ProcessBatch(count))
|
|
||||||
{
|
{
|
||||||
compositeDetected = true;
|
compositeDetected = true;
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Cleanup:
|
Cleanup:
|
||||||
state.ReturnScratch(scratch);
|
|
||||||
divisorPool.Return(divisorArray);
|
divisorPool.Return(divisorArray);
|
||||||
hitPool.Return(hitFlags);
|
|
||||||
gpu.Dispose();
|
gpu.Dispose();
|
||||||
limiter.Dispose();
|
limiter.Dispose();
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
internal static ulong ComputeHeuristicDivisorSquareLimit(ulong n)
|
||||||
internal static ulong ComputeHeuristicSqrt(ulong n)
|
|
||||||
{
|
{
|
||||||
ulong sqrt = (ulong)Math.Sqrt(n);
|
return n;
|
||||||
UInt128 square = (UInt128)sqrt * sqrt;
|
|
||||||
|
|
||||||
while (square > n)
|
|
||||||
{
|
|
||||||
sqrt--;
|
|
||||||
square = (UInt128)sqrt * sqrt;
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong next = sqrt + 1UL;
|
|
||||||
UInt128 nextSquare = (UInt128)next * next;
|
|
||||||
while (nextSquare <= n)
|
|
||||||
{
|
|
||||||
sqrt = next;
|
|
||||||
next++;
|
|
||||||
nextSquare = (UInt128)next * next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sqrt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReadOnlySpan<uint> GetCombinedDivisors(byte nMod10)
|
private static ReadOnlySpan<uint> GetCombinedDivisors(byte nMod10)
|
||||||
@@ -308,7 +295,19 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
3 => CombinedDivisorsEnding3,
|
3 => CombinedDivisorsEnding3,
|
||||||
7 => CombinedDivisorsEnding7,
|
7 => CombinedDivisorsEnding7,
|
||||||
9 => CombinedDivisorsEnding9,
|
9 => CombinedDivisorsEnding9,
|
||||||
_ => ReadOnlySpan<uint>.Empty,
|
_ => throw new InvalidOperationException($"Unsupported combined divisor selector for digit {nMod10}."),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReadOnlySpan<ulong> GetCombinedDivisorSquares(byte nMod10)
|
||||||
|
{
|
||||||
|
return nMod10 switch
|
||||||
|
{
|
||||||
|
1 => CombinedDivisorsEnding1Squares,
|
||||||
|
3 => CombinedDivisorsEnding3Squares,
|
||||||
|
7 => CombinedDivisorsEnding7Squares,
|
||||||
|
9 => CombinedDivisorsEnding9Squares,
|
||||||
|
_ => throw new InvalidOperationException($"Unsupported combined divisor square selector for digit {nMod10}."),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,7 +321,7 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
3 => CombinedDivisorsEnding3TwoAOneB,
|
3 => CombinedDivisorsEnding3TwoAOneB,
|
||||||
7 => CombinedDivisorsEnding7TwoAOneB,
|
7 => CombinedDivisorsEnding7TwoAOneB,
|
||||||
9 => CombinedDivisorsEnding9TwoAOneB,
|
9 => CombinedDivisorsEnding9TwoAOneB,
|
||||||
_ => ReadOnlySpan<uint>.Empty,
|
_ => throw new InvalidOperationException($"Unsupported combined divisor selector for digit {nMod10}."),
|
||||||
},
|
},
|
||||||
CombinedDivisorPattern.OneAOneB => nMod10 switch
|
CombinedDivisorPattern.OneAOneB => nMod10 switch
|
||||||
{
|
{
|
||||||
@@ -330,7 +329,7 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
3 => CombinedDivisorsEnding3OneAOneB,
|
3 => CombinedDivisorsEnding3OneAOneB,
|
||||||
7 => CombinedDivisorsEnding7OneAOneB,
|
7 => CombinedDivisorsEnding7OneAOneB,
|
||||||
9 => CombinedDivisorsEnding9OneAOneB,
|
9 => CombinedDivisorsEnding9OneAOneB,
|
||||||
_ => ReadOnlySpan<uint>.Empty,
|
_ => throw new InvalidOperationException($"Unsupported combined divisor selector for digit {nMod10}."),
|
||||||
},
|
},
|
||||||
CombinedDivisorPattern.ThreeAOneB => nMod10 switch
|
CombinedDivisorPattern.ThreeAOneB => nMod10 switch
|
||||||
{
|
{
|
||||||
@@ -338,7 +337,7 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
3 => CombinedDivisorsEnding3ThreeAOneB,
|
3 => CombinedDivisorsEnding3ThreeAOneB,
|
||||||
7 => CombinedDivisorsEnding7ThreeAOneB,
|
7 => CombinedDivisorsEnding7ThreeAOneB,
|
||||||
9 => CombinedDivisorsEnding9ThreeAOneB,
|
9 => CombinedDivisorsEnding9ThreeAOneB,
|
||||||
_ => ReadOnlySpan<uint>.Empty,
|
_ => throw new InvalidOperationException($"Unsupported combined divisor selector for digit {nMod10}."),
|
||||||
},
|
},
|
||||||
CombinedDivisorPattern.ThreeATwoB => nMod10 switch
|
CombinedDivisorPattern.ThreeATwoB => nMod10 switch
|
||||||
{
|
{
|
||||||
@@ -346,13 +345,53 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
3 => CombinedDivisorsEnding3ThreeATwoB,
|
3 => CombinedDivisorsEnding3ThreeATwoB,
|
||||||
7 => CombinedDivisorsEnding7ThreeATwoB,
|
7 => CombinedDivisorsEnding7ThreeATwoB,
|
||||||
9 => CombinedDivisorsEnding9ThreeATwoB,
|
9 => CombinedDivisorsEnding9ThreeATwoB,
|
||||||
_ => ReadOnlySpan<uint>.Empty,
|
_ => throw new InvalidOperationException($"Unsupported combined divisor selector for digit {nMod10}."),
|
||||||
},
|
},
|
||||||
_ => ReadOnlySpan<uint>.Empty,
|
_ => throw new InvalidOperationException($"Unsupported combined divisor pattern: {pattern}."),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static uint[] BuildCombinedDivisors(byte nMod10, CombinedDivisorPattern pattern)
|
internal static ReadOnlySpan<ulong> GetCombinedDivisorSquares(byte nMod10, CombinedDivisorPattern pattern)
|
||||||
|
{
|
||||||
|
return pattern switch
|
||||||
|
{
|
||||||
|
CombinedDivisorPattern.TwoAOneB => nMod10 switch
|
||||||
|
{
|
||||||
|
1 => CombinedDivisorsEnding1TwoAOneBSquares,
|
||||||
|
3 => CombinedDivisorsEnding3TwoAOneBSquares,
|
||||||
|
7 => CombinedDivisorsEnding7TwoAOneBSquares,
|
||||||
|
9 => CombinedDivisorsEnding9TwoAOneBSquares,
|
||||||
|
_ => throw new InvalidOperationException($"Unsupported combined divisor square selector for digit {nMod10}."),
|
||||||
|
},
|
||||||
|
CombinedDivisorPattern.OneAOneB => nMod10 switch
|
||||||
|
{
|
||||||
|
1 => CombinedDivisorsEnding1OneAOneBSquares,
|
||||||
|
3 => CombinedDivisorsEnding3OneAOneBSquares,
|
||||||
|
7 => CombinedDivisorsEnding7OneAOneBSquares,
|
||||||
|
9 => CombinedDivisorsEnding9OneAOneBSquares,
|
||||||
|
_ => throw new InvalidOperationException($"Unsupported combined divisor square selector for digit {nMod10}."),
|
||||||
|
},
|
||||||
|
CombinedDivisorPattern.ThreeAOneB => nMod10 switch
|
||||||
|
{
|
||||||
|
1 => CombinedDivisorsEnding1ThreeAOneBSquares,
|
||||||
|
3 => CombinedDivisorsEnding3ThreeAOneBSquares,
|
||||||
|
7 => CombinedDivisorsEnding7ThreeAOneBSquares,
|
||||||
|
9 => CombinedDivisorsEnding9ThreeAOneBSquares,
|
||||||
|
_ => throw new InvalidOperationException($"Unsupported combined divisor square selector for digit {nMod10}."),
|
||||||
|
},
|
||||||
|
CombinedDivisorPattern.ThreeATwoB => nMod10 switch
|
||||||
|
{
|
||||||
|
1 => CombinedDivisorsEnding1ThreeATwoBSquares,
|
||||||
|
3 => CombinedDivisorsEnding3ThreeATwoBSquares,
|
||||||
|
7 => CombinedDivisorsEnding7ThreeATwoBSquares,
|
||||||
|
9 => CombinedDivisorsEnding9ThreeATwoBSquares,
|
||||||
|
_ => throw new InvalidOperationException($"Unsupported combined divisor square selector for digit {nMod10}."),
|
||||||
|
},
|
||||||
|
_ => throw new InvalidOperationException($"Unsupported combined divisor pattern for squares: {pattern}."),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint[] BuildCombinedDivisors(byte nMod10, CombinedDivisorPattern pattern, out ulong[] squares)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<int> groupA = HeuristicPrimeSieves.GroupADivisors;
|
ReadOnlySpan<int> groupA = HeuristicPrimeSieves.GroupADivisors;
|
||||||
ReadOnlySpan<uint> groupB = GetGroupBDivisors(nMod10);
|
ReadOnlySpan<uint> groupB = GetGroupBDivisors(nMod10);
|
||||||
@@ -412,7 +451,16 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return combined.ToArray();
|
uint[] result = combined.ToArray();
|
||||||
|
ulong[] resultSquares = new ulong[result.Length];
|
||||||
|
for (int i = 0; i < result.Length; i++)
|
||||||
|
{
|
||||||
|
ulong divisor = result[i];
|
||||||
|
resultSquares[i] = divisor * divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
squares = resultSquares;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -447,9 +495,9 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
};
|
};
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal static HeuristicDivisorEnumerator CreateHeuristicDivisorEnumerator(ulong sqrtLimit, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
internal static HeuristicDivisorEnumerator CreateHeuristicDivisorEnumerator(ulong maxDivisorSquare, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
||||||
{
|
{
|
||||||
return new HeuristicDivisorEnumerator(sqrtLimit, nMod10, groupBBuffer);
|
return new HeuristicDivisorEnumerator(maxDivisorSquare, nMod10, groupBBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal struct HeuristicGroupBSequenceState
|
internal struct HeuristicGroupBSequenceState
|
||||||
@@ -531,15 +579,17 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
|
|
||||||
internal ref struct HeuristicDivisorEnumerator
|
internal ref struct HeuristicDivisorEnumerator
|
||||||
{
|
{
|
||||||
private readonly ulong sqrtLimit;
|
private readonly ulong maxDivisorSquare;
|
||||||
private readonly ReadOnlySpan<uint> combinedDivisors;
|
private readonly ReadOnlySpan<uint> combinedDivisors;
|
||||||
|
private readonly ReadOnlySpan<ulong> combinedDivisorSquares;
|
||||||
private int index;
|
private int index;
|
||||||
|
|
||||||
public HeuristicDivisorEnumerator(ulong sqrtLimit, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
public HeuristicDivisorEnumerator(ulong maxDivisorSquare, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
||||||
{
|
{
|
||||||
this.sqrtLimit = sqrtLimit;
|
this.maxDivisorSquare = maxDivisorSquare;
|
||||||
_ = groupBBuffer;
|
_ = groupBBuffer;
|
||||||
combinedDivisors = GetCombinedDivisors(nMod10);
|
combinedDivisors = GetCombinedDivisors(nMod10);
|
||||||
|
combinedDivisorSquares = GetCombinedDivisorSquares(nMod10);
|
||||||
index = 0;
|
index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,13 +597,17 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
{
|
{
|
||||||
while (index < combinedDivisors.Length)
|
while (index < combinedDivisors.Length)
|
||||||
{
|
{
|
||||||
uint entry = combinedDivisors[index++];
|
int currentIndex = index;
|
||||||
if (entry > sqrtLimit)
|
ulong divisorSquare = combinedDivisorSquares[currentIndex];
|
||||||
|
if (divisorSquare > maxDivisorSquare)
|
||||||
{
|
{
|
||||||
index = combinedDivisors.Length;
|
index = combinedDivisors.Length;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint entry = combinedDivisors[currentIndex];
|
||||||
|
index = currentIndex + 1;
|
||||||
|
|
||||||
HeuristicDivisorGroup group = ResolveGroup(entry);
|
HeuristicDivisorGroup group = ResolveGroup(entry);
|
||||||
ulong value = entry;
|
ulong value = entry;
|
||||||
byte ending = (byte)(value % 10UL);
|
byte ending = (byte)(value % 10UL);
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ public sealed class HeuristicPrimeTester
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Don't use these tiny arrays. Hard-code the values / checks instead
|
// TODO: Don't use these tiny arrays. Hard-code the values / checks instead
|
||||||
private static readonly ulong[] GroupAConstantDivisors = [3UL, 7UL, 11UL, 13UL];
|
|
||||||
private static readonly byte[] GroupAIncrementPattern = [20, 10];
|
|
||||||
private static readonly byte[] GroupBEndingOrderMod1 = [9, 1];
|
private static readonly byte[] GroupBEndingOrderMod1 = [9, 1];
|
||||||
private static readonly byte[] GroupBEndingOrderMod3 = [9, 7];
|
private static readonly byte[] GroupBEndingOrderMod3 = [9, 7];
|
||||||
private static readonly byte[] GroupBEndingOrderMod7 = [7, 1];
|
private static readonly byte[] GroupBEndingOrderMod7 = [7, 1];
|
||||||
@@ -30,10 +28,8 @@ public sealed class HeuristicPrimeTester
|
|||||||
|
|
||||||
// TODO: Would it speed up things if we implement something like ExponentRemainderStepper,
|
// TODO: Would it speed up things if we implement something like ExponentRemainderStepper,
|
||||||
// MersenneDivisorResidueStepper, or CycleRemainderStepper work with these?
|
// MersenneDivisorResidueStepper, or CycleRemainderStepper work with these?
|
||||||
private static readonly ushort[] Wheel210ResiduesEnding1 = [1, 11, 31, 41, 61, 71, 101, 121, 131, 151, 181, 191];
|
|
||||||
private static readonly ushort[] Wheel210ResiduesEnding7 = [17, 37, 47, 67, 97, 107, 127, 137, 157, 167, 187, 197];
|
|
||||||
private static readonly ushort[] Wheel210ResiduesEnding9 = [19, 29, 59, 79, 89, 109, 139, 149, 169, 179, 199, 209];
|
|
||||||
private const ulong Wheel210 = 210UL;
|
private const ulong Wheel210 = 210UL;
|
||||||
|
private const int GroupAConstantCount = 4;
|
||||||
internal const int MaxGroupBSequences = 36;
|
internal const int MaxGroupBSequences = 36;
|
||||||
|
|
||||||
private static readonly bool UseHeuristicGroupBTrialDivision = false; // Temporary fallback gate for Group B.
|
private static readonly bool UseHeuristicGroupBTrialDivision = false; // Temporary fallback gate for Group B.
|
||||||
@@ -81,76 +77,73 @@ public sealed class HeuristicPrimeTester
|
|||||||
|
|
||||||
public static HeuristicPrimeTester Exclusive => _tester ??= new();
|
public static HeuristicPrimeTester Exclusive => _tester ??= new();
|
||||||
|
|
||||||
public bool IsPrimeCpu(ulong n, CancellationToken ct)
|
public bool IsPrimeCpu(ulong n)
|
||||||
{
|
{
|
||||||
ct.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
byte nMod10 = (byte)n.Mod10();
|
byte nMod10 = (byte)n.Mod10();
|
||||||
ulong sqrtLimit = ComputeHeuristicSqrt(n);
|
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
|
||||||
|
|
||||||
if (sqrtLimit < 3UL)
|
if (maxDivisorSquare < 9UL)
|
||||||
{
|
{
|
||||||
return EvaluateWithOpenNumericFallback(n);
|
return EvaluateWithOpenNumericFallback(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool includeGroupB = UseHeuristicGroupBTrialDivision;
|
bool includeGroupB = UseHeuristicGroupBTrialDivision;
|
||||||
return HeuristicTrialDivisionCpu(n, sqrtLimit, nMod10, includeGroupB);
|
return HeuristicTrialDivisionCpu(n, maxDivisorSquare, nMod10, includeGroupB);
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsPrimeCpu(ulong n)
|
|
||||||
{
|
|
||||||
return IsPrimeCpu(n, CancellationToken.None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPrimeGpu(ulong n)
|
public bool IsPrimeGpu(ulong n)
|
||||||
{
|
{
|
||||||
byte nMod10 = (byte)n.Mod10();
|
byte nMod10 = (byte)n.Mod10();
|
||||||
ulong sqrtLimit = ComputeHeuristicSqrt(n);
|
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
|
||||||
return IsPrimeGpu(n, sqrtLimit, nMod10);
|
return IsPrimeGpu(n, maxDivisorSquare, nMod10);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPrimeGpu(ulong n, ulong sqrtLimit, byte nMod10)
|
public bool IsPrimeGpu(ulong n, ulong maxDivisorSquare, byte nMod10)
|
||||||
{
|
{
|
||||||
return HeuristicIsPrimeGpuCore(n, sqrtLimit, nMod10);
|
return HeuristicIsPrimeGpuCore(n, maxDivisorSquare, nMod10);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HeuristicIsPrimeGpuCore(ulong n, ulong sqrtLimit, byte nMod10)
|
private bool HeuristicIsPrimeGpuCore(ulong n, ulong maxDivisorSquare, byte nMod10)
|
||||||
{
|
{
|
||||||
// TODO: Is this condition ever met on the execution path in EvenPerfectBitScanner?
|
// TODO: Is this condition ever met on the execution path in EvenPerfectBitScanner?
|
||||||
if (sqrtLimit < 3UL)
|
if (maxDivisorSquare < 9UL)
|
||||||
{
|
{
|
||||||
return EvaluateWithOpenNumericFallback(n);
|
return EvaluateWithOpenNumericFallback(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (!UseHeuristicGroupBTrialDivision)
|
// if (!UseHeuristicGroupBTrialDivision)
|
||||||
// {
|
// {
|
||||||
// return HeuristicTrialDivisionCpu(n, sqrtLimit, nMod10, includeGroupB: false);
|
// return HeuristicTrialDivisionCpu(n, maxDivisorSquare, nMod10, includeGroupB: false);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(n, sqrtLimit, nMod10);
|
bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(n, maxDivisorSquare, nMod10);
|
||||||
return !compositeDetected;
|
return !compositeDetected;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HeuristicTrialDivisionCpu(ulong n, ulong sqrtLimit, byte nMod10, bool includeGroupB)
|
|
||||||
|
private static bool HeuristicTrialDivisionCpu(ulong n, ulong maxDivisorSquare, byte nMod10, bool includeGroupB)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<int> groupADivisors = HeuristicPrimeSieves.GroupADivisors;
|
ReadOnlySpan<int> groupADivisors = HeuristicPrimeSieves.GroupADivisors;
|
||||||
|
ReadOnlySpan<ulong> groupADivisorSquares = HeuristicPrimeSieves.GroupADivisorSquares;
|
||||||
int interleaveBatchSize = Math.Max(1, HeuristicDivisorInterleaveBatchSize);
|
int interleaveBatchSize = Math.Max(1, HeuristicDivisorInterleaveBatchSize);
|
||||||
int groupAIndex = 0;
|
int groupAIndex = 0;
|
||||||
|
|
||||||
if (!includeGroupB)
|
if (!includeGroupB)
|
||||||
{
|
{
|
||||||
while (groupAIndex < groupADivisors.Length)
|
int groupALength = groupADivisors.Length;
|
||||||
|
while (groupAIndex < groupALength)
|
||||||
{
|
{
|
||||||
int processed = 0;
|
int processed = 0;
|
||||||
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
while (processed < interleaveBatchSize && groupAIndex < groupALength)
|
||||||
{
|
{
|
||||||
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
ulong divisorSquare = groupADivisorSquares[groupAIndex];
|
||||||
if (divisor > sqrtLimit)
|
if (divisorSquare > maxDivisorSquare)
|
||||||
{
|
{
|
||||||
groupAIndex = groupADivisors.Length;
|
groupAIndex = groupALength;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
||||||
if (n % divisor == 0UL)
|
if (n % divisor == 0UL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -160,14 +153,14 @@ public sealed class HeuristicPrimeTester
|
|||||||
processed++;
|
processed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupAIndex >= groupADivisors.Length)
|
if (groupAIndex >= groupALength)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ulong)groupADivisors[groupAIndex] > sqrtLimit)
|
if (groupADivisorSquares[groupAIndex] > maxDivisorSquare)
|
||||||
{
|
{
|
||||||
groupAIndex = groupADivisors.Length;
|
groupAIndex = groupALength;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,8 +178,8 @@ public sealed class HeuristicPrimeTester
|
|||||||
? stackalloc int[endingOrder.Length]
|
? stackalloc int[endingOrder.Length]
|
||||||
: new int[endingOrder.Length];
|
: new int[endingOrder.Length];
|
||||||
|
|
||||||
bool groupAHasMore = groupAIndex < groupADivisors.Length && (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
bool groupAHasMore = groupAIndex < groupADivisors.Length && groupADivisorSquares[groupAIndex] <= maxDivisorSquare;
|
||||||
bool groupBHasMore = HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
bool groupBHasMore = HasGroupBCandidates(endingOrder, indices, maxDivisorSquare);
|
||||||
|
|
||||||
while (groupAHasMore || groupBHasMore)
|
while (groupAHasMore || groupBHasMore)
|
||||||
{
|
{
|
||||||
@@ -195,14 +188,15 @@ public sealed class HeuristicPrimeTester
|
|||||||
int processed = 0;
|
int processed = 0;
|
||||||
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
||||||
{
|
{
|
||||||
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
ulong divisorSquare = groupADivisorSquares[groupAIndex];
|
||||||
if (divisor > sqrtLimit)
|
if (divisorSquare > maxDivisorSquare)
|
||||||
{
|
{
|
||||||
groupAIndex = groupADivisors.Length;
|
groupAIndex = groupADivisors.Length;
|
||||||
groupAHasMore = false;
|
groupAHasMore = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
||||||
if (n % divisor == 0UL)
|
if (n % divisor == 0UL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -218,7 +212,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
groupAHasMore = (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
groupAHasMore = groupADivisorSquares[groupAIndex] <= maxDivisorSquare;
|
||||||
if (!groupAHasMore)
|
if (!groupAHasMore)
|
||||||
{
|
{
|
||||||
groupAIndex = groupADivisors.Length;
|
groupAIndex = groupADivisors.Length;
|
||||||
@@ -231,7 +225,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
int processed = 0;
|
int processed = 0;
|
||||||
while (processed < interleaveBatchSize)
|
while (processed < interleaveBatchSize)
|
||||||
{
|
{
|
||||||
if (!TrySelectNextGroupBDivisor(endingOrder, indices, sqrtLimit, out ulong divisor))
|
if (!TrySelectNextGroupBDivisor(endingOrder, indices, maxDivisorSquare, out ulong divisor))
|
||||||
{
|
{
|
||||||
groupBHasMore = false;
|
groupBHasMore = false;
|
||||||
break;
|
break;
|
||||||
@@ -247,7 +241,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
|
|
||||||
if (groupBHasMore)
|
if (groupBHasMore)
|
||||||
{
|
{
|
||||||
groupBHasMore = HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
groupBHasMore = HasGroupBCandidates(endingOrder, indices, maxDivisorSquare);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,29 +252,27 @@ public sealed class HeuristicPrimeTester
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
private bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong sqrtLimit, byte nMod10)
|
|
||||||
|
|
||||||
|
|
||||||
|
private bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10)
|
||||||
{
|
{
|
||||||
int batchCapacity = HeuristicGpuDivisorBatchSize;
|
int batchCapacity = HeuristicGpuDivisorBatchSize;
|
||||||
int interleaveBatchSize = HeuristicDivisorInterleaveBatchSize;
|
int interleaveBatchSize = HeuristicDivisorInterleaveBatchSize;
|
||||||
var divisorPool = ThreadStaticPools.UlongPool;
|
var divisorPool = ThreadStaticPools.UlongPool;
|
||||||
var hitPool = ThreadStaticPools.BytePool;
|
|
||||||
|
|
||||||
ulong[] divisorArray = divisorPool.Rent(batchCapacity);
|
ulong[] divisorArray = divisorPool.Rent(batchCapacity);
|
||||||
byte[] hitFlags = hitPool.Rent(batchCapacity);
|
|
||||||
|
|
||||||
var limiter = GpuPrimeWorkLimiter.Acquire();
|
var limiter = GpuPrimeWorkLimiter.Acquire();
|
||||||
var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent();
|
var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(batchCapacity);
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = gpu.Accelerator;
|
||||||
var state = PrimeTester.GpuKernelState.GetOrCreate(accelerator);
|
var kernel = gpu.HeuristicTrialDivisionKernel;
|
||||||
var scratch = state.RentScratch(batchCapacity, accelerator);
|
|
||||||
|
|
||||||
bool compositeDetected = false;
|
bool compositeDetected = false;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
// lock (gpu.ExecutionLock)
|
var inputView = gpu.Input.View;
|
||||||
{
|
var flagView = gpu.HeuristicFlag.View;
|
||||||
var input = scratch.Input;
|
|
||||||
var output = scratch.Output;
|
|
||||||
|
|
||||||
bool ProcessBatch(int length)
|
bool ProcessBatch(int length)
|
||||||
{
|
{
|
||||||
@@ -289,37 +281,25 @@ public sealed class HeuristicPrimeTester
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
input.View.CopyFromCPU(ref divisorArray[0], length);
|
inputView.CopyFromCPU(ref divisorArray[0], length);
|
||||||
state.HeuristicTrialDivisionKernel(length, input.View, n, output.View);
|
int compositeFlag = 0;
|
||||||
|
flagView.CopyFromCPU(ref compositeFlag, 1);
|
||||||
|
kernel(length, inputView, n, flagView);
|
||||||
accelerator.Synchronize();
|
accelerator.Synchronize();
|
||||||
output.View.CopyToCPU(ref hitFlags[0], length);
|
flagView.CopyToCPU(ref compositeFlag, 1);
|
||||||
|
return compositeFlag != 0;
|
||||||
for (int i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
if (hitFlags[i] == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong divisor = divisorArray[i];
|
|
||||||
if (divisor > 1UL && n % divisor == 0UL)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<int> groupADivisors = HeuristicPrimeSieves.GroupADivisors;
|
ReadOnlySpan<int> groupADivisors = HeuristicPrimeSieves.GroupADivisors;
|
||||||
|
ReadOnlySpan<ulong> groupADivisorSquares = HeuristicPrimeSieves.GroupADivisorSquares;
|
||||||
int groupAIndex = 0;
|
int groupAIndex = 0;
|
||||||
ReadOnlySpan<byte> endingOrder = GetGroupBEndingOrder(nMod10);
|
ReadOnlySpan<byte> endingOrder = GetGroupBEndingOrder(nMod10);
|
||||||
Span<int> indices = endingOrder.Length <= 8
|
Span<int> indices = endingOrder.Length <= 8
|
||||||
? stackalloc int[endingOrder.Length]
|
? stackalloc int[endingOrder.Length]
|
||||||
: new int[endingOrder.Length];
|
: new int[endingOrder.Length];
|
||||||
|
|
||||||
bool groupAHasMore = groupAIndex < groupADivisors.Length && (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
bool groupAHasMore = groupAIndex < groupADivisors.Length && groupADivisorSquares[groupAIndex] <= maxDivisorSquare;
|
||||||
bool groupBHasMore = !endingOrder.IsEmpty && HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
bool groupBHasMore = !endingOrder.IsEmpty && HasGroupBCandidates(endingOrder, indices, maxDivisorSquare);
|
||||||
|
|
||||||
while (!compositeDetected && (groupAHasMore || groupBHasMore))
|
while (!compositeDetected && (groupAHasMore || groupBHasMore))
|
||||||
{
|
{
|
||||||
@@ -328,15 +308,15 @@ public sealed class HeuristicPrimeTester
|
|||||||
int processed = 0;
|
int processed = 0;
|
||||||
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
||||||
{
|
{
|
||||||
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
ulong divisorSquare = groupADivisorSquares[groupAIndex];
|
||||||
if (divisor > sqrtLimit)
|
if (divisorSquare > maxDivisorSquare)
|
||||||
{
|
{
|
||||||
groupAIndex = groupADivisors.Length;
|
groupAIndex = groupADivisors.Length;
|
||||||
groupAHasMore = false;
|
groupAHasMore = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
divisorArray[count] = divisor;
|
divisorArray[count] = (ulong)groupADivisors[groupAIndex];
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
if (count == batchCapacity)
|
if (count == batchCapacity)
|
||||||
@@ -344,7 +324,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
if (ProcessBatch(count))
|
if (ProcessBatch(count))
|
||||||
{
|
{
|
||||||
compositeDetected = true;
|
compositeDetected = true;
|
||||||
goto Cleanup;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
count = 0;
|
count = 0;
|
||||||
@@ -354,13 +334,18 @@ public sealed class HeuristicPrimeTester
|
|||||||
processed++;
|
processed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (compositeDetected)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (groupAIndex >= groupADivisors.Length)
|
if (groupAIndex >= groupADivisors.Length)
|
||||||
{
|
{
|
||||||
groupAHasMore = false;
|
groupAHasMore = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
groupAHasMore = (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
groupAHasMore = groupADivisorSquares[groupAIndex] <= maxDivisorSquare;
|
||||||
if (!groupAHasMore)
|
if (!groupAHasMore)
|
||||||
{
|
{
|
||||||
groupAIndex = groupADivisors.Length;
|
groupAIndex = groupADivisors.Length;
|
||||||
@@ -373,7 +358,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
int processed = 0;
|
int processed = 0;
|
||||||
while (processed < interleaveBatchSize)
|
while (processed < interleaveBatchSize)
|
||||||
{
|
{
|
||||||
if (!TrySelectNextGroupBDivisor(endingOrder, indices, sqrtLimit, out ulong divisor))
|
if (!TrySelectNextGroupBDivisor(endingOrder, indices, maxDivisorSquare, out ulong divisor))
|
||||||
{
|
{
|
||||||
groupBHasMore = false;
|
groupBHasMore = false;
|
||||||
break;
|
break;
|
||||||
@@ -387,7 +372,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
if (ProcessBatch(count))
|
if (ProcessBatch(count))
|
||||||
{
|
{
|
||||||
compositeDetected = true;
|
compositeDetected = true;
|
||||||
goto Cleanup;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
count = 0;
|
count = 0;
|
||||||
@@ -396,60 +381,33 @@ public sealed class HeuristicPrimeTester
|
|||||||
processed++;
|
processed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupBHasMore)
|
if (!compositeDetected && groupBHasMore)
|
||||||
{
|
{
|
||||||
groupBHasMore = HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
groupBHasMore = HasGroupBCandidates(endingOrder, indices, maxDivisorSquare);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!compositeDetected && count > 0)
|
if (!compositeDetected && count > 0 && ProcessBatch(count))
|
||||||
{
|
|
||||||
if (ProcessBatch(count))
|
|
||||||
{
|
{
|
||||||
compositeDetected = true;
|
compositeDetected = true;
|
||||||
goto Cleanup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Cleanup:
|
|
||||||
state.ReturnScratch(scratch);
|
|
||||||
divisorPool.Return(divisorArray);
|
divisorPool.Return(divisorArray);
|
||||||
hitPool.Return(hitFlags);
|
|
||||||
gpu.Dispose();
|
gpu.Dispose();
|
||||||
limiter.Dispose();
|
limiter.Dispose();
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
internal static ulong ComputeHeuristicDivisorSquareLimit(ulong n)
|
||||||
internal static ulong ComputeHeuristicSqrt(ulong n)
|
|
||||||
{
|
{
|
||||||
ulong sqrt = (ulong)Math.Sqrt(n);
|
return n;
|
||||||
UInt128 square = (UInt128)sqrt * sqrt;
|
|
||||||
|
|
||||||
while (square > n)
|
|
||||||
{
|
|
||||||
sqrt--;
|
|
||||||
square = (UInt128)sqrt * sqrt;
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong next = sqrt + 1UL;
|
|
||||||
UInt128 nextSquare = (UInt128)next * next;
|
|
||||||
while (nextSquare <= n)
|
|
||||||
{
|
|
||||||
sqrt = next;
|
|
||||||
next++;
|
|
||||||
nextSquare = (UInt128)next * next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sqrt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> GetGroupBEndingOrder(byte nMod10) => nMod10 switch
|
private static ReadOnlySpan<byte> GetGroupBEndingOrder(byte nMod10) => nMod10 switch
|
||||||
@@ -462,8 +420,9 @@ Cleanup:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static bool TrySelectNextGroupBDivisor(ReadOnlySpan<byte> endingOrder, Span<int> indices, ulong sqrtLimit, out ulong divisor)
|
private static bool TrySelectNextGroupBDivisor(ReadOnlySpan<byte> endingOrder, Span<int> indices, ulong maxDivisorSquare, out ulong divisor)
|
||||||
{
|
{
|
||||||
ulong bestCandidate = ulong.MaxValue;
|
ulong bestCandidate = ulong.MaxValue;
|
||||||
int bestEndingIndex = -1;
|
int bestEndingIndex = -1;
|
||||||
@@ -471,19 +430,21 @@ Cleanup:
|
|||||||
for (int i = 0; i < endingOrder.Length; i++)
|
for (int i = 0; i < endingOrder.Length; i++)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<uint> divisors = GetGroupBDivisors(endingOrder[i]);
|
ReadOnlySpan<uint> divisors = GetGroupBDivisors(endingOrder[i]);
|
||||||
|
ReadOnlySpan<ulong> squares = GetGroupBDivisorSquares(endingOrder[i]);
|
||||||
int index = indices[i];
|
int index = indices[i];
|
||||||
if ((uint)index >= (uint)divisors.Length)
|
if ((uint)index >= (uint)divisors.Length)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong candidate = (ulong)divisors[index];
|
ulong candidateSquare = squares[index];
|
||||||
if (candidate > sqrtLimit)
|
if (candidateSquare > maxDivisorSquare)
|
||||||
{
|
{
|
||||||
indices[i] = divisors.Length;
|
indices[i] = divisors.Length;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ulong candidate = (ulong)divisors[index];
|
||||||
if (bestEndingIndex == -1 || candidate < bestCandidate)
|
if (bestEndingIndex == -1 || candidate < bestCandidate)
|
||||||
{
|
{
|
||||||
bestCandidate = candidate;
|
bestCandidate = candidate;
|
||||||
@@ -497,25 +458,29 @@ Cleanup:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
divisor = bestCandidate;
|
ReadOnlySpan<uint> selectedDivisors = GetGroupBDivisors(endingOrder[bestEndingIndex]);
|
||||||
|
divisor = (ulong)selectedDivisors[indices[bestEndingIndex]];
|
||||||
indices[bestEndingIndex]++;
|
indices[bestEndingIndex]++;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static bool HasGroupBCandidates(ReadOnlySpan<byte> endingOrder, Span<int> indices, ulong sqrtLimit)
|
private static bool HasGroupBCandidates(ReadOnlySpan<byte> endingOrder, Span<int> indices, ulong maxDivisorSquare)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < endingOrder.Length; i++)
|
for (int i = 0; i < endingOrder.Length; i++)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<uint> divisors = GetGroupBDivisors(endingOrder[i]);
|
ReadOnlySpan<uint> divisors = GetGroupBDivisors(endingOrder[i]);
|
||||||
|
ReadOnlySpan<ulong> squares = GetGroupBDivisorSquares(endingOrder[i]);
|
||||||
int index = indices[i];
|
int index = indices[i];
|
||||||
if ((uint)index >= (uint)divisors.Length)
|
if ((uint)index >= (uint)divisors.Length)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong candidate = (ulong)divisors[index];
|
if (squares[index] > maxDivisorSquare)
|
||||||
if (candidate > sqrtLimit)
|
|
||||||
{
|
{
|
||||||
indices[i] = divisors.Length;
|
indices[i] = divisors.Length;
|
||||||
continue;
|
continue;
|
||||||
@@ -527,6 +492,8 @@ Cleanup:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static ReadOnlySpan<uint> GetGroupBDivisors(byte ending) => ending switch
|
private static ReadOnlySpan<uint> GetGroupBDivisors(byte ending) => ending switch
|
||||||
{
|
{
|
||||||
1 => DivisorGenerator.SmallPrimesLastOneWithoutLastThree,
|
1 => DivisorGenerator.SmallPrimesLastOneWithoutLastThree,
|
||||||
@@ -536,35 +503,32 @@ Cleanup:
|
|||||||
_ => ReadOnlySpan<uint>.Empty,
|
_ => ReadOnlySpan<uint>.Empty,
|
||||||
};
|
};
|
||||||
|
|
||||||
private static ReadOnlySpan<ushort> GetWheel210ResiduesForEnding(byte ending) => ending switch
|
private static ReadOnlySpan<ulong> GetGroupBDivisorSquares(byte ending) => ending switch
|
||||||
{
|
{
|
||||||
1 => Wheel210ResiduesEnding1,
|
1 => DivisorGenerator.SmallPrimesPow2LastOneWithoutLastThree,
|
||||||
7 => Wheel210ResiduesEnding7,
|
3 => ReadOnlySpan<ulong>.Empty,
|
||||||
9 => Wheel210ResiduesEnding9,
|
7 => DivisorGenerator.SmallPrimesPow2LastSevenWithoutLastThree,
|
||||||
_ => ReadOnlySpan<ushort>.Empty,
|
9 => DivisorGenerator.SmallPrimesPow2LastNineWithoutLastThree,
|
||||||
|
_ => ReadOnlySpan<ulong>.Empty,
|
||||||
};
|
};
|
||||||
|
|
||||||
private static int InitializeGroupBStates(byte nMod10, Span<HeuristicGroupBSequenceState> buffer)
|
private static int InitializeGroupBStates(byte nMod10, Span<HeuristicGroupBSequenceState> buffer)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> endings = GetGroupBEndingOrder(nMod10);
|
ReadOnlySpan<byte> endings = GetGroupBEndingOrder(nMod10);
|
||||||
int count = 0;
|
int count = endings.Length;
|
||||||
|
|
||||||
for (int priorityIndex = 0; priorityIndex < endings.Length; priorityIndex++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<ushort> residues = GetWheel210ResiduesForEnding(endings[priorityIndex]);
|
buffer[i] = new HeuristicGroupBSequenceState(endings[i], (byte)i);
|
||||||
for (int residueIndex = 0; residueIndex < residues.Length; residueIndex++)
|
|
||||||
{
|
|
||||||
buffer[count++] = new HeuristicGroupBSequenceState(residues[residueIndex], endings[priorityIndex], (byte)priorityIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal static HeuristicDivisorEnumerator CreateHeuristicDivisorEnumerator(ulong sqrtLimit, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
internal static HeuristicDivisorEnumerator CreateHeuristicDivisorEnumerator(ulong maxDivisorSquare, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
||||||
{
|
{
|
||||||
return new HeuristicDivisorEnumerator(sqrtLimit, nMod10, groupBBuffer);
|
return new HeuristicDivisorEnumerator(maxDivisorSquare, nMod10, groupBBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -640,96 +604,69 @@ Cleanup:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal ref struct HeuristicDivisorEnumerator
|
internal ref struct HeuristicDivisorEnumerator
|
||||||
{
|
{
|
||||||
private readonly ulong sqrtLimit;
|
private readonly ulong maxDivisorSquare;
|
||||||
private int groupAConstantIndex;
|
private readonly ReadOnlySpan<int> groupADivisors;
|
||||||
private ulong groupAWheelCandidate;
|
private readonly ReadOnlySpan<ulong> groupADivisorSquares;
|
||||||
private int groupAIncrementIndex;
|
private int groupAIndex;
|
||||||
private bool groupAWheelActive;
|
|
||||||
private Span<HeuristicGroupBSequenceState> groupBStates;
|
private Span<HeuristicGroupBSequenceState> groupBStates;
|
||||||
|
|
||||||
public HeuristicDivisorEnumerator(ulong sqrtLimit, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
public HeuristicDivisorEnumerator(ulong maxDivisorSquare, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
||||||
{
|
{
|
||||||
this.sqrtLimit = sqrtLimit;
|
this.maxDivisorSquare = maxDivisorSquare;
|
||||||
groupAConstantIndex = 0;
|
groupADivisors = HeuristicPrimeSieves.GroupADivisors;
|
||||||
groupAWheelCandidate = 23UL;
|
groupADivisorSquares = HeuristicPrimeSieves.GroupADivisorSquares;
|
||||||
groupAIncrementIndex = 0;
|
groupAIndex = 0;
|
||||||
groupAWheelActive = sqrtLimit >= 23UL;
|
|
||||||
|
|
||||||
int count = InitializeGroupBStates(nMod10, groupBBuffer);
|
int count = InitializeGroupBStates(nMod10, groupBBuffer);
|
||||||
groupBStates = count == 0 ? Span<HeuristicGroupBSequenceState>.Empty : groupBBuffer[..count];
|
groupBStates = count == 0 ? Span<HeuristicGroupBSequenceState>.Empty : groupBBuffer[..count];
|
||||||
|
|
||||||
for (int i = 0; i < groupBStates.Length; i++)
|
|
||||||
{
|
|
||||||
groupBStates[i].TryNormalize();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetNext(out HeuristicDivisorCandidate candidate)
|
public bool TryGetNext(out HeuristicDivisorCandidate candidate)
|
||||||
{
|
{
|
||||||
// TODO: Hard-code constant A divisor checks here instead of using an array
|
while (groupAIndex < groupADivisors.Length)
|
||||||
while (groupAConstantIndex < GroupAConstantDivisors.Length)
|
|
||||||
{
|
{
|
||||||
ulong value = GroupAConstantDivisors[groupAConstantIndex++];
|
ulong divisorSquare = groupADivisorSquares[groupAIndex];
|
||||||
if (value > sqrtLimit)
|
if (divisorSquare > maxDivisorSquare)
|
||||||
{
|
{
|
||||||
groupAConstantIndex = GroupAConstantDivisors.Length;
|
groupAIndex = groupADivisors.Length;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We're constantly dividing constants mod 10 which is constant too.
|
int currentIndex = groupAIndex;
|
||||||
candidate = new HeuristicDivisorCandidate(value, HeuristicDivisorGroup.GroupAConstant, (byte)(value % 10UL), 0, (ushort)value);
|
ulong divisor = (ulong)groupADivisors[currentIndex];
|
||||||
|
groupAIndex++;
|
||||||
|
|
||||||
|
HeuristicDivisorGroup group = currentIndex < GroupAConstantCount
|
||||||
|
? HeuristicDivisorGroup.GroupAConstant
|
||||||
|
: HeuristicDivisorGroup.GroupAWheel;
|
||||||
|
|
||||||
|
candidate = new HeuristicDivisorCandidate(
|
||||||
|
divisor,
|
||||||
|
group,
|
||||||
|
(byte)(divisor % 10UL),
|
||||||
|
0,
|
||||||
|
(ushort)(divisor % Wheel210));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupAWheelActive)
|
|
||||||
{
|
|
||||||
ulong value = groupAWheelCandidate;
|
|
||||||
if (value <= sqrtLimit)
|
|
||||||
{
|
|
||||||
candidate = new HeuristicDivisorCandidate(value, HeuristicDivisorGroup.GroupAWheel, 3, 0, (ushort)(value % Wheel210));
|
|
||||||
byte increment = GroupAIncrementPattern[groupAIncrementIndex];
|
|
||||||
groupAIncrementIndex ^= 1;
|
|
||||||
|
|
||||||
ulong next = value + increment;
|
|
||||||
if (next <= value)
|
|
||||||
{
|
|
||||||
groupAWheelActive = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
groupAWheelCandidate = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
groupAWheelActive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong bestCandidate = ulong.MaxValue;
|
ulong bestCandidate = ulong.MaxValue;
|
||||||
int bestIndex = -1;
|
int bestIndex = -1;
|
||||||
|
|
||||||
for (int i = 0; i < groupBStates.Length; i++)
|
for (int i = 0; i < groupBStates.Length; i++)
|
||||||
{
|
{
|
||||||
ref HeuristicGroupBSequenceState state = ref groupBStates[i];
|
ref HeuristicGroupBSequenceState state = ref groupBStates[i];
|
||||||
ulong value = state.Candidate;
|
if (!state.TryPeek(maxDivisorSquare, out ulong candidateValue))
|
||||||
if (value == ulong.MaxValue)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value > sqrtLimit)
|
if (bestIndex == -1 || candidateValue < bestCandidate ||
|
||||||
|
(candidateValue == bestCandidate && state.PriorityIndex < groupBStates[bestIndex].PriorityIndex))
|
||||||
{
|
{
|
||||||
state.Candidate = ulong.MaxValue;
|
bestCandidate = candidateValue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bestIndex == -1 || value < bestCandidate ||
|
|
||||||
(value == bestCandidate && state.PriorityIndex < groupBStates[bestIndex].PriorityIndex))
|
|
||||||
{
|
|
||||||
bestCandidate = value;
|
|
||||||
bestIndex = i;
|
bestIndex = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -741,57 +678,69 @@ Cleanup:
|
|||||||
}
|
}
|
||||||
|
|
||||||
ref HeuristicGroupBSequenceState bestState = ref groupBStates[bestIndex];
|
ref HeuristicGroupBSequenceState bestState = ref groupBStates[bestIndex];
|
||||||
var result = new HeuristicDivisorCandidate(bestState.Candidate, HeuristicDivisorGroup.GroupB, bestState.Ending, bestState.PriorityIndex, bestState.Residue);
|
|
||||||
bestState.Advance();
|
bestState.Advance();
|
||||||
candidate = result;
|
candidate = new HeuristicDivisorCandidate(
|
||||||
|
bestCandidate,
|
||||||
|
HeuristicDivisorGroup.GroupB,
|
||||||
|
bestState.Ending,
|
||||||
|
bestState.PriorityIndex,
|
||||||
|
(ushort)(bestCandidate % Wheel210));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal struct HeuristicGroupBSequenceState(ushort residue, byte ending, byte priorityIndex)
|
|
||||||
{
|
|
||||||
public ulong Candidate = residue;
|
|
||||||
public byte PriorityIndex = priorityIndex;
|
|
||||||
public byte Ending = ending;
|
|
||||||
public ushort Residue = residue;
|
|
||||||
|
|
||||||
public void TryNormalize()
|
|
||||||
|
internal struct HeuristicGroupBSequenceState
|
||||||
{
|
{
|
||||||
ulong candidate = Candidate;
|
public byte Ending;
|
||||||
if (candidate == ulong.MaxValue)
|
public byte PriorityIndex;
|
||||||
|
public int Index;
|
||||||
|
|
||||||
|
public HeuristicGroupBSequenceState(byte ending, byte priorityIndex)
|
||||||
{
|
{
|
||||||
return;
|
Ending = ending;
|
||||||
|
PriorityIndex = priorityIndex;
|
||||||
|
Index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (candidate <= 13UL)
|
public bool TryPeek(ulong maxDivisorSquare, out ulong divisor)
|
||||||
{
|
{
|
||||||
ulong next = candidate + Wheel210;
|
ReadOnlySpan<uint> divisors = GetGroupBDivisors(Ending);
|
||||||
if (next <= candidate)
|
ReadOnlySpan<ulong> squares = GetGroupBDivisorSquares(Ending);
|
||||||
|
int index = Index;
|
||||||
|
if ((uint)index >= (uint)divisors.Length)
|
||||||
{
|
{
|
||||||
Candidate = ulong.MaxValue;
|
divisor = 0UL;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate = next;
|
if (squares[index] > maxDivisorSquare)
|
||||||
|
{
|
||||||
|
Index = divisors.Length;
|
||||||
|
divisor = 0UL;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Candidate = candidate;
|
divisor = (ulong)divisors[index];
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Advance()
|
public void Advance()
|
||||||
{
|
{
|
||||||
ulong candidate = Candidate;
|
int next = Index + 1;
|
||||||
if (candidate == ulong.MaxValue)
|
ReadOnlySpan<uint> divisors = GetGroupBDivisors(Ending);
|
||||||
|
if ((uint)next >= (uint)divisors.Length)
|
||||||
{
|
{
|
||||||
return;
|
Index = divisors.Length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Index = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong next = candidate + Wheel210;
|
|
||||||
Candidate = next > candidate ? next : ulong.MaxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal ref struct MersenneHeuristicDivisorEnumerator
|
internal ref struct MersenneHeuristicDivisorEnumerator
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -411,8 +411,8 @@ public class MersenneDivisorCycles
|
|||||||
// Atomic.Add(ref _tryFactorIntoCountsInternalHits, 1UL);
|
// Atomic.Add(ref _tryFactorIntoCountsInternalHits, 1UL);
|
||||||
// Console.WriteLine($"MersenneDivisorCycles.TryFactorIntoCountsInternal hits {Volatile.Read(ref _tryFactorIntoCountsInternalHits)}");
|
// Console.WriteLine($"MersenneDivisorCycles.TryFactorIntoCountsInternal hits {Volatile.Read(ref _tryFactorIntoCountsInternalHits)}");
|
||||||
|
|
||||||
HeuristicPrimeTester primeTester = _primeTester ??= new();
|
// HeuristicPrimeTester primeTester = _primeTester ??= new();
|
||||||
if (primeTester.IsPrimeGpu(remaining))
|
if (PrimeTester.IsPrime(remaining))
|
||||||
// if (PrimeTester.IsPrimeCpu(remaining, CancellationToken.None))
|
// if (PrimeTester.IsPrimeCpu(remaining, CancellationToken.None))
|
||||||
{
|
{
|
||||||
AddFactor(counts, remaining);
|
AddFactor(counts, remaining);
|
||||||
@@ -917,8 +917,8 @@ public class MersenneDivisorCycles
|
|||||||
return CalculateCycleLengthFallback(divisor);
|
return CalculateCycleLengthFallback(divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ThreadStatic]
|
// [ThreadStatic]
|
||||||
private static HeuristicPrimeTester? _primeTester;
|
// private static HeuristicPrimeTester? _primeTester;
|
||||||
|
|
||||||
internal static bool TryCalculateCycleLengthHeuristic(ulong divisor, in MontgomeryDivisorData divisorData, out ulong cycleLength, bool skipPrimeOrderHeuristic = false)
|
internal static bool TryCalculateCycleLengthHeuristic(ulong divisor, in MontgomeryDivisorData divisorData, out ulong cycleLength, bool skipPrimeOrderHeuristic = false)
|
||||||
{
|
{
|
||||||
@@ -940,10 +940,10 @@ public class MersenneDivisorCycles
|
|||||||
// Console.WriteLine($"MersenneDivisorCycles.CalculateCycleLengthFallback hits {Volatile.Read(ref _tryCalculateCycleLengthHeuristicHits)}");
|
// Console.WriteLine($"MersenneDivisorCycles.CalculateCycleLengthFallback hits {Volatile.Read(ref _tryCalculateCycleLengthHeuristicHits)}");
|
||||||
// }
|
// }
|
||||||
|
|
||||||
HeuristicPrimeTester primeTester = _primeTester ??= new();
|
// HeuristicPrimeTester primeTester = _primeTester ??= new();
|
||||||
// if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.Exclusive.IsPrimeCpu(divisor, CancellationToken.None))
|
// if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.Exclusive.IsPrimeCpu(divisor, CancellationToken.None))
|
||||||
if (!skipPrimeOrderHeuristic && primeTester.IsPrimeGpu(divisor))
|
// if (!skipPrimeOrderHeuristic && primeTester.IsPrimeGpu(divisor))
|
||||||
// if (!skipPrimeOrderHeuristic && PrimeTester.IsPrimeCpu(divisor, CancellationToken.None))
|
if (!skipPrimeOrderHeuristic && PrimeTester.IsPrime(divisor))
|
||||||
{
|
{
|
||||||
ulong computedOrder = PrimeOrderCalculator.Calculate(
|
ulong computedOrder = PrimeOrderCalculator.Calculate(
|
||||||
divisor,
|
divisor,
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public static class MersennePrimeFactorTester
|
|||||||
// Atomic.Add(ref _isPrimeFactorHits, 1UL);
|
// Atomic.Add(ref _isPrimeFactorHits, 1UL);
|
||||||
// Console.WriteLine($"MersennePrimeFactorTester.IsPrimeFactor hits {Volatile.Read(ref _isPrimeFactorHits)}");
|
// Console.WriteLine($"MersennePrimeFactorTester.IsPrimeFactor hits {Volatile.Read(ref _isPrimeFactorHits)}");
|
||||||
|
|
||||||
if (!PrimeTester.IsPrimeCpu(q, ct))
|
if (!PrimeTester.IsPrime(q))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -247,7 +247,7 @@ public static class MersennePrimeFactorTester
|
|||||||
// Console.WriteLine($"MersennePrimeFactorTester.Factor64 hits {Volatile.Read(ref _factor64Hits)}");
|
// Console.WriteLine($"MersennePrimeFactorTester.Factor64 hits {Volatile.Read(ref _factor64Hits)}");
|
||||||
|
|
||||||
// if (HeuristicPrimeTester.Exclusive.IsPrime(m, ct))
|
// if (HeuristicPrimeTester.Exclusive.IsPrime(m, ct))
|
||||||
if (PrimeTester.IsPrimeCpu(m, ct))
|
if (PrimeTester.IsPrime(m))
|
||||||
{
|
{
|
||||||
dict[m] = 1;
|
dict[m] = 1;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -1079,7 +1079,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
PartialFactorResult result;
|
PartialFactorResult result;
|
||||||
PrimeTester primeTester = PrimeTester.Exclusive;
|
PrimeTester primeTester = PrimeTester.Exclusive;
|
||||||
CancellationToken ct = CancellationToken.None;
|
CancellationToken ct = CancellationToken.None;
|
||||||
HeuristicPrimeTester? tester = _tester ??= new();
|
// HeuristicPrimeTester? tester = _tester ??= new();
|
||||||
|
|
||||||
if (!gpuFactored)
|
if (!gpuFactored)
|
||||||
{
|
{
|
||||||
@@ -1152,7 +1152,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// Console.WriteLine($"Partial factor hits {Volatile.Read(ref _partialFactorHits)}");
|
// Console.WriteLine($"Partial factor hits {Volatile.Read(ref _partialFactorHits)}");
|
||||||
|
|
||||||
// bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None);
|
// bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None);
|
||||||
bool isPrime = tester.IsPrimeGpu(composite);
|
bool isPrime = PrimeTester.IsPrime(composite);
|
||||||
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
|
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
|
||||||
|
|
||||||
if (isPrime)
|
if (isPrime)
|
||||||
@@ -1220,7 +1220,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// Console.WriteLine($"Partial factor pending hits {Volatile.Read(ref _partialFactorPendingHits)}");
|
// Console.WriteLine($"Partial factor pending hits {Volatile.Read(ref _partialFactorPendingHits)}");
|
||||||
|
|
||||||
// bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None);
|
// bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None);
|
||||||
bool isPrime = tester.IsPrimeGpu(composite);
|
bool isPrime = PrimeTester.IsPrime(composite);
|
||||||
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
|
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
|
||||||
|
|
||||||
entry = entry.WithPrimality(isPrime);
|
entry = entry.WithPrimality(isPrime);
|
||||||
@@ -1257,7 +1257,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// Atomic.Add(ref _partialFactorCofactorHits, 1UL);
|
// Atomic.Add(ref _partialFactorCofactorHits, 1UL);
|
||||||
// Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}");
|
// Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}");
|
||||||
|
|
||||||
cofactorIsPrime = tester.IsPrimeGpu(cofactor);
|
cofactorIsPrime = PrimeTester.IsPrime(cofactor);
|
||||||
// cofactorIsPrime = PrimeTester.IsPrimeGpu(cofactor);
|
// cofactorIsPrime = PrimeTester.IsPrimeGpu(cofactor);
|
||||||
// cofactorIsPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(cofactor);
|
// cofactorIsPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(cofactor);
|
||||||
}
|
}
|
||||||
@@ -1782,8 +1782,8 @@ internal static partial class PrimeOrderCalculator
|
|||||||
|
|
||||||
// private static ulong _factorCompletelyHits;
|
// private static ulong _factorCompletelyHits;
|
||||||
// private static ulong _factorCompletelyPollardRhoHits;
|
// private static ulong _factorCompletelyPollardRhoHits;
|
||||||
[ThreadStatic]
|
// [ThreadStatic]
|
||||||
private static HeuristicPrimeTester? _tester;
|
// private static HeuristicPrimeTester? _tester;
|
||||||
|
|
||||||
private static void FactorCompletely(ulong value, Dictionary<ulong, int> counts, bool knownComposite)
|
private static void FactorCompletely(ulong value, Dictionary<ulong, int> counts, bool knownComposite)
|
||||||
{
|
{
|
||||||
@@ -1794,10 +1794,10 @@ internal static partial class PrimeOrderCalculator
|
|||||||
|
|
||||||
// Atomic.Add(ref _factorCompletelyHits, 1UL);
|
// Atomic.Add(ref _factorCompletelyHits, 1UL);
|
||||||
// Console.WriteLine($"Factor completely hits {Volatile.Read(ref _factorCompletelyHits)}");
|
// Console.WriteLine($"Factor completely hits {Volatile.Read(ref _factorCompletelyHits)}");
|
||||||
HeuristicPrimeTester tester = _tester ??= new();
|
// HeuristicPrimeTester tester = _tester ??= new();
|
||||||
|
|
||||||
// bool isPrime = primeTester.HeuristicIsPrimeGpu(value);
|
// bool isPrime = primeTester.HeuristicIsPrimeGpu(value);
|
||||||
bool isPrime = tester.IsPrimeGpu(value);
|
bool isPrime = PrimeTester.IsPrime(value);
|
||||||
// bool isPrime = PrimeTester.IsPrimeGpu(value);
|
// bool isPrime = PrimeTester.IsPrimeGpu(value);
|
||||||
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(value);
|
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(value);
|
||||||
if (!knownComposite && isPrime)
|
if (!knownComposite && isPrime)
|
||||||
@@ -1813,7 +1813,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// Console.WriteLine($"Factor completely after PollardRho hits {Volatile.Read(ref _factorCompletelyPollardRhoHits)}");
|
// Console.WriteLine($"Factor completely after PollardRho hits {Volatile.Read(ref _factorCompletelyPollardRhoHits)}");
|
||||||
|
|
||||||
// isPrime = primeTester.HeuristicIsPrimeGpu(factor);
|
// isPrime = primeTester.HeuristicIsPrimeGpu(factor);
|
||||||
isPrime = tester.IsPrimeGpu(factor);
|
isPrime = PrimeTester.IsPrime(factor);
|
||||||
// isPrime = PrimeTester.IsPrimeGpu(factor);
|
// isPrime = PrimeTester.IsPrimeGpu(factor);
|
||||||
// isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(factor);
|
// isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(factor);
|
||||||
if (isPrime)
|
if (isPrime)
|
||||||
@@ -1850,7 +1850,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// Atomic.Add(ref _partialFactorCofactorHits, 1UL);
|
// Atomic.Add(ref _partialFactorCofactorHits, 1UL);
|
||||||
// Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}");
|
// Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}");
|
||||||
|
|
||||||
isPrime = tester.IsPrimeGpu(quotient);
|
isPrime = PrimeTester.IsPrime(quotient);
|
||||||
// isPrime = PrimeTester.IsPrimeGpu(quotient);
|
// isPrime = PrimeTester.IsPrimeGpu(quotient);
|
||||||
// isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(quotient);
|
// isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(quotient);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -950,7 +950,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
if (value <= ulong.MaxValue)
|
if (value <= ulong.MaxValue)
|
||||||
{
|
{
|
||||||
// return HeuristicPrimeTester.Exclusive.IsPrimeCpu((ulong)value, CancellationToken.None);
|
// return HeuristicPrimeTester.Exclusive.IsPrimeCpu((ulong)value, CancellationToken.None);
|
||||||
return PrimeTester.IsPrimeCpu((ulong)value, CancellationToken.None);
|
return PrimeTester.IsPrime((ulong)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((value & UInt128.One) == UInt128.Zero)
|
if ((value & UInt128.One) == UInt128.Zero)
|
||||||
|
|||||||
+216
-261
@@ -2,6 +2,7 @@ using System.Buffers;
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
@@ -19,7 +20,7 @@ public sealed class PrimeTester
|
|||||||
|
|
||||||
public static PrimeTester Exclusive => _tester ??= new();
|
public static PrimeTester Exclusive => _tester ??= new();
|
||||||
|
|
||||||
public static bool IsPrime(ulong n, CancellationToken ct)
|
public static bool IsPrime(ulong n)
|
||||||
{
|
{
|
||||||
if (n <= 1UL)
|
if (n <= 1UL)
|
||||||
{
|
{
|
||||||
@@ -89,56 +90,70 @@ public sealed class PrimeTester
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsPrimeGpu(ulong n)
|
|
||||||
{
|
|
||||||
return Exclusive.IsPrimeGpu(n, CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool IsPrimeGpu(ulong n, CancellationToken ct)
|
public bool IsPrimeGpu(ulong n)
|
||||||
{
|
{
|
||||||
bool forceCpu = GpuContextPool.ForceCpu;
|
bool forceCpu = GpuContextPool.ForceCpu;
|
||||||
bool belowGpuRange = n < 31UL;
|
bool belowGpuRange = n < 31UL;
|
||||||
|
|
||||||
if (forceCpu || belowGpuRange)
|
if (forceCpu || belowGpuRange)
|
||||||
{
|
{
|
||||||
return IsPrimeCpu(n, ct);
|
return IsPrime(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<ulong> one = stackalloc ulong[1];
|
var limiter = GpuPrimeWorkLimiter.Acquire();
|
||||||
Span<byte> outFlags = stackalloc byte[1];
|
var gpu = PrimeTesterGpuContextPool.Rent(1);
|
||||||
one[0] = n;
|
var accelerator = gpu.Accelerator;
|
||||||
outFlags[0] = 0;
|
var kernel = gpu.Kernel;
|
||||||
|
|
||||||
IsPrimeBatchGpu(one, outFlags);
|
ulong value = n;
|
||||||
|
byte flag = 0;
|
||||||
|
|
||||||
return outFlags[0] != 0;
|
var input = gpu.Input;
|
||||||
|
var output = gpu.Output;
|
||||||
|
var inputView = input.View;
|
||||||
|
var outputView = output.View;
|
||||||
|
|
||||||
|
inputView.CopyFromCPU(ref value, 1);
|
||||||
|
kernel(
|
||||||
|
1,
|
||||||
|
inputView,
|
||||||
|
gpu.DevicePrimesDefault.View,
|
||||||
|
gpu.DevicePrimesLastOne.View,
|
||||||
|
gpu.DevicePrimesLastSeven.View,
|
||||||
|
gpu.DevicePrimesLastThree.View,
|
||||||
|
gpu.DevicePrimesLastNine.View,
|
||||||
|
gpu.DevicePrimesPow2Default.View,
|
||||||
|
gpu.DevicePrimesPow2LastOne.View,
|
||||||
|
gpu.DevicePrimesPow2LastSeven.View,
|
||||||
|
gpu.DevicePrimesPow2LastThree.View,
|
||||||
|
gpu.DevicePrimesPow2LastNine.View,
|
||||||
|
outputView);
|
||||||
|
accelerator.Synchronize();
|
||||||
|
outputView.CopyToCPU(ref flag, 1);
|
||||||
|
|
||||||
|
gpu.Dispose();
|
||||||
|
limiter.Dispose();
|
||||||
|
|
||||||
|
return flag != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsPrimeCpu(ulong n, CancellationToken ct)
|
|
||||||
{
|
|
||||||
// Preserve the legacy entry point for callers that bypass the thread-local caches.
|
|
||||||
return IsPrime(n, ct);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static int GpuBatchSize { get; set; } = 262_144;
|
public static int GpuBatchSize { get; set; } = 262_144;
|
||||||
|
|
||||||
[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)
|
||||||
{
|
{
|
||||||
var limiter = GpuPrimeWorkLimiter.Acquire();
|
var limiter = GpuPrimeWorkLimiter.Acquire();
|
||||||
var gpu = PrimeTesterGpuContextPool.Rent();
|
var gpu = PrimeTesterGpuContextPool.Rent(GpuBatchSize);
|
||||||
var state = gpu.State;
|
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = gpu.Accelerator;
|
||||||
|
var kernel = gpu.Kernel;
|
||||||
int totalLength = values.Length;
|
int totalLength = values.Length;
|
||||||
int batchSize = Math.Max(1, GpuBatchSize);
|
int batchSize = GpuBatchSize;
|
||||||
|
|
||||||
var scratch = state.RentScratch(batchSize, accelerator);
|
var input = gpu.Input;
|
||||||
var input = scratch.Input;
|
var output = gpu.Output;
|
||||||
var output = scratch.Output;
|
var inputView = input.View;
|
||||||
ArrayPool<ulong> pool = ThreadStaticPools.UlongPool;
|
var outputView = output.View;
|
||||||
ulong[] temp = pool.Rent(batchSize);
|
|
||||||
|
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
while (pos < totalLength)
|
while (pos < totalLength)
|
||||||
@@ -146,242 +161,211 @@ public sealed class PrimeTester
|
|||||||
int remaining = totalLength - pos;
|
int remaining = totalLength - pos;
|
||||||
int count = remaining > batchSize ? batchSize : remaining;
|
int count = remaining > batchSize ? batchSize : remaining;
|
||||||
|
|
||||||
values.Slice(pos, count).CopyTo(temp);
|
var valueSlice = values.Slice(pos, count);
|
||||||
input.View.CopyFromCPU(ref temp[0], count);
|
ref ulong valueRef = ref MemoryMarshal.GetReference(valueSlice);
|
||||||
|
inputView.CopyFromCPU(ref valueRef, count);
|
||||||
|
|
||||||
state.Kernel(
|
kernel(
|
||||||
count,
|
count,
|
||||||
input.View,
|
inputView,
|
||||||
state.DevicePrimesDefault.View,
|
gpu.DevicePrimesDefault.View,
|
||||||
state.DevicePrimesLastOne.View,
|
gpu.DevicePrimesLastOne.View,
|
||||||
state.DevicePrimesLastSeven.View,
|
gpu.DevicePrimesLastSeven.View,
|
||||||
state.DevicePrimesLastThree.View,
|
gpu.DevicePrimesLastThree.View,
|
||||||
state.DevicePrimesLastNine.View,
|
gpu.DevicePrimesLastNine.View,
|
||||||
state.DevicePrimesPow2Default.View,
|
gpu.DevicePrimesPow2Default.View,
|
||||||
state.DevicePrimesPow2LastOne.View,
|
gpu.DevicePrimesPow2LastOne.View,
|
||||||
state.DevicePrimesPow2LastSeven.View,
|
gpu.DevicePrimesPow2LastSeven.View,
|
||||||
state.DevicePrimesPow2LastThree.View,
|
gpu.DevicePrimesPow2LastThree.View,
|
||||||
state.DevicePrimesPow2LastNine.View,
|
gpu.DevicePrimesPow2LastNine.View,
|
||||||
output.View);
|
outputView);
|
||||||
accelerator.Synchronize();
|
accelerator.Synchronize();
|
||||||
output.View.CopyToCPU(ref results[pos], count);
|
var resultSlice = results.Slice(pos, count);
|
||||||
|
ref byte resultRef = ref MemoryMarshal.GetReference(resultSlice);
|
||||||
|
outputView.CopyToCPU(ref resultRef, count);
|
||||||
|
|
||||||
pos += count;
|
pos += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
pool.Return(temp, clearArray: false);
|
|
||||||
state.ReturnScratch(scratch);
|
|
||||||
|
|
||||||
gpu.Dispose();
|
gpu.Dispose();
|
||||||
limiter.Dispose();
|
limiter.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static class PrimeTesterGpuContextPool
|
internal static class PrimeTesterGpuContextPool
|
||||||
{
|
{
|
||||||
internal sealed class PooledContext
|
private static readonly ConcurrentQueue<PrimeTesterGpuContextLease> Pool = new();
|
||||||
|
|
||||||
|
internal static PrimeTesterGpuContextLease Rent(int minBufferCapacity = 1)
|
||||||
{
|
{
|
||||||
public Context Context { get; }
|
if (!Pool.TryDequeue(out var lease))
|
||||||
|
|
||||||
public Accelerator Accelerator { get; }
|
|
||||||
|
|
||||||
private KernelState? _kernelState;
|
|
||||||
|
|
||||||
public PooledContext()
|
|
||||||
{
|
{
|
||||||
Context = Context.CreateDefault();
|
lease = new PrimeTesterGpuContextLease(minBufferCapacity);
|
||||||
Accelerator = Context.GetPreferredDevice(false).CreateAccelerator(Context);
|
}
|
||||||
_kernelState = GpuKernelState.GetOrCreate(Accelerator);
|
else
|
||||||
|
{
|
||||||
|
lease.EnsureCapacity(minBufferCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelState KernelState
|
lease.ResetReturnFlag();
|
||||||
{
|
return lease;
|
||||||
get
|
|
||||||
{
|
|
||||||
var state = _kernelState;
|
|
||||||
if (state is null || state.IsDisposed)
|
|
||||||
{
|
|
||||||
state = GpuKernelState.GetOrCreate(Accelerator);
|
|
||||||
_kernelState = state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
internal static void Return(PrimeTesterGpuContextLease lease)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
{
|
||||||
ClearGpuCaches(Accelerator);
|
lease.Accelerator.Synchronize();
|
||||||
_kernelState = null;
|
Pool.Enqueue(lease);
|
||||||
Accelerator.Dispose();
|
|
||||||
Context.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly ConcurrentQueue<PooledContext> Pool = new();
|
|
||||||
|
|
||||||
internal static PrimeTesterGpuContextLease Rent()
|
|
||||||
{
|
|
||||||
if (Pool.TryDequeue(out var ctx))
|
|
||||||
{
|
|
||||||
return new PrimeTesterGpuContextLease(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PrimeTesterGpuContextLease(new PooledContext());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void DisposeAll()
|
internal static void DisposeAll()
|
||||||
{
|
{
|
||||||
while (Pool.TryDequeue(out var ctx))
|
while (Pool.TryDequeue(out var lease))
|
||||||
{
|
{
|
||||||
ctx.Dispose();
|
lease.DisposeResources();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Return(PooledContext ctx)
|
internal static void Clear(Accelerator accelerator)
|
||||||
{
|
{
|
||||||
ctx.Accelerator.Synchronize();
|
if (accelerator is null)
|
||||||
Pool.Enqueue(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal readonly struct PrimeTesterGpuContextLease(PrimeTesterGpuContextPool.PooledContext ctx)
|
|
||||||
{
|
|
||||||
private readonly PooledContext? _ctx = ctx;
|
|
||||||
|
|
||||||
public readonly Accelerator Accelerator => _ctx!.Accelerator;
|
|
||||||
|
|
||||||
|
|
||||||
public readonly KernelState State => _ctx!.KernelState;
|
|
||||||
|
|
||||||
public readonly void Dispose()
|
|
||||||
{
|
|
||||||
Return(_ctx ?? throw new InvalidOperationException("GPU context lease is not initialized."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Per-accelerator GPU state for prime sieve (kernel + uploaded primes).
|
|
||||||
internal sealed class KernelState
|
|
||||||
{
|
|
||||||
public Action<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> Kernel;
|
|
||||||
public Action<Index1D, ArrayView<ulong>, ulong, ArrayView<byte>> HeuristicTrialDivisionKernel;
|
|
||||||
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesDefault;
|
|
||||||
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastOne;
|
|
||||||
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastSeven;
|
|
||||||
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastThree;
|
|
||||||
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastNine;
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2Default;
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastOne;
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastSeven;
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastThree;
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastNine;
|
|
||||||
public bool IsDisposed;
|
|
||||||
private readonly ConcurrentBag<ScratchBuffers> _scratchPool = [];
|
|
||||||
// TODO: Replace this ConcurrentBag with the lock-free ring buffer variant validated in
|
|
||||||
// GpuModularArithmeticBenchmarks so renting scratch buffers stops contending on the bag's internal locks when
|
|
||||||
// thousands of GPU batches execute per second.
|
|
||||||
|
|
||||||
public KernelState(Accelerator accelerator)
|
|
||||||
{
|
|
||||||
IsDisposed = false;
|
|
||||||
// Compile once per accelerator and upload primes once.
|
|
||||||
Kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel);
|
|
||||||
HeuristicTrialDivisionKernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ulong, ArrayView<byte>>(PrimeTesterKernels.HeuristicTrialDivisionKernel);
|
|
||||||
|
|
||||||
var primesDefault = DivisorGenerator.SmallPrimes;
|
|
||||||
DevicePrimesDefault = accelerator.Allocate1D<uint>(primesDefault.Length);
|
|
||||||
DevicePrimesDefault.View.CopyFromCPU(primesDefault);
|
|
||||||
|
|
||||||
var primesLastOne = DivisorGenerator.SmallPrimesLastOne;
|
|
||||||
DevicePrimesLastOne = accelerator.Allocate1D<uint>(primesLastOne.Length);
|
|
||||||
DevicePrimesLastOne.View.CopyFromCPU(primesLastOne);
|
|
||||||
|
|
||||||
var primesLastSeven = DivisorGenerator.SmallPrimesLastSeven;
|
|
||||||
DevicePrimesLastSeven = accelerator.Allocate1D<uint>(primesLastSeven.Length);
|
|
||||||
DevicePrimesLastSeven.View.CopyFromCPU(primesLastSeven);
|
|
||||||
|
|
||||||
var primesLastThree = DivisorGenerator.SmallPrimesLastThree;
|
|
||||||
DevicePrimesLastThree = accelerator.Allocate1D<uint>(primesLastThree.Length);
|
|
||||||
DevicePrimesLastThree.View.CopyFromCPU(primesLastThree);
|
|
||||||
|
|
||||||
var primesLastNine = DivisorGenerator.SmallPrimesLastNine;
|
|
||||||
DevicePrimesLastNine = accelerator.Allocate1D<uint>(primesLastNine.Length);
|
|
||||||
DevicePrimesLastNine.View.CopyFromCPU(primesLastNine);
|
|
||||||
|
|
||||||
var primesPow2Default = DivisorGenerator.SmallPrimesPow2;
|
|
||||||
DevicePrimesPow2Default = accelerator.Allocate1D<ulong>(primesPow2Default.Length);
|
|
||||||
DevicePrimesPow2Default.View.CopyFromCPU(primesPow2Default);
|
|
||||||
|
|
||||||
var primesPow2LastOne = DivisorGenerator.SmallPrimesPow2LastOne;
|
|
||||||
DevicePrimesPow2LastOne = accelerator.Allocate1D<ulong>(primesPow2LastOne.Length);
|
|
||||||
DevicePrimesPow2LastOne.View.CopyFromCPU(primesPow2LastOne);
|
|
||||||
|
|
||||||
var primesPow2LastSeven = DivisorGenerator.SmallPrimesPow2LastSeven;
|
|
||||||
DevicePrimesPow2LastSeven = accelerator.Allocate1D<ulong>(primesPow2LastSeven.Length);
|
|
||||||
DevicePrimesPow2LastSeven.View.CopyFromCPU(primesPow2LastSeven);
|
|
||||||
|
|
||||||
var primesPow2LastThree = DivisorGenerator.SmallPrimesPow2LastThree;
|
|
||||||
DevicePrimesPow2LastThree = accelerator.Allocate1D<ulong>(primesPow2LastThree.Length);
|
|
||||||
DevicePrimesPow2LastThree.View.CopyFromCPU(primesPow2LastThree);
|
|
||||||
|
|
||||||
var primesPow2LastNine = DivisorGenerator.SmallPrimesPow2LastNine;
|
|
||||||
DevicePrimesPow2LastNine = accelerator.Allocate1D<ulong>(primesPow2LastNine.Length);
|
|
||||||
DevicePrimesPow2LastNine.View.CopyFromCPU(primesPow2LastNine);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class ScratchBuffers
|
|
||||||
{
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense> Input { get; private set; }
|
|
||||||
public MemoryBuffer1D<byte, Stride1D.Dense> Output { get; private set; }
|
|
||||||
public int Capacity { get; private set; }
|
|
||||||
|
|
||||||
public ScratchBuffers(Accelerator accel, int capacity)
|
|
||||||
{
|
|
||||||
Capacity = Math.Max(1, capacity);
|
|
||||||
Input = accel.Allocate1D<ulong>(Capacity);
|
|
||||||
Output = accel.Allocate1D<byte>(Capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Output.Dispose();
|
|
||||||
Input.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ScratchBuffers RentScratch(int minCapacity, Accelerator accel)
|
|
||||||
{
|
|
||||||
while (_scratchPool.TryTake(out var sb))
|
|
||||||
{
|
|
||||||
if (sb.Capacity >= minCapacity)
|
|
||||||
{
|
|
||||||
return sb;
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ScratchBuffers(accel, minCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void ReturnScratch(ScratchBuffers scratch)
|
|
||||||
{
|
|
||||||
_scratchPool.Add(scratch);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
if (IsDisposed)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (_scratchPool.TryTake(out var sb))
|
var retained = new List<PrimeTesterGpuContextLease>();
|
||||||
|
while (Pool.TryDequeue(out var lease))
|
||||||
{
|
{
|
||||||
sb.Dispose();
|
if (lease.Accelerator == accelerator)
|
||||||
|
{
|
||||||
|
lease.DisposeResources();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
retained.Add(lease);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int retainedCount = retained.Count;
|
||||||
|
for (int i = 0; i < retainedCount; i++)
|
||||||
|
{
|
||||||
|
Pool.Enqueue(retained[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class PrimeTesterGpuContextLease : IDisposable
|
||||||
|
{
|
||||||
|
private bool _returned;
|
||||||
|
|
||||||
|
internal readonly Context AcceleratorContext;
|
||||||
|
public readonly Accelerator Accelerator;
|
||||||
|
public readonly Action<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> Kernel;
|
||||||
|
public readonly Action<Index1D, ArrayView<ulong>, ulong, ArrayView<int>> HeuristicTrialDivisionKernel;
|
||||||
|
public readonly MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesDefault;
|
||||||
|
public readonly MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastOne;
|
||||||
|
public readonly MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastSeven;
|
||||||
|
public readonly MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastThree;
|
||||||
|
public readonly MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastNine;
|
||||||
|
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2Default;
|
||||||
|
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastOne;
|
||||||
|
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastSeven;
|
||||||
|
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastThree;
|
||||||
|
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastNine;
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense> Input;
|
||||||
|
public MemoryBuffer1D<byte, Stride1D.Dense> Output;
|
||||||
|
public MemoryBuffer1D<int, Stride1D.Dense> HeuristicFlag;
|
||||||
|
public int BufferCapacity;
|
||||||
|
|
||||||
|
internal PrimeTesterGpuContextLease(int minBufferCapacity)
|
||||||
|
{
|
||||||
|
AcceleratorContext = Context.CreateDefault();
|
||||||
|
Accelerator = AcceleratorContext.GetPreferredDevice(false).CreateAccelerator(AcceleratorContext);
|
||||||
|
Kernel = Accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel);
|
||||||
|
HeuristicTrialDivisionKernel = Accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ulong, ArrayView<int>>(PrimeTesterKernels.HeuristicTrialDivisionKernel);
|
||||||
|
|
||||||
|
var primesDefault = DivisorGenerator.SmallPrimes;
|
||||||
|
DevicePrimesDefault = Accelerator.Allocate1D<uint>(primesDefault.Length);
|
||||||
|
DevicePrimesDefault.View.CopyFromCPU(primesDefault);
|
||||||
|
|
||||||
|
var primesLastOne = DivisorGenerator.SmallPrimesLastOne;
|
||||||
|
DevicePrimesLastOne = Accelerator.Allocate1D<uint>(primesLastOne.Length);
|
||||||
|
DevicePrimesLastOne.View.CopyFromCPU(primesLastOne);
|
||||||
|
|
||||||
|
var primesLastSeven = DivisorGenerator.SmallPrimesLastSeven;
|
||||||
|
DevicePrimesLastSeven = Accelerator.Allocate1D<uint>(primesLastSeven.Length);
|
||||||
|
DevicePrimesLastSeven.View.CopyFromCPU(primesLastSeven);
|
||||||
|
|
||||||
|
var primesLastThree = DivisorGenerator.SmallPrimesLastThree;
|
||||||
|
DevicePrimesLastThree = Accelerator.Allocate1D<uint>(primesLastThree.Length);
|
||||||
|
DevicePrimesLastThree.View.CopyFromCPU(primesLastThree);
|
||||||
|
|
||||||
|
var primesLastNine = DivisorGenerator.SmallPrimesLastNine;
|
||||||
|
DevicePrimesLastNine = Accelerator.Allocate1D<uint>(primesLastNine.Length);
|
||||||
|
DevicePrimesLastNine.View.CopyFromCPU(primesLastNine);
|
||||||
|
|
||||||
|
var primesPow2Default = DivisorGenerator.SmallPrimesPow2;
|
||||||
|
DevicePrimesPow2Default = Accelerator.Allocate1D<ulong>(primesPow2Default.Length);
|
||||||
|
DevicePrimesPow2Default.View.CopyFromCPU(primesPow2Default);
|
||||||
|
|
||||||
|
var primesPow2LastOne = DivisorGenerator.SmallPrimesPow2LastOne;
|
||||||
|
DevicePrimesPow2LastOne = Accelerator.Allocate1D<ulong>(primesPow2LastOne.Length);
|
||||||
|
DevicePrimesPow2LastOne.View.CopyFromCPU(primesPow2LastOne);
|
||||||
|
|
||||||
|
var primesPow2LastSeven = DivisorGenerator.SmallPrimesPow2LastSeven;
|
||||||
|
DevicePrimesPow2LastSeven = Accelerator.Allocate1D<ulong>(primesPow2LastSeven.Length);
|
||||||
|
DevicePrimesPow2LastSeven.View.CopyFromCPU(primesPow2LastSeven);
|
||||||
|
|
||||||
|
var primesPow2LastThree = DivisorGenerator.SmallPrimesPow2LastThree;
|
||||||
|
DevicePrimesPow2LastThree = Accelerator.Allocate1D<ulong>(primesPow2LastThree.Length);
|
||||||
|
DevicePrimesPow2LastThree.View.CopyFromCPU(primesPow2LastThree);
|
||||||
|
|
||||||
|
var primesPow2LastNine = DivisorGenerator.SmallPrimesPow2LastNine;
|
||||||
|
DevicePrimesPow2LastNine = Accelerator.Allocate1D<ulong>(primesPow2LastNine.Length);
|
||||||
|
DevicePrimesPow2LastNine.View.CopyFromCPU(primesPow2LastNine);
|
||||||
|
|
||||||
|
BufferCapacity = minBufferCapacity < 1 ? 1 : minBufferCapacity;
|
||||||
|
Input = Accelerator.Allocate1D<ulong>(BufferCapacity);
|
||||||
|
Output = Accelerator.Allocate1D<byte>(BufferCapacity);
|
||||||
|
HeuristicFlag = Accelerator.Allocate1D<int>(BufferCapacity);
|
||||||
|
_returned = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnsureCapacity(int minCapacity)
|
||||||
|
{
|
||||||
|
int required = minCapacity < 1 ? 1 : minCapacity;
|
||||||
|
if (required <= BufferCapacity)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Input.Dispose();
|
||||||
|
Output.Dispose();
|
||||||
|
HeuristicFlag.Dispose();
|
||||||
|
Input = Accelerator.Allocate1D<ulong>(required);
|
||||||
|
Output = Accelerator.Allocate1D<byte>(required);
|
||||||
|
HeuristicFlag = Accelerator.Allocate1D<int>(required);
|
||||||
|
BufferCapacity = required;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ResetReturnFlag()
|
||||||
|
{
|
||||||
|
_returned = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_returned)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_returned = true;
|
||||||
|
PrimeTesterGpuContextPool.Return(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void DisposeResources()
|
||||||
|
{
|
||||||
|
Input.Dispose();
|
||||||
|
Output.Dispose();
|
||||||
|
HeuristicFlag.Dispose();
|
||||||
DevicePrimesDefault.Dispose();
|
DevicePrimesDefault.Dispose();
|
||||||
DevicePrimesLastOne.Dispose();
|
DevicePrimesLastOne.Dispose();
|
||||||
DevicePrimesLastSeven.Dispose();
|
DevicePrimesLastSeven.Dispose();
|
||||||
@@ -392,44 +376,15 @@ public sealed class PrimeTester
|
|||||||
DevicePrimesPow2LastSeven.Dispose();
|
DevicePrimesPow2LastSeven.Dispose();
|
||||||
DevicePrimesPow2LastThree.Dispose();
|
DevicePrimesPow2LastThree.Dispose();
|
||||||
DevicePrimesPow2LastNine.Dispose();
|
DevicePrimesPow2LastNine.Dispose();
|
||||||
IsDisposed = true;
|
Accelerator.Dispose();
|
||||||
}
|
AcceleratorContext.Dispose();
|
||||||
}
|
|
||||||
|
|
||||||
internal static class GpuKernelState
|
|
||||||
{
|
|
||||||
// Map accelerator to cached state; use Lazy to serialize kernel creation
|
|
||||||
private static readonly System.Collections.Concurrent.ConcurrentDictionary<Accelerator, Lazy<KernelState>> States = new();
|
|
||||||
// TODO: Prewarm this per-accelerator cache during startup (and reuse a simple array keyed by accelerator index)
|
|
||||||
// once the kernel pool exposes deterministic ordering; the Lazy wrappers showed measurable overhead in the
|
|
||||||
// GpuModularArithmeticBenchmarks hot path.
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static KernelState GetOrCreate(Accelerator accelerator)
|
|
||||||
{
|
|
||||||
var lazy = States.GetOrAdd(
|
|
||||||
accelerator,
|
|
||||||
acc => new Lazy<KernelState>(() => new KernelState(acc), LazyThreadSafetyMode.ExecutionAndPublication));
|
|
||||||
return lazy.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Clear(Accelerator accelerator)
|
|
||||||
{
|
|
||||||
if (States.TryRemove(accelerator, out var lazy))
|
|
||||||
{
|
|
||||||
if (lazy.IsValueCreated)
|
|
||||||
{
|
|
||||||
var state = lazy.Value;
|
|
||||||
state.Clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Expose cache clearing for accelerator disposal coordination
|
// Expose cache clearing for accelerator disposal coordination
|
||||||
public static void ClearGpuCaches(Accelerator accelerator)
|
public static void ClearGpuCaches(Accelerator accelerator)
|
||||||
{
|
{
|
||||||
GpuKernelState.Clear(accelerator);
|
PrimeTesterGpuContextPool.Clear(accelerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void DisposeGpuContexts()
|
internal static void DisposeGpuContexts()
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ The plan incorporates the updated divisor-class heuristics (Groups A/B, wheels,
|
|||||||
* [done] The CPU heuristic now operates as a pure trial-division sweep without maintaining per-call summaries, relying on `PrepareHeuristicDivisor` only when downstream consumers request Montgomery data.
|
* [done] The CPU heuristic now operates as a pure trial-division sweep without maintaining per-call summaries, relying on `PrepareHeuristicDivisor` only when downstream consumers request Montgomery data.
|
||||||
* [done] Introduced the temporary `UseHeuristicGroupBTrialDivision` gate so current builds execute Group A locally and then fall back to `Open.Numeric.Primes.Prime.Numbers.IsPrime`, keeping the full Group B implementation available for reactivation.
|
* [done] Introduced the temporary `UseHeuristicGroupBTrialDivision` gate so current builds execute Group A locally and then fall back to `Open.Numeric.Primes.Prime.Numbers.IsPrime`, keeping the full Group B implementation available for reactivation.
|
||||||
7. [done] **Implement `IsPrimeGpu`**
|
7. [done] **Implement `IsPrimeGpu`**
|
||||||
* [done] Mirror CPU structure while batching divisor checks on the accelerator via `HeuristicTrialDivisionGpu`, drawing divisors from `HeuristicPrimeSieves`, reusing caller-provided `sqrtLimit`/`nMod10` inputs, and falling back to CPU when GPUs are disabled.
|
* [done] Mirror CPU structure while batching divisor checks on the accelerator via `HeuristicTrialDivisionGpu`, drawing divisors from `HeuristicPrimeSieves`, reusing caller-provided `maxDivisorSquare`/`nMod10` inputs, and falling back to CPU when GPUs are disabled.
|
||||||
* [done] Added `PrimeTesterKernels.HeuristicTrialDivisionKernel` so GPU batches respect Group A/B ordering while emitting hit flags for early exits.
|
* [done] Added `PrimeTesterKernels.HeuristicTrialDivisionKernel` so GPU batches respect Group A/B ordering while emitting hit flags for early exits.
|
||||||
* [done] Kept GPU divisibility in standard modular arithmetic while reserving Montgomery transforms for CPU confirmation logic.
|
* [done] Kept GPU divisibility in standard modular arithmetic while reserving Montgomery transforms for CPU confirmation logic.
|
||||||
* [done] When the temporary Group B gate is disabled, delegate GPU calls to the CPU fallback so Group A coverage remains in place while the accelerator path stays available for future re-enablement.
|
* [done] When the temporary Group B gate is disabled, delegate GPU calls to the CPU fallback so Group A coverage remains in place while the accelerator path stays available for future re-enablement.
|
||||||
|
|||||||
Reference in new issue
Block a user