CPU vs GPU work delegation adjusted to better utilize CPU.
This commit is contained in:
1 parent
c311b27dcc
commit
6a1198c417
12 files changed
+575
-343
No files matched your search
@@ -136,7 +136,7 @@ public class PrimeTesterBenchmarks
|
|||||||
|
|
||||||
for (int i = 0; i < values.Length; i++)
|
for (int i = 0; i < values.Length; i++)
|
||||||
{
|
{
|
||||||
if (PrimeTester.IsPrime(values[i]))
|
if (PrimeTester.IsPrimeCpu(values[i]))
|
||||||
{
|
{
|
||||||
primeCount++;
|
primeCount++;
|
||||||
}
|
}
|
||||||
@@ -153,7 +153,24 @@ public class PrimeTesterBenchmarks
|
|||||||
|
|
||||||
for (int i = 0; i < values.Length; i++)
|
for (int i = 0; i < values.Length; i++)
|
||||||
{
|
{
|
||||||
if (PrimeTester.IsPrime(values[i]))
|
if (PrimeTesterByLastDigit.IsPrimeCpu(values[i]))
|
||||||
|
{
|
||||||
|
primeCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return primeCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public int SevenOrOthersCpu()
|
||||||
|
{
|
||||||
|
ulong[] values = _candidates;
|
||||||
|
int primeCount = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
if (PrimeTesterSeverOrOthers.IsPrimeCpu(values[i]))
|
||||||
{
|
{
|
||||||
primeCount++;
|
primeCount++;
|
||||||
}
|
}
|
||||||
@@ -195,12 +212,54 @@ public class PrimeTesterBenchmarks
|
|||||||
4_000_000UL,
|
4_000_000UL,
|
||||||
4_096);
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥16_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 16,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
16_000_001UL,
|
||||||
|
16_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥32_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 32,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
32_000_001UL,
|
||||||
|
32_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥64_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 64,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
64_000_001UL,
|
||||||
|
64_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥100_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 100,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
100_000_001UL,
|
||||||
|
100_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
yield return PrimeBenchmarkCase.FromRange(
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
"≥138_000_000",
|
"≥138_000_000",
|
||||||
"Sampled admissible odd values ≥ 138,000,000 (k ≤ 5, not divisible by 5)",
|
"Sampled admissible odd values ≥ 138,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
138_000_001UL,
|
138_000_001UL,
|
||||||
138_500_000UL,
|
138_500_000UL,
|
||||||
4_096);
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥276_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 276,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
276_000_001UL,
|
||||||
|
276_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥552_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 552,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
552_000_001UL,
|
||||||
|
552_500_000UL,
|
||||||
|
4_096);
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly record struct PrimeBenchmarkCase(string Name, string Description, ulong[] Candidates)
|
public readonly record struct PrimeBenchmarkCase(string Name, string Description, ulong[] Candidates)
|
||||||
|
|||||||
@@ -52,7 +52,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).Should().Be(expected);
|
PrimeTester.IsPrimeCpu(n).Should().Be(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -631,7 +631,7 @@ internal static class Program
|
|||||||
if (!_cliArguments.UseByDivisor)
|
if (!_cliArguments.UseByDivisor)
|
||||||
{
|
{
|
||||||
if (!(_runPrimesOnCpu
|
if (!(_runPrimesOnCpu
|
||||||
? PrimeTester.IsPrime(p)
|
? PrimeTester.IsPrimeCpu(p)
|
||||||
: HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, p)))
|
: HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, p)))
|
||||||
{
|
{
|
||||||
_lastCompositeP = true;
|
_lastCompositeP = true;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class PrimeTesterIsPrimeTests
|
|||||||
{
|
{
|
||||||
var tester = new PrimeTester();
|
var tester = new PrimeTester();
|
||||||
|
|
||||||
PrimeTester.IsPrime(n).Should().Be(expected);
|
PrimeTester.IsPrimeCpu(n).Should().Be(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
@@ -56,8 +56,8 @@ public class PrimeTesterIsPrimeTests
|
|||||||
public void IsPrimeGpu_falls_back_to_cpu_when_forced()
|
public void IsPrimeGpu_falls_back_to_cpu_when_forced()
|
||||||
{
|
{
|
||||||
var tester = new PrimeTester();
|
var tester = new PrimeTester();
|
||||||
PrimeTester.IsPrime(137UL).Should().BeTrue();
|
PrimeTester.IsPrimeCpu(137UL).Should().BeTrue();
|
||||||
PrimeTester.IsPrime(341UL).Should().BeFalse();
|
PrimeTester.IsPrimeCpu(341UL).Should().BeFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -415,7 +415,7 @@ public class MersenneDivisorCycles
|
|||||||
// 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 (HeuristicCombinedPrimeTester.IsPrimeCpu(remaining))
|
if (PrimeTesterByLastDigit.IsPrimeCpu(remaining))
|
||||||
// if (HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, remaining))
|
// if (HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, remaining))
|
||||||
// if (PrimeTester.IsPrime(remaining))
|
// if (PrimeTester.IsPrime(remaining))
|
||||||
{
|
{
|
||||||
@@ -966,7 +966,7 @@ public class MersenneDivisorCycles
|
|||||||
|
|
||||||
// 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 && HeuristicCombinedPrimeTester.IsPrimeCpu(divisor))
|
if (!skipPrimeOrderHeuristic && PrimeTesterByLastDigit.IsPrimeCpu(divisor))
|
||||||
// if (!skipPrimeOrderHeuristic && HeuristicCombinedPrimeTester.IsPrimeGpu(divisor))
|
// if (!skipPrimeOrderHeuristic && HeuristicCombinedPrimeTester.IsPrimeGpu(divisor))
|
||||||
{
|
{
|
||||||
ulong computedOrder = PrimeOrderCalculator.Calculate(
|
ulong computedOrder = PrimeOrderCalculator.Calculate(
|
||||||
|
|||||||
@@ -35,7 +35,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.IsPrime(q))
|
if (!PrimeTester.IsPrimeCpu(q))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -256,7 +256,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.IsPrime(m))
|
if (PrimeTester.IsPrimeCpu(m))
|
||||||
{
|
{
|
||||||
dict[m] = 1;
|
dict[m] = 1;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ public static class PerfectNumberConstants
|
|||||||
public const int DefaultSmallPrimeFactorSlotCount = 64;
|
public const int DefaultSmallPrimeFactorSlotCount = 64;
|
||||||
public const int DefaultSpecialMaxFactorCapacity = 1024;
|
public const int DefaultSpecialMaxFactorCapacity = 1024;
|
||||||
public const ulong ExtraDivisorCycleSearchLimit = 64UL;
|
public const ulong ExtraDivisorCycleSearchLimit = 64UL;
|
||||||
public const int GpuFrequency = 192 - 1;
|
public const int GpuFrequency = 2048 - 1;
|
||||||
public const int MaxQForDivisorCycles = 4_000_000;
|
public const int MaxQForDivisorCycles = 4_000_000;
|
||||||
public static readonly uint PrimesLimit = 1_000_000; //(ulong)Array.MaxLength;// 1_000_000;
|
public static readonly uint PrimesLimit = 1_000_000; //(ulong)Array.MaxLength;// 1_000_000;
|
||||||
public const int MaxOddPowersCount = 128;
|
public const int MaxOddPowersCount = 128;
|
||||||
|
|||||||
@@ -68,8 +68,11 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// result = prime <= PerfectNumberConstants.MaxQForDivisorCycles
|
// result = prime <= PerfectNumberConstants.MaxQForDivisorCycles
|
||||||
// ? CalculateByFactorizationGpu(gpu, prime, divisorData)
|
// ? CalculateByFactorizationGpu(gpu, prime, divisorData)
|
||||||
// : CalculateByFactorizationCpu(gpu, prime, divisorData);
|
// : CalculateByFactorizationCpu(gpu, prime, divisorData);
|
||||||
result = CalculateByFactorizationGpu(gpu, prime, divisorData);
|
// result = RunOnCpu()
|
||||||
// result = CalculateByFactorizationCpu(gpu, prime, divisorData);
|
// ? CalculateByFactorizationCpu(gpu, prime, divisorData)
|
||||||
|
// : CalculateByFactorizationGpu(gpu, prime, divisorData);
|
||||||
|
// result = CalculateByFactorizationGpu(gpu, prime, divisorData);
|
||||||
|
result = CalculateByFactorizationCpu(gpu, prime, divisorData);
|
||||||
|
|
||||||
phiFactors.Dispose();
|
phiFactors.Dispose();
|
||||||
return result;
|
return result;
|
||||||
@@ -105,8 +108,10 @@ internal static partial class PrimeOrderCalculator
|
|||||||
if (config.Mode == PrimeOrderMode.Strict)
|
if (config.Mode == PrimeOrderMode.Strict)
|
||||||
{
|
{
|
||||||
orderFactors?.Dispose();
|
orderFactors?.Dispose();
|
||||||
// return CalculateByFactorizationCpu(gpu, prime, divisorData);
|
return CalculateByFactorizationCpu(gpu, prime, divisorData);
|
||||||
return CalculateByFactorizationGpu(gpu, prime, divisorData);
|
// return RunOnCpu()
|
||||||
|
// ? CalculateByFactorizationCpu(gpu, prime, divisorData)
|
||||||
|
// : CalculateByFactorizationGpu(gpu, prime, divisorData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TryHeuristicFinishCpu(gpu, prime, candidateOrder, previousOrder, divisorData, config, phiFactors, orderFactors, out ulong order))
|
if (TryHeuristicFinishCpu(gpu, prime, candidateOrder, previousOrder, divisorData, config, phiFactors, orderFactors, out ulong order))
|
||||||
@@ -132,7 +137,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
|
|
||||||
Span<ulong> stackBuffer = stackalloc ulong[length];
|
Span<ulong> stackBuffer = stackalloc ulong[length];
|
||||||
|
|
||||||
if (RunOnCpu())
|
if (length <= 8)
|
||||||
{
|
{
|
||||||
return EvaluateSpecialMaxCandidatesCpu(stackBuffer, factorSpan, phi, prime, divisorData);
|
return EvaluateSpecialMaxCandidatesCpu(stackBuffer, factorSpan, phi, prime, divisorData);
|
||||||
}
|
}
|
||||||
@@ -1175,7 +1180,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
{
|
{
|
||||||
// bool isPrime = HeuristicCombinedPrimeTester.IsPrimeCpu(composite);
|
// bool isPrime = HeuristicCombinedPrimeTester.IsPrimeCpu(composite);
|
||||||
// bool isPrime = PrimeTester.IsPrime(composite);
|
// bool isPrime = PrimeTester.IsPrime(composite);
|
||||||
bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite);
|
bool isPrime = RunOnCpu() ? PrimeTesterByLastDigit.IsPrimeCpu(composite) : HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite);
|
||||||
// bool isPrime = PrimeTester.IsPrimeInternal(composite, CancellationToken.None);
|
// bool isPrime = PrimeTester.IsPrimeInternal(composite, CancellationToken.None);
|
||||||
|
|
||||||
// Atomic.Add(ref _partialFactorPendingHits, 1UL);
|
// Atomic.Add(ref _partialFactorPendingHits, 1UL);
|
||||||
@@ -1218,7 +1223,8 @@ internal static partial class PrimeOrderCalculator
|
|||||||
{
|
{
|
||||||
// cofactorIsPrime = PrimeTester.IsPrime(cofactor);
|
// cofactorIsPrime = PrimeTester.IsPrime(cofactor);
|
||||||
// cofactorIsPrime = HeuristicCombinedPrimeTester.IsPrimeCpu(cofactor);
|
// cofactorIsPrime = HeuristicCombinedPrimeTester.IsPrimeCpu(cofactor);
|
||||||
cofactorIsPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, cofactor);
|
cofactorIsPrime = RunOnCpu() ? PrimeTesterByLastDigit.IsPrimeCpu(cofactor) : HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, cofactor);
|
||||||
|
// cofactorIsPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, cofactor);
|
||||||
// cofactorIsPrime = PrimeTester.IsPrimeInternal(cofactor, CancellationToken.None);
|
// cofactorIsPrime = PrimeTester.IsPrimeInternal(cofactor, CancellationToken.None);
|
||||||
|
|
||||||
// Atomic.Add(ref _partialFactorCofactorHits, 1UL);
|
// Atomic.Add(ref _partialFactorCofactorHits, 1UL);
|
||||||
@@ -1319,7 +1325,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
while (compositeStack.Count > 0)
|
while (compositeStack.Count > 0)
|
||||||
{
|
{
|
||||||
ulong composite = compositeStack.Pop();
|
ulong composite = compositeStack.Pop();
|
||||||
bool isPrime = RunOnCpu() ? PrimeTester.IsPrime(composite) : HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite);
|
bool isPrime = RunOnCpu() ? PrimeTesterByLastDigit.IsPrimeCpu(composite) : HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite);
|
||||||
|
|
||||||
if (isPrime)
|
if (isPrime)
|
||||||
{
|
{
|
||||||
@@ -1354,7 +1360,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
while (compositeStack.Count > 0)
|
while (compositeStack.Count > 0)
|
||||||
{
|
{
|
||||||
ulong composite = compositeStack.Pop();
|
ulong composite = compositeStack.Pop();
|
||||||
bool isPrime = PrimeTester.IsPrime(composite);
|
bool isPrime = PrimeTesterByLastDigit.IsPrimeCpu(composite);
|
||||||
|
|
||||||
if (isPrime)
|
if (isPrime)
|
||||||
{
|
{
|
||||||
@@ -1644,7 +1650,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
x = AdvancePolynomialCpu(x, c, n);
|
x = AdvancePolynomialCpu(x, c, n);
|
||||||
y = AdvancePolynomialTwiceCpu(y, c, n);
|
y = AdvancePolynomialTwiceCpu(y, c, n);
|
||||||
ulong diff = x > y ? x - y : y - x;
|
ulong diff = x > y ? x - y : y - x;
|
||||||
d = BinaryGcd(diff, n);
|
d = diff.BinaryGcd(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d != n)
|
if (d != n)
|
||||||
@@ -1862,7 +1868,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// bool isPrime = primeTester.HeuristicIsPrimeGpu(value);
|
// bool isPrime = primeTester.HeuristicIsPrimeGpu(value);
|
||||||
// bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, value);
|
// bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, value);
|
||||||
// bool isPrime = PrimeTester.IsPrimeGpu(value);
|
// bool isPrime = PrimeTester.IsPrimeGpu(value);
|
||||||
bool isPrime = PrimeTester.IsPrime(value);
|
bool isPrime = PrimeTesterByLastDigit.IsPrimeCpu(value);
|
||||||
// bool isPrime = RunOnGpu()
|
// bool isPrime = RunOnGpu()
|
||||||
// ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, value)
|
// ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, value)
|
||||||
// : PrimeTester.IsPrime(value);
|
// : PrimeTester.IsPrime(value);
|
||||||
@@ -1885,7 +1891,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// isPrime = RunOnGpu()
|
// isPrime = RunOnGpu()
|
||||||
// ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, factor)
|
// ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, factor)
|
||||||
// : PrimeTester.IsPrime(factor);
|
// : PrimeTester.IsPrime(factor);
|
||||||
isPrime = PrimeTester.IsPrime(factor);
|
isPrime = PrimeTesterByLastDigit.IsPrimeCpu(factor);
|
||||||
// isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(factor);
|
// isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(factor);
|
||||||
if (isPrime)
|
if (isPrime)
|
||||||
{
|
{
|
||||||
@@ -1922,7 +1928,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}");
|
// Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}");
|
||||||
|
|
||||||
// isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, quotient);
|
// isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, quotient);
|
||||||
isPrime = PrimeTester.IsPrime(quotient);
|
isPrime = PrimeTesterByLastDigit.IsPrimeCpu(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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -953,7 +953,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.IsPrime((ulong)value);
|
return PrimeTester.IsPrimeCpu((ulong)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((value & UInt128.One) == UInt128.Zero)
|
if ((value & UInt128.One) == UInt128.Zero)
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ using PerfectNumbers.Core.Gpu.Accelerators;
|
|||||||
|
|
||||||
namespace PerfectNumbers.Core;
|
namespace PerfectNumbers.Core;
|
||||||
|
|
||||||
public sealed class PrimeTesterByLastDigit
|
public sealed class PrimeTester
|
||||||
{
|
{
|
||||||
public PrimeTesterByLastDigit()
|
public PrimeTester()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static PrimeTesterByLastDigit? _tester;
|
private static PrimeTester? _tester;
|
||||||
|
|
||||||
public static PrimeTesterByLastDigit Exclusive => _tester ??= new();
|
public static PrimeTester Exclusive => _tester ??= new();
|
||||||
|
|
||||||
public static bool IsPrimeCpu(ulong n)
|
public static bool IsPrimeCpu(ulong n)
|
||||||
{
|
{
|
||||||
@@ -34,249 +34,6 @@ public sealed class PrimeTesterByLastDigit
|
|||||||
|
|
||||||
bool requiresTrialDivision = result && n >= 7UL;
|
bool requiresTrialDivision = result && n >= 7UL;
|
||||||
|
|
||||||
if (requiresTrialDivision)
|
|
||||||
{
|
|
||||||
// EvenPerfectBitScanner streams exponents starting at 136,279,841, so the Mod10/GCD guard never fires on the
|
|
||||||
// production path. Leave the logic commented out as instrumentation for diagnostic builds.
|
|
||||||
// bool sharesMaxExponentFactor = n.Mod10() == 1UL && SharesFactorWithMaxExponent(n);
|
|
||||||
// result &= !sharesMaxExponentFactor;
|
|
||||||
|
|
||||||
if (result)
|
|
||||||
{
|
|
||||||
uint[] smallPrimeDivisors;
|
|
||||||
ulong[] smallPrimeDivisorsMul;
|
|
||||||
|
|
||||||
ulong nMod10 = n.Mod10();
|
|
||||||
switch (nMod10)
|
|
||||||
{
|
|
||||||
case 1UL:
|
|
||||||
smallPrimeDivisors = PrimesGenerator.SmallPrimesLastOne;
|
|
||||||
smallPrimeDivisorsMul = PrimesGenerator.SmallPrimesPow2LastOne;
|
|
||||||
break;
|
|
||||||
case 3UL:
|
|
||||||
smallPrimeDivisors = DivisorGenerator.SmallPrimesLastThree;
|
|
||||||
smallPrimeDivisorsMul = DivisorGenerator.SmallPrimesPow2LastThree;
|
|
||||||
break;
|
|
||||||
case 7UL:
|
|
||||||
smallPrimeDivisors = PrimesGenerator.SmallPrimesLastSeven;
|
|
||||||
smallPrimeDivisorsMul = PrimesGenerator.SmallPrimesPow2LastSeven;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
smallPrimeDivisors = DivisorGenerator.SmallPrimesLastNine;
|
|
||||||
smallPrimeDivisorsMul = DivisorGenerator.SmallPrimesPow2LastNine;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int smallPrimeDivisorsLength = smallPrimeDivisors.Length;
|
|
||||||
for (int i = 0; i < smallPrimeDivisorsLength; i++)
|
|
||||||
{
|
|
||||||
ulong value = smallPrimeDivisorsMul[i];
|
|
||||||
if (value > n)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = n % smallPrimeDivisors[i];
|
|
||||||
if (value == 0)
|
|
||||||
{
|
|
||||||
result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n)
|
|
||||||
{
|
|
||||||
byte flag = 0;
|
|
||||||
var inputView = gpu.InputView;
|
|
||||||
var outputView = gpu.OutputByteView;
|
|
||||||
|
|
||||||
int acceleratorIndex = gpu.AcceleratorIndex;
|
|
||||||
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
|
||||||
inputView.CopyFromCPU(stream, ref n, 1);
|
|
||||||
|
|
||||||
var kernelLauncher = gpu.SmallPrimeSieveKernelLauncher;
|
|
||||||
|
|
||||||
kernelLauncher(
|
|
||||||
stream,
|
|
||||||
1,
|
|
||||||
inputView,
|
|
||||||
gpu.DevicePrimesLastOne,
|
|
||||||
gpu.DevicePrimesLastSeven,
|
|
||||||
gpu.DevicePrimesLastThree,
|
|
||||||
gpu.DevicePrimesLastNine,
|
|
||||||
gpu.DevicePrimesPow2LastOne,
|
|
||||||
gpu.DevicePrimesPow2LastSeven,
|
|
||||||
gpu.DevicePrimesPow2LastThree,
|
|
||||||
gpu.DevicePrimesPow2LastNine,
|
|
||||||
outputView);
|
|
||||||
|
|
||||||
outputView.CopyToCPU(stream, ref flag, 1);
|
|
||||||
stream.Synchronize();
|
|
||||||
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
|
||||||
|
|
||||||
return flag != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GpuBatchSize { get; set; } = 262_144;
|
|
||||||
|
|
||||||
private static readonly object GpuWarmUpLock = new();
|
|
||||||
private static int WarmedGpuLeaseCount;
|
|
||||||
|
|
||||||
public static void WarmUpGpuKernels(int threadCount)
|
|
||||||
{
|
|
||||||
int target = threadCount >> 2;
|
|
||||||
if (target == 0)
|
|
||||||
{
|
|
||||||
target = threadCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (GpuWarmUpLock)
|
|
||||||
{
|
|
||||||
if (target <= WarmedGpuLeaseCount)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrimeOrderCalculatorAccelerator.WarmUp();
|
|
||||||
WarmedGpuLeaseCount = target;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void IsPrimeBatchGpu(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<ulong> values, Span<byte> results)
|
|
||||||
{
|
|
||||||
// GpuPrimeWorkLimiter.Acquire();
|
|
||||||
int acceleratorIndex = gpu.AcceleratorIndex;
|
|
||||||
int totalLength = values.Length;
|
|
||||||
int batchSize = GpuBatchSize;
|
|
||||||
|
|
||||||
var inputView = gpu.InputView;
|
|
||||||
var outputView = gpu.OutputByteView;
|
|
||||||
|
|
||||||
int pos = 0;
|
|
||||||
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
|
||||||
var kernelLauncher = gpu.SmallPrimeSieveKernelLauncher;
|
|
||||||
|
|
||||||
while (pos < totalLength)
|
|
||||||
{
|
|
||||||
int remaining = totalLength - pos;
|
|
||||||
int count = remaining > batchSize ? batchSize : remaining;
|
|
||||||
|
|
||||||
var valueSlice = values.Slice(pos, count);
|
|
||||||
inputView.CopyFromCPU(stream, valueSlice);
|
|
||||||
|
|
||||||
kernelLauncher(
|
|
||||||
stream,
|
|
||||||
count,
|
|
||||||
inputView,
|
|
||||||
gpu.DevicePrimesLastOne,
|
|
||||||
gpu.DevicePrimesLastSeven,
|
|
||||||
gpu.DevicePrimesLastThree,
|
|
||||||
gpu.DevicePrimesLastNine,
|
|
||||||
gpu.DevicePrimesPow2LastOne,
|
|
||||||
gpu.DevicePrimesPow2LastSeven,
|
|
||||||
gpu.DevicePrimesPow2LastThree,
|
|
||||||
gpu.DevicePrimesPow2LastNine,
|
|
||||||
outputView);
|
|
||||||
|
|
||||||
var resultSlice = results.Slice(pos, count);
|
|
||||||
outputView.CopyToCPU(stream, resultSlice);
|
|
||||||
|
|
||||||
pos += count;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.Synchronize();
|
|
||||||
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
|
||||||
// GpuPrimeWorkLimiter.Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expose cache clearing for accelerator disposal coordination
|
|
||||||
public static void ClearGpuCaches()
|
|
||||||
{
|
|
||||||
PrimeOrderCalculatorAccelerator.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void DisposeGpuContexts()
|
|
||||||
{
|
|
||||||
PrimeOrderCalculatorAccelerator.DisposeAll();
|
|
||||||
lock (GpuWarmUpLock)
|
|
||||||
{
|
|
||||||
WarmedGpuLeaseCount = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
internal static bool SharesFactorWithMaxExponent(ulong n)
|
|
||||||
{
|
|
||||||
// TODO: Replace this on-the-fly GCD probe with the cached factor table derived from
|
|
||||||
// ResidueComputationBenchmarks so divisor-cycle metadata can short-circuit the test
|
|
||||||
// instead of recomputing binary GCD for every candidate.
|
|
||||||
ulong m = (ulong)BitOperations.Log2(n);
|
|
||||||
return n.BinaryGcd(m) != 1UL;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void SharesFactorWithMaxExponentBatch(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<ulong> values, Span<byte> results)
|
|
||||||
{
|
|
||||||
// TODO: Route this batch helper through the shared GPU kernel pool from
|
|
||||||
// GpuUInt128BinaryGcdBenchmarks so we reuse cached kernels, pinned host buffers,
|
|
||||||
// and divisor-cycle staging instead of allocating new device buffers per call.
|
|
||||||
// Check in benchmarks, which implementation was the fastest, is compatible with GPU,
|
|
||||||
// and implement it.
|
|
||||||
|
|
||||||
int length = values.Length;
|
|
||||||
|
|
||||||
int acceleratorIndex = gpu.AcceleratorIndex;
|
|
||||||
gpu.EnsureCapacity(0, length);
|
|
||||||
var inputBufferView = gpu.InputView;
|
|
||||||
var resultBufferView = gpu.OutputByteView;
|
|
||||||
var kernelLauncher = gpu.SharesFactorKernelLauncher;
|
|
||||||
|
|
||||||
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
|
||||||
inputBufferView.CopyFromCPU(stream, values);
|
|
||||||
|
|
||||||
kernelLauncher(stream, length, inputBufferView, resultBufferView);
|
|
||||||
|
|
||||||
resultBufferView.CopyToCPU(stream, in results);
|
|
||||||
stream.Synchronize();
|
|
||||||
|
|
||||||
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class PrimeTester
|
|
||||||
{
|
|
||||||
public PrimeTester()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[ThreadStatic]
|
|
||||||
private static PrimeTester? _tester;
|
|
||||||
|
|
||||||
public static PrimeTester Exclusive => _tester ??= new();
|
|
||||||
|
|
||||||
public static bool IsPrime(ulong n)
|
|
||||||
{
|
|
||||||
if (n <= 1UL)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n == 2UL)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("PrimeTester.IsPrime encountered the sentinel input 2.");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isOdd = (n & 1UL) != 0UL;
|
|
||||||
bool result = isOdd;
|
|
||||||
|
|
||||||
bool requiresTrialDivision = result && n >= 7UL;
|
|
||||||
|
|
||||||
if (requiresTrialDivision)
|
if (requiresTrialDivision)
|
||||||
{
|
{
|
||||||
// EvenPerfectBitScanner streams exponents starting at 136,279,841, so the Mod10/GCD guard never fires on the
|
// EvenPerfectBitScanner streams exponents starting at 136,279,841, so the Mod10/GCD guard never fires on the
|
||||||
@@ -308,76 +65,6 @@ public sealed class PrimeTester
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsPrimeLastDigit(ulong n)
|
|
||||||
{
|
|
||||||
if (n <= 1UL)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n == 2UL)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("PrimeTester.IsPrime encountered the sentinel input 2.");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isOdd = (n & 1UL) != 0UL;
|
|
||||||
bool result = isOdd;
|
|
||||||
|
|
||||||
bool requiresTrialDivision = result && n >= 7UL;
|
|
||||||
|
|
||||||
if (requiresTrialDivision)
|
|
||||||
{
|
|
||||||
// EvenPerfectBitScanner streams exponents starting at 136,279,841, so the Mod10/GCD guard never fires on the
|
|
||||||
// production path. Leave the logic commented out as instrumentation for diagnostic builds.
|
|
||||||
// bool sharesMaxExponentFactor = n.Mod10() == 1UL && SharesFactorWithMaxExponent(n);
|
|
||||||
// result &= !sharesMaxExponentFactor;
|
|
||||||
|
|
||||||
if (result)
|
|
||||||
{
|
|
||||||
uint[] smallPrimeDivisors = PrimesGenerator.SmallPrimes;
|
|
||||||
ulong[] smallPrimeDivisorsMul = PrimesGenerator.SmallPrimesPow2;
|
|
||||||
|
|
||||||
ulong nMod10 = n.Mod10();
|
|
||||||
switch (nMod10)
|
|
||||||
{
|
|
||||||
case 1UL:
|
|
||||||
smallPrimeDivisors = PrimesGenerator.SmallPrimesLastOne;
|
|
||||||
smallPrimeDivisorsMul = PrimesGenerator.SmallPrimesPow2LastOne;
|
|
||||||
break;
|
|
||||||
case 3UL:
|
|
||||||
smallPrimeDivisors = DivisorGenerator.SmallPrimesLastThree;
|
|
||||||
smallPrimeDivisorsMul = DivisorGenerator.SmallPrimesPow2LastThree;
|
|
||||||
break;
|
|
||||||
case 7UL:
|
|
||||||
smallPrimeDivisors = PrimesGenerator.SmallPrimesLastSeven;
|
|
||||||
smallPrimeDivisorsMul = PrimesGenerator.SmallPrimesPow2LastSeven;
|
|
||||||
break;
|
|
||||||
case 9UL:
|
|
||||||
smallPrimeDivisors = DivisorGenerator.SmallPrimesLastNine;
|
|
||||||
smallPrimeDivisorsMul = DivisorGenerator.SmallPrimesPow2LastNine;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int smallPrimeDivisorsLength = smallPrimeDivisors.Length;
|
|
||||||
for (int i = 0; i < smallPrimeDivisorsLength; i++)
|
|
||||||
{
|
|
||||||
if (smallPrimeDivisorsMul[i] > n)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n % smallPrimeDivisors[i] == 0)
|
|
||||||
{
|
|
||||||
result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n)
|
public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,239 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core;
|
||||||
|
|
||||||
|
public sealed class PrimeTesterByLastDigit
|
||||||
|
{
|
||||||
|
public PrimeTesterByLastDigit()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static PrimeTesterByLastDigit? _tester;
|
||||||
|
|
||||||
|
public static PrimeTesterByLastDigit Exclusive => _tester ??= new();
|
||||||
|
|
||||||
|
public static bool IsPrimeCpu(ulong n)
|
||||||
|
{
|
||||||
|
// The below IFs never trigger in production code of EvenPerfectBitScanner on --mersenne=bydivisor path.
|
||||||
|
// if (n <= 1UL)
|
||||||
|
// {
|
||||||
|
// throw new InvalidOperationException("PrimeTester.IsPrime encountered the sentinel input 2.");
|
||||||
|
// // return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (n == 2UL)
|
||||||
|
// {
|
||||||
|
// throw new InvalidOperationException("PrimeTester.IsPrime encountered the sentinel input 2.");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// EvenPerfectBitScanner streams exponents starting at 136,279,841, so the Mod10/GCD guard never fires on the
|
||||||
|
// production path. Leave the logic commented out as instrumentation for diagnostic builds.
|
||||||
|
// bool sharesMaxExponentFactor = n.Mod10() == 1UL && SharesFactorWithMaxExponent(n);
|
||||||
|
// result &= !sharesMaxExponentFactor;
|
||||||
|
|
||||||
|
uint[] smallPrimeDivisors;
|
||||||
|
ulong[] smallPrimeDivisorsMul;
|
||||||
|
|
||||||
|
ulong nMod10 = n.Mod10();
|
||||||
|
switch (nMod10)
|
||||||
|
{
|
||||||
|
case 1UL:
|
||||||
|
smallPrimeDivisors = PrimesGenerator.SmallPrimesLastOne;
|
||||||
|
smallPrimeDivisorsMul = PrimesGenerator.SmallPrimesPow2LastOne;
|
||||||
|
break;
|
||||||
|
case 3UL:
|
||||||
|
smallPrimeDivisors = DivisorGenerator.SmallPrimesLastThree;
|
||||||
|
smallPrimeDivisorsMul = DivisorGenerator.SmallPrimesPow2LastThree;
|
||||||
|
break;
|
||||||
|
case 7UL:
|
||||||
|
smallPrimeDivisors = PrimesGenerator.SmallPrimesLastSeven;
|
||||||
|
smallPrimeDivisorsMul = PrimesGenerator.SmallPrimesPow2LastSeven;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
smallPrimeDivisors = DivisorGenerator.SmallPrimesLastNine;
|
||||||
|
smallPrimeDivisorsMul = DivisorGenerator.SmallPrimesPow2LastNine;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int smallPrimeDivisorsLength = smallPrimeDivisors.Length;
|
||||||
|
for (int i = 0; i < smallPrimeDivisorsLength; i++)
|
||||||
|
{
|
||||||
|
ulong value = smallPrimeDivisorsMul[i];
|
||||||
|
if (value > n)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = n % smallPrimeDivisors[i];
|
||||||
|
if (value == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n)
|
||||||
|
{
|
||||||
|
byte flag = 0;
|
||||||
|
var inputView = gpu.InputView;
|
||||||
|
var outputView = gpu.OutputByteView;
|
||||||
|
|
||||||
|
int acceleratorIndex = gpu.AcceleratorIndex;
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
|
inputView.CopyFromCPU(stream, ref n, 1);
|
||||||
|
|
||||||
|
var kernelLauncher = gpu.SmallPrimeSieveKernelLauncher;
|
||||||
|
|
||||||
|
kernelLauncher(
|
||||||
|
stream,
|
||||||
|
1,
|
||||||
|
inputView,
|
||||||
|
gpu.DevicePrimesLastOne,
|
||||||
|
gpu.DevicePrimesLastSeven,
|
||||||
|
gpu.DevicePrimesLastThree,
|
||||||
|
gpu.DevicePrimesLastNine,
|
||||||
|
gpu.DevicePrimesPow2LastOne,
|
||||||
|
gpu.DevicePrimesPow2LastSeven,
|
||||||
|
gpu.DevicePrimesPow2LastThree,
|
||||||
|
gpu.DevicePrimesPow2LastNine,
|
||||||
|
outputView);
|
||||||
|
|
||||||
|
outputView.CopyToCPU(stream, ref flag, 1);
|
||||||
|
stream.Synchronize();
|
||||||
|
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||||
|
|
||||||
|
return flag != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GpuBatchSize { get; set; } = 262_144;
|
||||||
|
|
||||||
|
private static readonly object GpuWarmUpLock = new();
|
||||||
|
private static int WarmedGpuLeaseCount;
|
||||||
|
|
||||||
|
public static void WarmUpGpuKernels(int threadCount)
|
||||||
|
{
|
||||||
|
int target = threadCount >> 2;
|
||||||
|
if (target == 0)
|
||||||
|
{
|
||||||
|
target = threadCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (GpuWarmUpLock)
|
||||||
|
{
|
||||||
|
if (target <= WarmedGpuLeaseCount)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrimeOrderCalculatorAccelerator.WarmUp();
|
||||||
|
WarmedGpuLeaseCount = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void IsPrimeBatchGpu(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<ulong> values, Span<byte> results)
|
||||||
|
{
|
||||||
|
// GpuPrimeWorkLimiter.Acquire();
|
||||||
|
int acceleratorIndex = gpu.AcceleratorIndex;
|
||||||
|
int totalLength = values.Length;
|
||||||
|
int batchSize = GpuBatchSize;
|
||||||
|
|
||||||
|
var inputView = gpu.InputView;
|
||||||
|
var outputView = gpu.OutputByteView;
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
|
var kernelLauncher = gpu.SmallPrimeSieveKernelLauncher;
|
||||||
|
|
||||||
|
while (pos < totalLength)
|
||||||
|
{
|
||||||
|
int remaining = totalLength - pos;
|
||||||
|
int count = remaining > batchSize ? batchSize : remaining;
|
||||||
|
|
||||||
|
var valueSlice = values.Slice(pos, count);
|
||||||
|
inputView.CopyFromCPU(stream, valueSlice);
|
||||||
|
|
||||||
|
kernelLauncher(
|
||||||
|
stream,
|
||||||
|
count,
|
||||||
|
inputView,
|
||||||
|
gpu.DevicePrimesLastOne,
|
||||||
|
gpu.DevicePrimesLastSeven,
|
||||||
|
gpu.DevicePrimesLastThree,
|
||||||
|
gpu.DevicePrimesLastNine,
|
||||||
|
gpu.DevicePrimesPow2LastOne,
|
||||||
|
gpu.DevicePrimesPow2LastSeven,
|
||||||
|
gpu.DevicePrimesPow2LastThree,
|
||||||
|
gpu.DevicePrimesPow2LastNine,
|
||||||
|
outputView);
|
||||||
|
|
||||||
|
var resultSlice = results.Slice(pos, count);
|
||||||
|
outputView.CopyToCPU(stream, resultSlice);
|
||||||
|
|
||||||
|
pos += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.Synchronize();
|
||||||
|
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||||
|
// GpuPrimeWorkLimiter.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expose cache clearing for accelerator disposal coordination
|
||||||
|
public static void ClearGpuCaches()
|
||||||
|
{
|
||||||
|
PrimeOrderCalculatorAccelerator.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void DisposeGpuContexts()
|
||||||
|
{
|
||||||
|
PrimeOrderCalculatorAccelerator.DisposeAll();
|
||||||
|
lock (GpuWarmUpLock)
|
||||||
|
{
|
||||||
|
WarmedGpuLeaseCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static bool SharesFactorWithMaxExponent(ulong n)
|
||||||
|
{
|
||||||
|
// TODO: Replace this on-the-fly GCD probe with the cached factor table derived from
|
||||||
|
// ResidueComputationBenchmarks so divisor-cycle metadata can short-circuit the test
|
||||||
|
// instead of recomputing binary GCD for every candidate.
|
||||||
|
ulong m = (ulong)BitOperations.Log2(n);
|
||||||
|
return n.BinaryGcd(m) != 1UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void SharesFactorWithMaxExponentBatch(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<ulong> values, Span<byte> results)
|
||||||
|
{
|
||||||
|
// TODO: Route this batch helper through the shared GPU kernel pool from
|
||||||
|
// GpuUInt128BinaryGcdBenchmarks so we reuse cached kernels, pinned host buffers,
|
||||||
|
// and divisor-cycle staging instead of allocating new device buffers per call.
|
||||||
|
// Check in benchmarks, which implementation was the fastest, is compatible with GPU,
|
||||||
|
// and implement it.
|
||||||
|
|
||||||
|
int length = values.Length;
|
||||||
|
|
||||||
|
int acceleratorIndex = gpu.AcceleratorIndex;
|
||||||
|
gpu.EnsureCapacity(0, length);
|
||||||
|
var inputBufferView = gpu.InputView;
|
||||||
|
var resultBufferView = gpu.OutputByteView;
|
||||||
|
var kernelLauncher = gpu.SharesFactorKernelLauncher;
|
||||||
|
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
|
inputBufferView.CopyFromCPU(stream, values);
|
||||||
|
|
||||||
|
kernelLauncher(stream, length, inputBufferView, resultBufferView);
|
||||||
|
|
||||||
|
resultBufferView.CopyToCPU(stream, in results);
|
||||||
|
stream.Synchronize();
|
||||||
|
|
||||||
|
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,241 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core;
|
||||||
|
|
||||||
|
public sealed class PrimeTesterSeverOrOthers
|
||||||
|
{
|
||||||
|
public PrimeTesterSeverOrOthers()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static PrimeTesterSeverOrOthers? _tester;
|
||||||
|
|
||||||
|
public static PrimeTesterSeverOrOthers Exclusive => _tester ??= new();
|
||||||
|
|
||||||
|
public static bool IsPrimeCpu(ulong n)
|
||||||
|
{
|
||||||
|
if (n <= 1UL)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("PrimeTester.IsPrime encountered the sentinel input 2.");
|
||||||
|
// return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n == 2UL)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("PrimeTester.IsPrime encountered the sentinel input 2.");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isOdd = (n & 1UL) != 0UL;
|
||||||
|
bool result = isOdd;
|
||||||
|
|
||||||
|
bool requiresTrialDivision = result && n >= 7UL;
|
||||||
|
|
||||||
|
if (requiresTrialDivision)
|
||||||
|
{
|
||||||
|
// EvenPerfectBitScanner streams exponents starting at 136,279,841, so the Mod10/GCD guard never fires on the
|
||||||
|
// production path. Leave the logic commented out as instrumentation for diagnostic builds.
|
||||||
|
// bool sharesMaxExponentFactor = n.Mod10() == 1UL && SharesFactorWithMaxExponent(n);
|
||||||
|
// result &= !sharesMaxExponentFactor;
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
uint[] smallPrimeDivisors;
|
||||||
|
ulong[] smallPrimeDivisorsMul;
|
||||||
|
|
||||||
|
ulong nMod10 = n.Mod10();
|
||||||
|
if (nMod10 == 7)
|
||||||
|
{
|
||||||
|
smallPrimeDivisors = PrimesGenerator.SmallPrimesLastSeven;
|
||||||
|
smallPrimeDivisorsMul = PrimesGenerator.SmallPrimesPow2LastSeven;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
smallPrimeDivisors = PrimesGenerator.SmallPrimesLastOne;
|
||||||
|
smallPrimeDivisorsMul = PrimesGenerator.SmallPrimesPow2LastOne;
|
||||||
|
}
|
||||||
|
|
||||||
|
int smallPrimeDivisorsLength = smallPrimeDivisors.Length;
|
||||||
|
for (int i = 0; i < smallPrimeDivisorsLength; i++)
|
||||||
|
{
|
||||||
|
ulong value = smallPrimeDivisorsMul[i];
|
||||||
|
if (value > n)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = n % smallPrimeDivisors[i];
|
||||||
|
if (value == 0)
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n)
|
||||||
|
{
|
||||||
|
byte flag = 0;
|
||||||
|
var inputView = gpu.InputView;
|
||||||
|
var outputView = gpu.OutputByteView;
|
||||||
|
|
||||||
|
int acceleratorIndex = gpu.AcceleratorIndex;
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
|
inputView.CopyFromCPU(stream, ref n, 1);
|
||||||
|
|
||||||
|
var kernelLauncher = gpu.SmallPrimeSieveKernelLauncher;
|
||||||
|
|
||||||
|
kernelLauncher(
|
||||||
|
stream,
|
||||||
|
1,
|
||||||
|
inputView,
|
||||||
|
gpu.DevicePrimesLastOne,
|
||||||
|
gpu.DevicePrimesLastSeven,
|
||||||
|
gpu.DevicePrimesLastThree,
|
||||||
|
gpu.DevicePrimesLastNine,
|
||||||
|
gpu.DevicePrimesPow2LastOne,
|
||||||
|
gpu.DevicePrimesPow2LastSeven,
|
||||||
|
gpu.DevicePrimesPow2LastThree,
|
||||||
|
gpu.DevicePrimesPow2LastNine,
|
||||||
|
outputView);
|
||||||
|
|
||||||
|
outputView.CopyToCPU(stream, ref flag, 1);
|
||||||
|
stream.Synchronize();
|
||||||
|
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||||
|
|
||||||
|
return flag != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GpuBatchSize { get; set; } = 262_144;
|
||||||
|
|
||||||
|
private static readonly object GpuWarmUpLock = new();
|
||||||
|
private static int WarmedGpuLeaseCount;
|
||||||
|
|
||||||
|
public static void WarmUpGpuKernels(int threadCount)
|
||||||
|
{
|
||||||
|
int target = threadCount >> 2;
|
||||||
|
if (target == 0)
|
||||||
|
{
|
||||||
|
target = threadCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (GpuWarmUpLock)
|
||||||
|
{
|
||||||
|
if (target <= WarmedGpuLeaseCount)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrimeOrderCalculatorAccelerator.WarmUp();
|
||||||
|
WarmedGpuLeaseCount = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void IsPrimeBatchGpu(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<ulong> values, Span<byte> results)
|
||||||
|
{
|
||||||
|
// GpuPrimeWorkLimiter.Acquire();
|
||||||
|
int acceleratorIndex = gpu.AcceleratorIndex;
|
||||||
|
int totalLength = values.Length;
|
||||||
|
int batchSize = GpuBatchSize;
|
||||||
|
|
||||||
|
var inputView = gpu.InputView;
|
||||||
|
var outputView = gpu.OutputByteView;
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
|
var kernelLauncher = gpu.SmallPrimeSieveKernelLauncher;
|
||||||
|
|
||||||
|
while (pos < totalLength)
|
||||||
|
{
|
||||||
|
int remaining = totalLength - pos;
|
||||||
|
int count = remaining > batchSize ? batchSize : remaining;
|
||||||
|
|
||||||
|
var valueSlice = values.Slice(pos, count);
|
||||||
|
inputView.CopyFromCPU(stream, valueSlice);
|
||||||
|
|
||||||
|
kernelLauncher(
|
||||||
|
stream,
|
||||||
|
count,
|
||||||
|
inputView,
|
||||||
|
gpu.DevicePrimesLastOne,
|
||||||
|
gpu.DevicePrimesLastSeven,
|
||||||
|
gpu.DevicePrimesLastThree,
|
||||||
|
gpu.DevicePrimesLastNine,
|
||||||
|
gpu.DevicePrimesPow2LastOne,
|
||||||
|
gpu.DevicePrimesPow2LastSeven,
|
||||||
|
gpu.DevicePrimesPow2LastThree,
|
||||||
|
gpu.DevicePrimesPow2LastNine,
|
||||||
|
outputView);
|
||||||
|
|
||||||
|
var resultSlice = results.Slice(pos, count);
|
||||||
|
outputView.CopyToCPU(stream, resultSlice);
|
||||||
|
|
||||||
|
pos += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.Synchronize();
|
||||||
|
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||||
|
// GpuPrimeWorkLimiter.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expose cache clearing for accelerator disposal coordination
|
||||||
|
public static void ClearGpuCaches()
|
||||||
|
{
|
||||||
|
PrimeOrderCalculatorAccelerator.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void DisposeGpuContexts()
|
||||||
|
{
|
||||||
|
PrimeOrderCalculatorAccelerator.DisposeAll();
|
||||||
|
lock (GpuWarmUpLock)
|
||||||
|
{
|
||||||
|
WarmedGpuLeaseCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static bool SharesFactorWithMaxExponent(ulong n)
|
||||||
|
{
|
||||||
|
// TODO: Replace this on-the-fly GCD probe with the cached factor table derived from
|
||||||
|
// ResidueComputationBenchmarks so divisor-cycle metadata can short-circuit the test
|
||||||
|
// instead of recomputing binary GCD for every candidate.
|
||||||
|
ulong m = (ulong)BitOperations.Log2(n);
|
||||||
|
return n.BinaryGcd(m) != 1UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void SharesFactorWithMaxExponentBatch(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<ulong> values, Span<byte> results)
|
||||||
|
{
|
||||||
|
// TODO: Route this batch helper through the shared GPU kernel pool from
|
||||||
|
// GpuUInt128BinaryGcdBenchmarks so we reuse cached kernels, pinned host buffers,
|
||||||
|
// and divisor-cycle staging instead of allocating new device buffers per call.
|
||||||
|
// Check in benchmarks, which implementation was the fastest, is compatible with GPU,
|
||||||
|
// and implement it.
|
||||||
|
|
||||||
|
int length = values.Length;
|
||||||
|
|
||||||
|
int acceleratorIndex = gpu.AcceleratorIndex;
|
||||||
|
gpu.EnsureCapacity(0, length);
|
||||||
|
var inputBufferView = gpu.InputView;
|
||||||
|
var resultBufferView = gpu.OutputByteView;
|
||||||
|
var kernelLauncher = gpu.SharesFactorKernelLauncher;
|
||||||
|
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
|
inputBufferView.CopyFromCPU(stream, values);
|
||||||
|
|
||||||
|
kernelLauncher(stream, length, inputBufferView, resultBufferView);
|
||||||
|
|
||||||
|
resultBufferView.CopyToCPU(stream, in results);
|
||||||
|
stream.Synchronize();
|
||||||
|
|
||||||
|
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user