using ILGPU; using ILGPU.Runtime; using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu.Accelerators; namespace PerfectNumbers.Core; internal static partial class PrimeOrderCalculator { private static bool IsGpuPow2Allowed => s_pow2ModeInitialized && s_allowGpuPow2; private const int GpuSmallPrimeFactorSlots = 64; private static ulong CalculateByFactorizationGpu(PrimeOrderCalculatorAccelerator gpu, ulong prime, in MontgomeryDivisorData divisorData) { ulong phi = prime - 1UL; Dictionary counts = gpu.Pow2ModEntriesToTestOnHost; counts.Clear(); FactorCompletelyCpu(gpu, phi, counts); if (counts.Count == 0) { return phi; } int entryCount = counts.Count; Span> entries = stackalloc KeyValuePair[entryCount]; int stored = 0; foreach (var entry in counts) { entries[stored++] = entry; } entries.Sort(entries, static (a, b) => a.Key.CompareTo(b.Key)); ulong order = phi; var acceleratorIndex = gpu.AcceleratorIndex; var kernel = gpu.CheckFactorsKernel; gpu.EnsureCapacity(entryCount, 1); // GpuPrimeWorkLimiter.Acquire(); AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); gpu.Pow2ModEntriesToTestOnDevice.View.CopyFromCPU(stream, entries); var kernelLauncher = kernel.CreateLauncherDelegate, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D>>(); kernelLauncher(stream, entryCount, phi, gpu.Pow2ModEntriesToTestOnDevice.View, divisorData, gpu.OutputUlong.View); gpu.OutputUlong.View.CopyToCPU(stream, ref order, 1); stream.Synchronize(); AcceleratorStreamPool.Return(acceleratorIndex, stream); // GpuPrimeWorkLimiter.Release(); return order; } private static bool TryPopulateSmallPrimeFactorsGpu(PrimeOrderCalculatorAccelerator gpu, ulong value, uint limit, Dictionary counts, out int factorCount, out ulong remaining) { var primeBufferArray = ThreadStaticPools.UlongPool.Rent(GpuSmallPrimeFactorSlots); var exponentBufferArray = ThreadStaticPools.IntPool.Rent(GpuSmallPrimeFactorSlots); Span primeBuffer = primeBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots); Span exponentBuffer = exponentBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots); remaining = value; gpu.EnsureSmallPrimeFactorSlotsCapacity(GpuSmallPrimeFactorSlots); int acceleratorIndex = gpu.AcceleratorIndex; var accelerator = gpu.Accelerator; // GpuPrimeWorkLimiter.Acquire(); var stream = AcceleratorStreamPool.Rent(acceleratorIndex); var kernel = gpu.SmallPrimeFactorKernel; var kernelLauncher = kernel.CreateLauncherDelegate, ArrayView1D, int, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>>(); ArrayView1D smallPrimeFactorCountSlotView = gpu.SmallPrimeFactorCountSlot.View; ArrayView1D smallPrimeFactorRemainingSlotView = gpu.Input.View; ArrayView1D smallPrimeFactorPrimeSlotsView = gpu.OutputUlong.View; ArrayView1D smallPrimeFactorExponentSlotsView = gpu.OutputInt.View; ArrayView1D smallPrimeFactorsPrimesView = gpu.SmallPrimeFactorPrimes; ArrayView1D smallPrimeFactorsSquaresView = gpu.SmallPrimeFactorSquares; kernelLauncher( stream, 1, value, limit, smallPrimeFactorsPrimesView, smallPrimeFactorsSquaresView, (int)smallPrimeFactorsPrimesView.Length, smallPrimeFactorPrimeSlotsView, smallPrimeFactorExponentSlotsView, smallPrimeFactorCountSlotView, smallPrimeFactorRemainingSlotView); factorCount = 0; smallPrimeFactorCountSlotView.CopyToCPU(stream, ref factorCount, 1); stream.Synchronize(); factorCount = Math.Min(factorCount, GpuSmallPrimeFactorSlots); if (factorCount > 0) { primeBuffer = primeBuffer[..factorCount]; smallPrimeFactorPrimeSlotsView.CopyToCPU(stream, primeBuffer); exponentBuffer = exponentBuffer[..factorCount]; smallPrimeFactorExponentSlotsView.CopyToCPU(stream, exponentBuffer); } smallPrimeFactorRemainingSlotView.CopyToCPU(stream, ref remaining, 1); stream.Synchronize(); AcceleratorStreamPool.Return(acceleratorIndex, stream); // GpuPrimeWorkLimiter.Release(); for (int i = 0; i < factorCount; i++) { ulong primeValue = primeBuffer[i]; int exponent = exponentBuffer[i]; counts.Add(primeValue, exponent); } return true; } private static bool EvaluateSpecialMaxCandidatesGpu( PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan factors, ulong phi, ulong prime, in MontgomeryDivisorData divisorData) { int factorCount = factors.Length; gpu.EnsureSpecialMaxFactorsCapacity(factorCount); int acceleratorIndex = gpu.AcceleratorIndex; // GpuPrimeWorkLimiter.Acquire(); var stream = AcceleratorStreamPool.Rent(acceleratorIndex); ArrayView1D specialMaxFactorsView = gpu.Input.View; specialMaxFactorsView.SubView(0, factorCount).CopyFromCPU(stream, factors); var kernel = gpu.SpecialMaxKernel; ArrayView1D specialMaxResultView = gpu.SpecialMaxResult.View; var kernelLauncher = kernel.CreateLauncherDelegate, int, MontgomeryDivisorData, ArrayView1D, ArrayView1D>>(); kernelLauncher( stream, 1, phi, specialMaxFactorsView, factorCount, divisorData, gpu.OutputUlong.View, specialMaxResultView); ulong result = 0UL; specialMaxResultView.CopyToCPU(stream, ref result, 1); stream.Synchronize(); AcceleratorStreamPool.Return(acceleratorIndex, stream); // GpuPrimeWorkLimiter.Release(); return result != 0; } private static bool TryPollardRhoGpu(PrimeOrderCalculatorAccelerator gpu, ulong n, out ulong factor) { Span randomStateSpan = stackalloc ulong[1]; randomStateSpan[0] = ThreadStaticDeterministicRandomGpu.Exclusive.State; Span factoredSpan = stackalloc byte[1]; Span factorSpan = stackalloc ulong[1]; int acceleratorIndex = gpu.AcceleratorIndex; AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); gpu.Input.View.CopyFromCPU(stream, randomStateSpan); var kernelLauncher = gpu.PollardRhoKernel.CreateLauncherDelegate, ArrayView1D, ArrayView1D>>(); kernelLauncher(stream, n, 1, gpu.Input.View, gpu.OutputByte.View, gpu.OutputUlong.View); gpu.OutputByte.View.CopyToCPU(stream, factoredSpan); gpu.OutputUlong.View.CopyToCPU(stream, factorSpan); gpu.Input.View.CopyToCPU(stream, randomStateSpan); stream.Synchronize(); AcceleratorStreamPool.Return(acceleratorIndex, stream); ThreadStaticDeterministicRandomGpu.Exclusive.SetState(randomStateSpan[0]); bool factored = factoredSpan[0] != 0; factor = factored ? factorSpan[0] : 0UL; return factored; } }