Prime testing using divisor tables depending on the last digit separated to its own class. PrimeTester switched back to standard endings.

This commit is contained in:
mlemanczyk committed 2025-11-27 16:45:02 +01:00
1 parent a296e62d4d
commit c311b27dcc
12 files changed
+525 -214

No files matched your search

@@ -26,6 +26,8 @@ public class PrimeTesterBenchmarks
return; return;
} }
PerfectNumberConstants.RollingAccelerators = 1;
PrimeOrderCalculatorAccelerator.WarmUp(); PrimeOrderCalculatorAccelerator.WarmUp();
// HeuristicGroupABPrimeTesterAccelerator.WarmUp(); // HeuristicGroupABPrimeTesterAccelerator.WarmUp();
var gpu = PrimeOrderCalculatorAccelerator.Rent(1); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
@@ -53,7 +55,7 @@ public class PrimeTesterBenchmarks
return primeCount; return primeCount;
} }
[Benchmark] // [Benchmark]
public int HeuristicGpu() public int HeuristicGpu()
{ {
ulong[] values = _candidates; ulong[] values = _candidates;
@@ -72,7 +74,7 @@ public class PrimeTesterBenchmarks
return primeCount; return primeCount;
} }
[Benchmark] // [Benchmark]
public int HeuristicCombinedGpu() public int HeuristicCombinedGpu()
{ {
ulong[] values = _candidates; ulong[] values = _candidates;
@@ -90,7 +92,7 @@ public class PrimeTesterBenchmarks
return primeCount; return primeCount;
} }
[Benchmark] // [Benchmark]
public int HeuristicCombinedCpu() public int HeuristicCombinedCpu()
{ {
ulong[] values = _candidates; ulong[] values = _candidates;
@@ -107,7 +109,7 @@ public class PrimeTesterBenchmarks
return primeCount; return primeCount;
} }
[Benchmark] // [Benchmark]
public int NonHeuristicGpu() public int NonHeuristicGpu()
{ {
ulong[] values = _candidates; ulong[] values = _candidates;
@@ -143,6 +145,23 @@ public class PrimeTesterBenchmarks
return primeCount; return primeCount;
} }
[Benchmark]
public int ByLastDigitCpu()
{
ulong[] values = _candidates;
int primeCount = 0;
for (int i = 0; i < values.Length; i++)
{
if (PrimeTester.IsPrime(values[i]))
{
primeCount++;
}
}
return primeCount;
}
// [Benchmark] // [Benchmark]
public int OpenNumericPrimes() public int OpenNumericPrimes()
{ {
@@ -16,10 +16,10 @@ public static class AcceleratorStreamPool
public static AcceleratorStream Rent(int acceleratorIndex) public static AcceleratorStream Rent(int acceleratorIndex)
{ {
var streamLock = _locks[acceleratorIndex]; // var streamLock = _locks[acceleratorIndex];
var queue = _streams[acceleratorIndex]; var queue = _streams[acceleratorIndex];
streamLock.Wait(); // streamLock.Wait();
return queue.TryDequeue(out var stream) return queue.TryDequeue(out var stream)
? stream ? stream
@@ -28,10 +28,10 @@ public static class AcceleratorStreamPool
public static void Return(int acceleratorIndex, AcceleratorStream stream) public static void Return(int acceleratorIndex, AcceleratorStream stream)
{ {
var streamLock = _locks[acceleratorIndex]; // var streamLock = _locks[acceleratorIndex];
var queue = _streams[acceleratorIndex]; var queue = _streams[acceleratorIndex];
queue.Enqueue(stream); queue.Enqueue(stream);
streamLock.Release(); // streamLock.Release();
} }
public static void WarmUp(int acceleratorIndex) public static void WarmUp(int acceleratorIndex)
@@ -115,7 +115,7 @@ public sealed class PrimeOrderCalculatorAccelerator
public MemoryBuffer1D<ulong, Stride1D.Dense> OutputUlong; public MemoryBuffer1D<ulong, Stride1D.Dense> OutputUlong;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> OutputUlong2; public readonly MemoryBuffer1D<ulong, Stride1D.Dense> OutputUlong2;
public ArrayView1D<ulong, Stride1D.Dense> OutputUlongView; public ArrayView1D<ulong, Stride1D.Dense> OutputUlongView;
public readonly ArrayView1D<ulong, Stride1D.Dense> OutputUlongView2; public readonly ArrayView<ulong> OutputUlongView2;
public MemoryBuffer1D<int, Stride1D.Dense> OutputInt; public MemoryBuffer1D<int, Stride1D.Dense> OutputInt;
public readonly MemoryBuffer1D<int, Stride1D.Dense> OutputInt2; public readonly MemoryBuffer1D<int, Stride1D.Dense> OutputInt2;
@@ -159,7 +159,8 @@ public sealed class PrimeOrderCalculatorAccelerator
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>> ConvertToStandardKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelConvertToStandard)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>>(); public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>> ConvertToStandardKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelConvertToStandard)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>>();
public readonly Action<AcceleratorStream, Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews> HeuristicCombinedTrialDivisionKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews>(PrimeTesterKernels.HeuristicTrialCombinedDivisionKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews>>(); // public readonly Action<AcceleratorStream, Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews> HeuristicCombinedTrialDivisionKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews>(PrimeTesterKernels.HeuristicTrialCombinedDivisionKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews>>();
public readonly Action<AcceleratorStream, Index1D, ArrayView<int>, ulong, ulong, ArrayView<ulong>, ArrayView<ulong>> HeuristicCombinedTrialDivisionKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<int>, ulong, ulong, ArrayView<ulong>, ArrayView<ulong>>(PrimeTesterKernels.HeuristicTrialCombinedDivisionKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<int>, ulong, ulong, ArrayView<ulong>, ArrayView<ulong>>>();
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>> KeepMontgomeryKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelKeepMontgomery)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>>(); public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>> KeepMontgomeryKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelKeepMontgomery)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>>();
@@ -173,17 +174,17 @@ public sealed class PrimeOrderCalculatorAccelerator
// public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<byte>> SharesFactorKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SharesFactorKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<byte>>>(); // public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<byte>> SharesFactorKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SharesFactorKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<byte>>>();
public readonly Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SmallPrimeFactorKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(SmallPrimeFactorKernels.SmallPrimeFactorKernelScan)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>(); // public readonly Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SmallPrimeFactorKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(SmallPrimeFactorKernels.SmallPrimeFactorKernelScan)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> SmallPrimeSieveKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>(); // public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> SmallPrimeSieveKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>();
public readonly Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, ulong, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SpecialMaxKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, ulong, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, ulong, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>(); public readonly Action<AcceleratorStream, Index1D, ulong, ArrayView<ulong>, int, ulong, ArrayView<ulong>> SpecialMaxKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<ulong>, int, ulong, ArrayView<ulong>>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView<ulong>, int, ulong, ArrayView<ulong>>>();
} }
public readonly Action<AcceleratorStream, Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers> CalculateOrderKernelLauncher; public readonly Action<AcceleratorStream, Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers> CalculateOrderKernelLauncher;
public readonly Kernel CheckFactorsKernel; public readonly Kernel CheckFactorsKernel;
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>> ConvertToStandardKernelLauncher; public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>> ConvertToStandardKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews> HeuristicCombinedTrialDivisionKernelLauncher; public readonly Action<AcceleratorStream, Index1D, ArrayView<int>, ulong, ulong, ArrayView<ulong>, ArrayView<ulong>> HeuristicCombinedTrialDivisionKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>> KeepMontgomeryKernelLauncher; public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>> KeepMontgomeryKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, int, ulong, uint, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>> PartialFactorKernelLauncher; public readonly Action<AcceleratorStream, Index1D, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, int, ulong, uint, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>> PartialFactorKernelLauncher;
public readonly Kernel PollardRhoKernel; public readonly Kernel PollardRhoKernel;
@@ -192,7 +193,7 @@ public sealed class PrimeOrderCalculatorAccelerator
public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<byte>> SharesFactorKernelLauncher; public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<byte>> SharesFactorKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SmallPrimeFactorKernelLauncher; public readonly Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SmallPrimeFactorKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> SmallPrimeSieveKernelLauncher; public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> SmallPrimeSieveKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, ulong, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SpecialMaxKernelLauncher; public readonly Action<AcceleratorStream, Index1D, ulong, ArrayView<ulong>, int, ulong, ArrayView<ulong>> SpecialMaxKernelLauncher;
public void EnsureCalculateOrderWideCapacity(int newSize) public void EnsureCalculateOrderWideCapacity(int newSize)
{ {
@@ -442,14 +443,14 @@ public sealed class PrimeOrderCalculatorAccelerator
CheckFactorsKernel = kernels.CheckFactorsKernel; CheckFactorsKernel = kernels.CheckFactorsKernel;
ConvertToStandardKernelLauncher = kernels.ConvertToStandardKernelLauncher; ConvertToStandardKernelLauncher = kernels.ConvertToStandardKernelLauncher;
HeuristicCombinedTrialDivisionKernelLauncher = kernels.HeuristicCombinedTrialDivisionKernelLauncher; HeuristicCombinedTrialDivisionKernelLauncher = kernels.HeuristicCombinedTrialDivisionKernelLauncher;
KeepMontgomeryKernelLauncher = kernels.KeepMontgomeryKernelLauncher; // KeepMontgomeryKernelLauncher = kernels.KeepMontgomeryKernelLauncher;
// PartialFactorKernelLauncher = kernels.PartialFactorKernelLauncher; // PartialFactorKernelLauncher = kernels.PartialFactorKernelLauncher;
// PollardRhoKernel = kernels.PollardRhoKernel; // PollardRhoKernel = kernels.PollardRhoKernel;
Pow2ModKernelLauncher = kernels.Pow2ModKernelLauncher; Pow2ModKernelLauncher = kernels.Pow2ModKernelLauncher;
Pow2ModWideKernelLauncher = kernels.Pow2ModWideKernelLauncher; Pow2ModWideKernelLauncher = kernels.Pow2ModWideKernelLauncher;
// SharesFactorKernelLauncher = kernels.SharesFactorKernelLauncher; // SharesFactorKernelLauncher = kernels.SharesFactorKernelLauncher;
SmallPrimeFactorKernelLauncher = kernels.SmallPrimeFactorKernelLauncher; // SmallPrimeFactorKernelLauncher = kernels.SmallPrimeFactorKernelLauncher;
SmallPrimeSieveKernelLauncher = kernels.SmallPrimeSieveKernelLauncher; // SmallPrimeSieveKernelLauncher = kernels.SmallPrimeSieveKernelLauncher;
SpecialMaxKernelLauncher = kernels.SpecialMaxKernelLauncher; SpecialMaxKernelLauncher = kernels.SpecialMaxKernelLauncher;
// } // }
} }
@@ -8,72 +8,49 @@ internal static partial class PrimeOrderGpuHeuristics
internal static void EvaluateSpecialMaxCandidatesKernel( internal static void EvaluateSpecialMaxCandidatesKernel(
Index1D index, Index1D index,
ulong phi, ulong phi,
ArrayView1D<ulong, Stride1D.Dense> factors, ArrayView<ulong> factors,
int factorCount, int factorCount,
ulong divisorModulus, ulong divisorModulus,
ArrayView1D<ulong, Stride1D.Dense> candidates, ArrayView<ulong> resultOut)
ArrayView1D<ulong, Stride1D.Dense> resultOut)
{ {
if (index != 0)
{
return;
}
int actual = 0;
for (int i = 0; i < factorCount; i++) for (int i = 0; i < factorCount; i++)
{ {
ulong factor = factors[i]; factors[i] = phi / factors[i];
if (factor <= 1UL)
{
continue;
}
ulong reduced = phi / factor;
if (reduced == 0UL)
{
continue;
}
candidates[actual] = reduced;
actual++;
} }
if (actual == 0) for (int i = 1; i < factorCount; i++)
{ {
resultOut[0] = 1; ulong current = factors[i];
return;
}
for (int i = 1; i < actual; i++)
{
ulong current = candidates[i];
int insert = i - 1; int insert = i - 1;
while (insert >= 0 && candidates[insert] > current) while (insert >= 0 && factors[insert] > current)
{ {
candidates[insert + 1] = candidates[insert]; factors[insert + 1] = factors[insert];
insert--; insert--;
} }
candidates[insert + 1] = current; factors[insert + 1] = current;
} }
var divisorPartial = new GpuDivisorPartialData(divisorModulus); var divisorPartial = new GpuDivisorPartialData(divisorModulus);
var stepper = new ExponentRemainderStepperGpu(divisorPartial); var stepper = new ExponentRemainderStepperGpu(divisorPartial);
if (stepper.InitializeIsUnityGpu(candidates[0])) if (stepper.InitializeIsUnityGpu(factors[0]))
{ {
Atomic.Exchange(ref resultOut[0], 0); resultOut[0] = 0UL;
// Atomic.Exchange(ref resultOut[0], 0);
return; return;
} }
for (int i = 1; i < actual; i++) for (int i = 1; i < factorCount; i++)
{ {
if (stepper.ComputeNextIsUnityGpu(candidates[i])) if (stepper.ComputeNextIsUnityGpu(factors[i]))
{ {
Atomic.Exchange(ref resultOut[0], 0); resultOut[0] = 0UL;
// Atomic.Exchange(ref resultOut[0], 0);
return; return;
} }
} }
Atomic.Exchange(ref resultOut[0], 1); resultOut[0] = 1UL;
// Atomic.Exchange(ref resultOut[0], 1);
} }
} }
@@ -80,21 +80,17 @@ internal static class PrimeTesterKernels
public static void HeuristicTrialCombinedDivisionKernel( public static void HeuristicTrialCombinedDivisionKernel(
Index1D index, Index1D index,
byte nMod10, // byte nMod10,
ArrayView<int> resultFlag, ArrayView<int> resultFlag,
ulong n, ulong n,
ulong maxDivisorSquare, ulong maxDivisorSquare,
HeuristicCombinedGpuViews tables) ArrayView<ulong> divisors,
ArrayView<ulong> divisorSquares)
{ {
var (divisors, divisorSquares) = tables.SelectDivisorsAndSquares(nMod10); // var (divisors, divisorSquares) = tables.SelectDivisorsAndSquares(nMod10);
int threadIndex = index; int threadIndex = index;
if (divisorSquares[threadIndex] > maxDivisorSquare) if (divisorSquares[threadIndex] <= maxDivisorSquare && (n % divisors[threadIndex] == 0UL))
{
return;
}
if (n % divisors[threadIndex] == 0UL)
{ {
Atomic.Exchange(ref resultFlag[0], 1); Atomic.Exchange(ref resultFlag[0], 1);
} }
@@ -230,6 +230,7 @@ public sealed class HeuristicCombinedPrimeTester
bool compositeDetected; bool compositeDetected;
Span<int> compositeFlag = stackalloc int[1]; Span<int> compositeFlag = stackalloc int[1];
// int compositeFlag = 0; // int compositeFlag = 0;
var (divisors, divisorSquares) = gpu.DivisorTables.SelectDivisorsAndSquares(nMod10);
var stream = AcceleratorStreamPool.Rent(acceleratorIndex); var stream = AcceleratorStreamPool.Rent(acceleratorIndex);
flagView1DView.MemSetToZero(stream); flagView1DView.MemSetToZero(stream);
@@ -237,11 +238,11 @@ public sealed class HeuristicCombinedPrimeTester
kernelLauncher( kernelLauncher(
stream, stream,
1, 1,
nMod10,
flagView1DView, flagView1DView,
n, n,
maxDivisorSquare, maxDivisorSquare,
gpu.DivisorTables); divisors,
divisorSquares);
flagView1DView.CopyToCPU(stream, compositeFlag); flagView1DView.CopyToCPU(stream, compositeFlag);
stream.Synchronize(); stream.Synchronize();
+6 -6
View File
@@ -243,12 +243,13 @@ public sealed class HeuristicPrimeTester
{ {
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
int acceleratorIndex = gpu.AcceleratorIndex; int acceleratorIndex = gpu.AcceleratorIndex;
var stream = AcceleratorStreamPool.Rent(acceleratorIndex); var kernelLauncher = gpu.HeuristicCombinedTrialDivisionKernelLauncher;
var flagView1DView = gpu.OutputIntView;
bool compositeDetected; bool compositeDetected;
int compositeFlag = 0; int compositeFlag = 0;
var flagView1DView = gpu.OutputIntView;
var (divisors, divisorSquares) = gpu.DivisorTables.SelectDivisorsAndSquares(nMod10);
var stream = AcceleratorStreamPool.Rent(acceleratorIndex);
flagView1DView.CopyFromCPU(stream, ref compositeFlag, 1); flagView1DView.CopyFromCPU(stream, ref compositeFlag, 1);
// Index1D index, // Index1D index,
@@ -258,15 +259,14 @@ public sealed class HeuristicPrimeTester
// HeuristicGpuDivisorTableKind tableKind, // HeuristicGpuDivisorTableKind tableKind,
// HeuristicGpuDivisorTables tables) // HeuristicGpuDivisorTables tables)
var kernelLauncher = gpu.HeuristicCombinedTrialDivisionKernelLauncher;
kernelLauncher( kernelLauncher(
stream, stream,
1, 1,
nMod10,
flagView1DView, flagView1DView,
n, n,
maxDivisorSquare, maxDivisorSquare,
gpu.DivisorTables); divisors,
divisorSquares);
flagView1DView.CopyToCPU(stream, ref compositeFlag, 1); flagView1DView.CopyToCPU(stream, ref compositeFlag, 1);
stream.Synchronize(); stream.Synchronize();
@@ -8,14 +8,14 @@ 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 = 256 - 1; public const int GpuFrequency = 192 - 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;
public const int PooledArrayThreshold = 64; public const int PooledArrayThreshold = 64;
public const int Pow2WindowSize = 8; public const int Pow2WindowSize = 8;
public static readonly int RollingAccelerators = 298; //SharedGpuContext.Device.MaxNumThreadsPerGroup; public static int RollingAccelerators = 298; //SharedGpuContext.Device.MaxNumThreadsPerGroup;
public const int ThreadsByAccelerator = 1; public const int ThreadsByAccelerator = 16;
// TODO: Load these limits from the benchmark-driven configuration so CPU and GPU scans stay aligned with the optimal // TODO: Load these limits from the benchmark-driven configuration so CPU and GPU scans stay aligned with the optimal
// divisor-cycle datasets we generate offline. // divisor-cycle datasets we generate offline.
// TODO: Promote these magic numbers into a runtime profile derived from EvenPerfectBitScanner.Benchmarks so we can retune // TODO: Promote these magic numbers into a runtime profile derived from EvenPerfectBitScanner.Benchmarks so we can retune
+152 -114
View File
@@ -94,31 +94,26 @@ internal static partial class PrimeOrderCalculator
return phi; return phi;
} }
// GpuPrimeWorkLimiter.Acquire();
ulong candidateOrder = InitializeStartingOrderCpu(gpu, prime, phi, divisorData); ulong candidateOrder = InitializeStartingOrderCpu(gpu, prime, phi, divisorData);
candidateOrder = ExponentLoweringCpu(candidateOrder, prime, phiFactors, divisorData); candidateOrder = ExponentLoweringCpu(candidateOrder, prime, phiFactors, divisorData);
if (TryConfirmOrderCpu(gpu, prime, candidateOrder, divisorData, config, out PartialFactorResult? orderFactors)) if (TryConfirmOrderCpu(gpu, prime, candidateOrder, divisorData, config, out PartialFactorResult? orderFactors))
{ {
// GpuPrimeWorkLimiter.Release();
return candidateOrder; return candidateOrder;
} }
if (config.Mode == PrimeOrderMode.Strict) if (config.Mode == PrimeOrderMode.Strict)
{ {
orderFactors?.Dispose(); orderFactors?.Dispose();
// GpuPrimeWorkLimiter.Release();
// return CalculateByFactorizationCpu(gpu, prime, divisorData); // return CalculateByFactorizationCpu(gpu, prime, divisorData);
return CalculateByFactorizationGpu(gpu, prime, divisorData); return 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))
{ {
// GpuPrimeWorkLimiter.Release();
return order; return order;
} }
// GpuPrimeWorkLimiter.Release();
return candidateOrder; return candidateOrder;
} }
@@ -137,17 +132,16 @@ internal static partial class PrimeOrderCalculator
Span<ulong> stackBuffer = stackalloc ulong[length]; Span<ulong> stackBuffer = stackalloc ulong[length];
if (length <= 7) if (RunOnCpu())
{ {
return EvaluateSpecialMaxCandidates(stackBuffer, factorSpan, phi, prime, divisorData); return EvaluateSpecialMaxCandidatesCpu(stackBuffer, factorSpan, phi, prime, divisorData);
} }
return EvaluateSpecialMaxCandidatesGpu(gpu, factorSpan, phi, prime, divisorData); return EvaluateSpecialMaxCandidatesGpu(gpu, factorSpan, phi, prime, divisorData);
} }
private static bool EvaluateSpecialMaxCandidates(Span<ulong> buffer, ReadOnlySpan<ulong> factors, ulong phi, ulong prime, in MontgomeryDivisorData divisorData) private static bool EvaluateSpecialMaxCandidatesCpu(Span<ulong> buffer, ReadOnlySpan<ulong> factors, ulong phi, ulong prime, in MontgomeryDivisorData divisorData)
{ {
int actual = 0;
int factorCount = factors.Length; int factorCount = factors.Length;
for (int i = 0; i < factorCount; i++) for (int i = 0; i < factorCount; i++)
{ {
@@ -159,36 +153,27 @@ internal static partial class PrimeOrderCalculator
// continue; // continue;
// } // }
ulong reduced = phi / factor; // if (reduced == 0UL)
if (reduced == 0UL) // {
{ // continue;
continue; // }
}
buffer[actual] = reduced; buffer[i] = phi / factor;
actual++;
} }
if (actual == 0) buffer.Sort();
{
return true;
}
Span<ulong> candidates = buffer[..actual];
candidates.Sort();
ExponentRemainderStepperCpu stepper = ThreadStaticPools.RentExponentStepperCpu(divisorData); ExponentRemainderStepperCpu stepper = ThreadStaticPools.RentExponentStepperCpu(divisorData);
int candidateCount = candidates.Length;
if (stepper.InitializeCpuIsUnity(candidates[0])) if (stepper.InitializeCpuIsUnity(buffer[0]))
{ {
ThreadStaticPools.ReturnExponentStepperCpu(stepper); ThreadStaticPools.ReturnExponentStepperCpu(stepper);
return false; return false;
} }
for (int i = 1; i < candidateCount; i++) for (int i = 1; i < factorCount; i++)
{ {
if (stepper.ComputeNextIsUnity(candidates[i])) if (stepper.ComputeNextIsUnity(buffer[i]))
{ {
ThreadStaticPools.ReturnExponentStepperCpu(stepper); ThreadStaticPools.ReturnExponentStepperCpu(stepper);
return false; return false;
@@ -1095,14 +1080,12 @@ internal static partial class PrimeOrderCalculator
// Span<int> exponentSlots = exponentSlotsArray.AsSpan(0, FactorSlotCount); // Span<int> exponentSlots = exponentSlotsArray.AsSpan(0, FactorSlotCount);
Span<ulong> primeSlots = stackalloc ulong[FactorSlotCount]; Span<ulong> primeSlots = stackalloc ulong[FactorSlotCount];
Span<int> exponentSlots = stackalloc int[FactorSlotCount]; Span<int> exponentSlots = stackalloc int[FactorSlotCount];
ulong[]? primeSlotArray = null; ulong[] primeSlotArray = [];
int[]? exponentSlotArray = null; int[] exponentSlotArray = [];
// We don't need to worry about leftovers, because we always use indexes within the calculated counts // We don't need to worry about leftovers, because we always use indexes within the calculated counts
// primeSlots.Clear(); // primeSlots.Clear();
// exponentSlots.Clear(); // exponentSlots.Clear();
int factorCount = 0;
uint limit = config.SmallFactorLimit == 0 ? uint.MaxValue : config.SmallFactorLimit; uint limit = config.SmallFactorLimit == 0 ? uint.MaxValue : config.SmallFactorLimit;
ulong remaining = value; ulong remaining = value;
@@ -1114,7 +1097,7 @@ internal static partial class PrimeOrderCalculator
List<PendingEntry> pending = ThreadStaticPools.RentPrimeOrderPendingEntryList(2); List<PendingEntry> pending = ThreadStaticPools.RentPrimeOrderPendingEntryList(2);
Stack<ulong>? compositeStack = null; Stack<ulong>? compositeStack = null;
PrimeTester primeTester = PrimeTester.Exclusive; // PrimeTester primeTester = PrimeTester.Exclusive;
// HeuristicPrimeTester? tester = _tester ??= new(); // HeuristicPrimeTester? tester = _tester ??= new();
// if (!gpuFactored) // if (!gpuFactored)
@@ -1126,7 +1109,7 @@ internal static partial class PrimeOrderCalculator
ref exponentSlots, ref exponentSlots,
ref primeSlotArray, ref primeSlotArray,
ref exponentSlotArray, ref exponentSlotArray,
out factorCount, out int factorCount,
out remaining); out remaining);
// } // }
@@ -1146,72 +1129,21 @@ internal static partial class PrimeOrderCalculator
bool pollardRhoDeadlineReached = Stopwatch.GetTimestamp() > deadlineTimestamp; bool pollardRhoDeadlineReached = Stopwatch.GetTimestamp() > deadlineTimestamp;
if (!pollardRhoDeadlineReached) if (!pollardRhoDeadlineReached)
{ {
// GpuPrimeWorkLimiter.Acquire(); CollectFactors(gpu, ref primeSlots, ref exponentSlots, ref primeSlotArray, ref exponentSlotArray, ref factorCount, pending, compositeStack, deadlineTimestamp, ref pollardRhoDeadlineReached);
// bool runOnCpu = !RunOnGpu();
while (compositeStack.Count > 0) // if (runOnCpu)
{ // {
ulong composite = compositeStack.Pop(); // CollectFactorsCpu(ref primeSlots, ref exponentSlots, ref primeSlotArray, ref exponentSlotArray, ref factorCount, pending, compositeStack, deadlineTimestamp, ref pollardRhoDeadlineReached);
// composite will never be smaller than 1 on the execution path // }
// if (composite <= 1UL) // else
// { // {
// continue; // CollectFactorsGpu(gpu, ref primeSlots, ref exponentSlots, ref primeSlotArray, ref exponentSlotArray, ref factorCount, pending, compositeStack, deadlineTimestamp, ref pollardRhoDeadlineReached);
// } // }
// bool isPrime = primeTester.HeuristicIsPrimeGpu(composite);
// bool isPrime = PrimeTester.IsPrimeInternal(composite, CancellationToken.None);
// Atomic.Add(ref _partialFactorHits, 1UL);
// Console.WriteLine($"Partial factor hits {Volatile.Read(ref _partialFactorHits)}");
// bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None);
// bool goToGpu = Volatile.Read(ref _goToGpu);
// Volatile.Write(ref _goToGpu, !goToGpu);
// bool isPrime = goToGpu
// ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite)
// : PrimeTester.IsPrime(composite);
// bool isPrime = composite > PerfectNumberConstants.MaxQForDivisorCycles
// ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite)
// : PrimeTester.IsPrime(composite);
// bool isPrime = PrimeTester.IsPrime(composite);
// bool isPrime = HeuristicCombinedPrimeTester.IsPrimeCpu(composite);
// bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite);
bool runOnGpu = RunOnGpu();
bool isPrime = !runOnGpu ? PrimeTester.IsPrime(composite) : HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite);
// bool isPrime = PrimeTester.IsPrime(composite);
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
if (isPrime)
{
AddFactorToCollector(ref primeSlots, ref exponentSlots, ref primeSlotArray, ref exponentSlotArray, ref factorCount, composite, 1);
continue;
}
long currentTimestamp = Stopwatch.GetTimestamp();
if (currentTimestamp > deadlineTimestamp)
{
pollardRhoDeadlineReached = true;
pending.Add(new PendingEntry(composite, knownComposite: true));
continue;
}
// if (!TryPollardRhoGpu(gpu, composite, out ulong factor))
if (!TryPollardRhoCpu(composite, deadlineTimestamp, out ulong factor))
{
pending.Add(new PendingEntry(composite, knownComposite: true));
continue;
}
ulong quotient = composite / factor;
compositeStack.Push(factor);
compositeStack.Push(quotient);
}
if (!pollardRhoDeadlineReached) if (!pollardRhoDeadlineReached)
{ {
pollardRhoDeadlineReached = Stopwatch.GetTimestamp() > deadlineTimestamp; pollardRhoDeadlineReached = Stopwatch.GetTimestamp() > deadlineTimestamp;
} }
// GpuPrimeWorkLimiter.Release();
} }
if (pollardRhoDeadlineReached) if (pollardRhoDeadlineReached)
@@ -1227,7 +1159,6 @@ internal static partial class PrimeOrderCalculator
bool cofactorContainsComposite = false; bool cofactorContainsComposite = false;
int pendingCount = pending.Count; int pendingCount = pending.Count;
int index = 0; int index = 0;
// GpuPrimeWorkLimiter.Acquire();
for (; index < pendingCount; index++) for (; index < pendingCount; index++)
{ {
PendingEntry entry = pending[index]; PendingEntry entry = pending[index];
@@ -1382,7 +1313,112 @@ internal static partial class PrimeOrderCalculator
return result; return result;
} }
private static bool RunOnGpu() [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void CollectFactors(PrimeOrderCalculatorAccelerator gpu, ref Span<ulong> primeSlots, ref Span<int> exponentSlots, ref ulong[] primeSlotArray, ref int[] exponentSlotArray, ref int factorCount, List<PendingEntry> pending, Stack<ulong> compositeStack, long deadlineTimestamp, ref bool pollardRhoDeadlineReached)
{
while (compositeStack.Count > 0)
{
ulong composite = compositeStack.Pop();
bool isPrime = RunOnCpu() ? PrimeTester.IsPrime(composite) : HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite);
if (isPrime)
{
AddFactorToCollector(ref primeSlots, ref exponentSlots, ref primeSlotArray, ref exponentSlotArray, ref factorCount, composite, 1);
continue;
}
long currentTimestamp = Stopwatch.GetTimestamp();
if (currentTimestamp > deadlineTimestamp)
{
pollardRhoDeadlineReached = true;
pending.Add(new PendingEntry(composite, knownComposite: true));
continue;
}
// if (!TryPollardRhoGpu(gpu, composite, out ulong factor))
if (!TryPollardRhoCpu(composite, deadlineTimestamp, out ulong factor))
{
pending.Add(new PendingEntry(composite, knownComposite: true));
continue;
}
ulong quotient = composite / factor;
compositeStack.Push(factor);
compositeStack.Push(quotient);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void CollectFactorsCpu(ref Span<ulong> primeSlots, ref Span<int> exponentSlots, ref ulong[] primeSlotArray, ref int[] exponentSlotArray, ref int factorCount, List<PendingEntry> pending, Stack<ulong> compositeStack, long deadlineTimestamp, ref bool pollardRhoDeadlineReached)
{
while (compositeStack.Count > 0)
{
ulong composite = compositeStack.Pop();
bool isPrime = PrimeTester.IsPrime(composite);
if (isPrime)
{
AddFactorToCollector(ref primeSlots, ref exponentSlots, ref primeSlotArray, ref exponentSlotArray, ref factorCount, composite, 1);
continue;
}
long currentTimestamp = Stopwatch.GetTimestamp();
if (currentTimestamp > deadlineTimestamp)
{
pollardRhoDeadlineReached = true;
pending.Add(new PendingEntry(composite, knownComposite: true));
continue;
}
// if (!TryPollardRhoGpu(gpu, composite, out ulong factor))
if (!TryPollardRhoCpu(composite, deadlineTimestamp, out ulong factor))
{
pending.Add(new PendingEntry(composite, knownComposite: true));
continue;
}
ulong quotient = composite / factor;
compositeStack.Push(factor);
compositeStack.Push(quotient);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void CollectFactorsGpu(PrimeOrderCalculatorAccelerator gpu, ref Span<ulong> primeSlots, ref Span<int> exponentSlots, ref ulong[] primeSlotArray, ref int[] exponentSlotArray, ref int factorCount, List<PendingEntry> pending, Stack<ulong> compositeStack, long deadlineTimestamp, ref bool pollardRhoDeadlineReached)
{
while (compositeStack.Count > 0)
{
ulong composite = compositeStack.Pop();
bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite);
if (isPrime)
{
AddFactorToCollector(ref primeSlots, ref exponentSlots, ref primeSlotArray, ref exponentSlotArray, ref factorCount, composite, 1);
continue;
}
long currentTimestamp = Stopwatch.GetTimestamp();
if (currentTimestamp > deadlineTimestamp)
{
pollardRhoDeadlineReached = true;
pending.Add(new PendingEntry(composite, knownComposite: true));
continue;
}
// if (!TryPollardRhoGpu(gpu, composite, out ulong factor))
if (!TryPollardRhoCpu(composite, deadlineTimestamp, out ulong factor))
{
pending.Add(new PendingEntry(composite, knownComposite: true));
continue;
}
ulong quotient = composite / factor;
compositeStack.Push(factor);
compositeStack.Push(quotient);
}
}
private static bool RunOnCpu()
{ {
// return false; // return false;
// return true; // return true;
@@ -1412,7 +1448,7 @@ internal static partial class PrimeOrderCalculator
cpuCount -= PerfectNumberConstants.GpuFrequency; cpuCount -= PerfectNumberConstants.GpuFrequency;
} }
return cpuCount == PerfectNumberConstants.GpuFrequency; return cpuCount != PerfectNumberConstants.GpuFrequency;
} }
private static void PopulateSmallPrimeFactorsCpu( private static void PopulateSmallPrimeFactorsCpu(
@@ -1420,8 +1456,8 @@ internal static partial class PrimeOrderCalculator
uint limit, uint limit,
ref Span<ulong> primeTargets, ref Span<ulong> primeTargets,
ref Span<int> exponentTargets, ref Span<int> exponentTargets,
ref ulong[]? primeArray, ref ulong[] primeArray,
ref int[]? exponentArray, ref int[] exponentArray,
out int factorCount, out int factorCount,
out ulong remaining) out ulong remaining)
{ {
@@ -1592,12 +1628,6 @@ internal static partial class PrimeOrderCalculator
long timestamp = 0L; // reused for deadline checks. long timestamp = 0L; // reused for deadline checks.
while (true) while (true)
{ {
timestamp = Stopwatch.GetTimestamp();
if (timestamp > deadlineTimestamp)
{
return false;
}
ulong c = (DeterministicRandomCpu.NextUInt64() % (n - 1UL)) + 1UL; ulong c = (DeterministicRandomCpu.NextUInt64() % (n - 1UL)) + 1UL;
ulong x = (DeterministicRandomCpu.NextUInt64() % (n - 2UL)) + 2UL; ulong x = (DeterministicRandomCpu.NextUInt64() % (n - 2UL)) + 2UL;
ulong y = x; ulong y = x;
@@ -1612,8 +1642,7 @@ internal static partial class PrimeOrderCalculator
} }
x = AdvancePolynomialCpu(x, c, n); x = AdvancePolynomialCpu(x, c, n);
y = AdvancePolynomialCpu(y, c, n); y = AdvancePolynomialTwiceCpu(y, c, n);
y = AdvancePolynomialCpu(y, c, n);
ulong diff = x > y ? x - y : y - x; ulong diff = x > y ? x - y : y - x;
d = BinaryGcd(diff, n); d = BinaryGcd(diff, n);
} }
@@ -1633,6 +1662,15 @@ internal static partial class PrimeOrderCalculator
return (ulong)(value % modulus); return (ulong)(value % modulus);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong AdvancePolynomialTwiceCpu(ulong x, ulong c, ulong modulus)
{
UInt128 value = (UInt128)x * x + c;
var remainder = (ulong)(value % modulus);
value = (UInt128)remainder * remainder + c;
return (ulong)(value % modulus);
}
private static ulong BinaryGcd(ulong a, ulong b) private static ulong BinaryGcd(ulong a, ulong b)
{ {
if (a == 0UL) if (a == 0UL)
@@ -1679,8 +1717,8 @@ internal static partial class PrimeOrderCalculator
private static void AddFactorToCollector( private static void AddFactorToCollector(
ref Span<ulong> primes, ref Span<ulong> primes,
ref Span<int> exponents, ref Span<int> exponents,
ref ulong[]? primeArray, ref ulong[] primeArray,
ref int[]? exponentArray, ref int[] exponentArray,
ref int count, ref int count,
ulong prime, ulong prime,
int exponent) int exponent)
@@ -1715,8 +1753,8 @@ internal static partial class PrimeOrderCalculator
private static void EnsureCollectorCapacity( private static void EnsureCollectorCapacity(
ref Span<ulong> primes, ref Span<ulong> primes,
ref Span<int> exponents, ref Span<int> exponents,
ref ulong[]? primeArray, ref ulong[] primeArray,
ref int[]? exponentArray, ref int[] exponentArray,
int count, int count,
int requiredCapacity) int requiredCapacity)
{ {
@@ -1736,10 +1774,10 @@ internal static partial class PrimeOrderCalculator
Span<ulong> newPrimeSpan = newPrimeArray.AsSpan(); Span<ulong> newPrimeSpan = newPrimeArray.AsSpan();
Span<int> newExponentSpan = newExponentArray.AsSpan(); Span<int> newExponentSpan = newExponentArray.AsSpan();
primes.Slice(0, count).CopyTo(newPrimeSpan); primes[..count].CopyTo(newPrimeSpan);
exponents.Slice(0, count).CopyTo(newExponentSpan); exponents[..count].CopyTo(newExponentSpan);
if (primeArray is not null) if (primeArray.Length > 0)
{ {
ThreadStaticPools.UlongPool.Return(primeArray, clearArray: false); ThreadStaticPools.UlongPool.Return(primeArray, clearArray: false);
ThreadStaticPools.IntPool.Return(exponentArray!, clearArray: false); ThreadStaticPools.IntPool.Return(exponentArray!, clearArray: false);
@@ -41,7 +41,6 @@ internal static partial class PrimeOrderCalculator
gpu.EnsureCapacity(entryCount, 1); gpu.EnsureCapacity(entryCount, 1);
var pow2ModEntriesToTestOnDeviceView = gpu.Pow2ModEntriesToTestOnDeviceView; var pow2ModEntriesToTestOnDeviceView = gpu.Pow2ModEntriesToTestOnDeviceView;
// GpuPrimeWorkLimiter.Acquire();
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
pow2ModEntriesToTestOnDeviceView.CopyFromCPU(stream, entries); pow2ModEntriesToTestOnDeviceView.CopyFromCPU(stream, entries);
@@ -51,7 +50,6 @@ internal static partial class PrimeOrderCalculator
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(acceleratorIndex, stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
// GpuPrimeWorkLimiter.Release();
return order; return order;
} }
@@ -133,8 +131,8 @@ internal static partial class PrimeOrderCalculator
int acceleratorIndex = gpu.AcceleratorIndex; int acceleratorIndex = gpu.AcceleratorIndex;
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
ArrayView1D<ulong, Stride1D.Dense> specialMaxFactorsView = gpu.InputView; ArrayView<ulong> specialMaxFactorsView = gpu.InputView;
ArrayView1D<ulong, Stride1D.Dense> specialMaxResultView = gpu.OutputUlongView2; ArrayView<ulong> specialMaxResultView = gpu.OutputUlongView2;
var kernelLauncher = gpu.SpecialMaxKernelLauncher; var kernelLauncher = gpu.SpecialMaxKernelLauncher;
var stream = AcceleratorStreamPool.Rent(acceleratorIndex); var stream = AcceleratorStreamPool.Rent(acceleratorIndex);
@@ -147,7 +145,6 @@ internal static partial class PrimeOrderCalculator
specialMaxFactorsView, specialMaxFactorsView,
factorCount, factorCount,
divisorData.Modulus, divisorData.Modulus,
gpu.OutputUlongView,
specialMaxResultView); specialMaxResultView);
ulong result = 0UL; ulong result = 0UL;
+291
View File
@@ -6,6 +6,249 @@ using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core; 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)
{
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;
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 sealed class PrimeTester
{ {
public PrimeTester() public PrimeTester()
@@ -34,6 +277,54 @@ public sealed class PrimeTester
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 = PrimesGenerator.SmallPrimes;
ulong[] smallPrimeDivisorsMul = PrimesGenerator.SmallPrimesPow2;
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;
}
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) 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
+8 -17
View File
@@ -340,23 +340,14 @@ public static partial class ULongExtensions
// Benchmarks (Mod5ULongBenchmarks) show the direct `% 5` is still cheaper (~0.26 ns vs 0.43 ns), so keep the modulo until a faster lookup is proven. // Benchmarks (Mod5ULongBenchmarks) show the direct `% 5` is still cheaper (~0.26 ns vs 0.43 ns), so keep the modulo until a faster lookup is proven.
// (Mod8/Mod10 stay masked because they win; Mod5 currently does not.) // (Mod8/Mod10 stay masked because they win; Mod5 currently does not.)
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Mod10(this ulong value) => (value & 1UL) == 0UL public static ulong Mod10(this ulong value) => (value % 5UL) switch
? (value % 5UL) switch {
{ 0UL => 0UL,
0UL => 0UL, 1UL => 6UL,
1UL => 6UL, 2UL => 2UL,
2UL => 2UL, 3UL => 8UL,
3UL => 8UL, _ => 4UL,
_ => 4UL, };
}
: (value % 5UL) switch
{
0UL => 5UL,
1UL => 1UL,
2UL => 7UL,
3UL => 3UL,
_ => 9UL,
};
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Mod128(this ulong value) => value & 127UL; public static ulong Mod128(this ulong value) => value & 127UL;