From 13498dc4604bf5a52ca460c151d3645e806f04be Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Wed, 26 Nov 2025 03:16:39 +0100 Subject: [PATCH] Most device buffers integrated into PrimeOrderCalculatorAccelerator. PrimeOrderCalculator switched to partial factoring on GPU. --- .../Gpu/GpuKernelCompilationTests.cs | 2 +- .../Gpu/PrimeOrderGpuHeuristicsTests.cs | 37 +- .../PrimeOrderCalculatorAccelerator.cs | 259 +++++++++--- .../Gpu/CalculateOrderKernelConfig.cs | 10 + .../Gpu/Kernels/PrimeOrderKernelStatus.cs | 10 + .../Gpu/Kernels/PrimeOrderKernels.cs | 23 +- PerfectNumbers.Core/Gpu/OrderKernelBuffers.cs | 28 ++ .../Gpu/Pow2MontgomeryGpuCalculator.cs | 20 +- .../Gpu/PrimeOrderConstants.cs | 14 + .../Gpu/PrimeOrderGpuHeuristics.cs | 397 +++++------------- .../HeuristicCombinedPrimeTester.cs | 10 +- PerfectNumbers.Core/HeuristicPrimeTester.cs | 10 +- PerfectNumbers.Core/MersenneDivisorCycles.cs | 5 +- .../PrimeOrderCalculator.Cpu.cs | 18 +- .../PrimeOrderCalculator.Gpu.cs | 46 +- .../PrimeOrderCalculator.Shared.cs | 2 +- PerfectNumbers.Core/PrimeTester.cs | 24 +- 17 files changed, 483 insertions(+), 432 deletions(-) create mode 100644 PerfectNumbers.Core/Gpu/CalculateOrderKernelConfig.cs create mode 100644 PerfectNumbers.Core/Gpu/Kernels/PrimeOrderKernelStatus.cs create mode 100644 PerfectNumbers.Core/Gpu/OrderKernelBuffers.cs create mode 100644 PerfectNumbers.Core/Gpu/PrimeOrderConstants.cs diff --git a/PerfectNumbers.Core.Tests/Gpu/GpuKernelCompilationTests.cs b/PerfectNumbers.Core.Tests/Gpu/GpuKernelCompilationTests.cs index bc4b36c..e870d04 100644 --- a/PerfectNumbers.Core.Tests/Gpu/GpuKernelCompilationTests.cs +++ b/PerfectNumbers.Core.Tests/Gpu/GpuKernelCompilationTests.cs @@ -321,7 +321,7 @@ public class GpuKernelCompilationTests private static void CompilePrimeOrderPow2ModWideKernel(Accelerator accelerator) { - var kernel = accelerator.LoadAutoGroupedStreamKernel, GpuUInt128, ArrayView1D>(PrimeOrderGpuHeuristics.Pow2ModKernelWide); + var kernel = accelerator.LoadAutoGroupedStreamKernel, GpuUInt128, ArrayView1D>(PrimeOrderKernels.Pow2ModKernelWide); _ = kernel; } diff --git a/PerfectNumbers.Core.Tests/Gpu/PrimeOrderGpuHeuristicsTests.cs b/PerfectNumbers.Core.Tests/Gpu/PrimeOrderGpuHeuristicsTests.cs index 9aa5340..5a4cc1a 100644 --- a/PerfectNumbers.Core.Tests/Gpu/PrimeOrderGpuHeuristicsTests.cs +++ b/PerfectNumbers.Core.Tests/Gpu/PrimeOrderGpuHeuristicsTests.cs @@ -21,17 +21,19 @@ public class PrimeOrderGpuHeuristicsTests public void TryPow2Mod_returns_overflow_for_marked_prime() { const ulong prime = 97UL; + var gpu = PrimeOrderCalculatorAccelerator.Rent(1); try { PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0; MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); - GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(1UL, prime, out ulong remainder, divisorData); + GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, 1UL, prime, out ulong remainder, divisorData); status.Should().Be(GpuPow2ModStatus.Overflow); remainder.Should().Be(0UL); } finally { + PrimeOrderCalculatorAccelerator.Return(gpu); PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _); } } @@ -42,7 +44,10 @@ public class PrimeOrderGpuHeuristicsTests const ulong exponent = 138_000_001UL; const ulong prime = 138_000_001UL; MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); - GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(exponent, prime, out ulong remainder, divisorData); + var gpu = PrimeOrderCalculatorAccelerator.Rent(1); + GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, exponent, prime, out ulong remainder, divisorData); + + PrimeOrderCalculatorAccelerator.Return(gpu); status.Should().Be(GpuPow2ModStatus.Success); remainder.Should().Be(2UL); @@ -54,16 +59,18 @@ public class PrimeOrderGpuHeuristicsTests const ulong prime = 97UL; var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(6, 64); PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability); + var gpu = PrimeOrderCalculatorAccelerator.Rent(1); try { MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); - GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(1UL, prime, out ulong remainder, divisorData); + GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, 1UL, prime, out ulong remainder, divisorData); status.Should().Be(GpuPow2ModStatus.Overflow); remainder.Should().Be(0UL); } finally { + PrimeOrderCalculatorAccelerator.Return(gpu); PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting(); PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _); } @@ -73,22 +80,24 @@ public class PrimeOrderGpuHeuristicsTests public void TryPow2Mod_returns_overflow_when_exponent_exceeds_capability() { var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 3); + var gpu = PrimeOrderCalculatorAccelerator.Rent(1); PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability); try { const ulong prime = 193UL; MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); - GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(16UL, prime, out ulong remainder, divisorData); + GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, 16UL, prime, out ulong remainder, divisorData); status.Should().Be(GpuPow2ModStatus.Overflow); remainder.Should().Be(0UL); - GpuPow2ModStatus followUp = PrimeOrderGpuHeuristics.TryPow2Mod(4UL, prime, out ulong followUpRemainder, divisorData); + GpuPow2ModStatus followUp = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, 4UL, prime, out ulong followUpRemainder, divisorData); followUp.Should().Be(GpuPow2ModStatus.Success); followUpRemainder.Should().Be(16UL); } finally { + PrimeOrderCalculatorAccelerator.Return(gpu); PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting(); } } @@ -97,6 +106,7 @@ public class PrimeOrderGpuHeuristicsTests public void TryPow2ModBatch_returns_overflow_for_marked_prime() { const ulong prime = 193UL; + var gpu = PrimeOrderCalculatorAccelerator.Rent(1); try { PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0; @@ -104,7 +114,7 @@ public class PrimeOrderGpuHeuristicsTests ulong[] remainders = new ulong[2]; ulong[] exponents = { 1UL, 2UL }; MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); - GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData); + GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders, divisorData); status.Should().Be(GpuPow2ModStatus.Overflow); remainders[0].Should().Be(0UL); @@ -112,6 +122,7 @@ public class PrimeOrderGpuHeuristicsTests } finally { + PrimeOrderCalculatorAccelerator.Return(gpu); PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _); } } @@ -123,7 +134,11 @@ public class PrimeOrderGpuHeuristicsTests ulong[] exponents = { 138_000_001UL, 138_000_011UL }; const ulong prime = 138_000_001UL; MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); - GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData); + + var gpu = PrimeOrderCalculatorAccelerator.Rent(1); + GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders, divisorData); + + PrimeOrderCalculatorAccelerator.Return(gpu); ulong expectedFirst = (ulong)BigInteger.ModPow(2, exponents[0], prime); ulong expectedSecond = (ulong)BigInteger.ModPow(2, exponents[1], prime); @@ -140,6 +155,7 @@ public class PrimeOrderGpuHeuristicsTests { const ulong prime = 97UL; var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(6, 64); + var gpu = PrimeOrderCalculatorAccelerator.Rent(1); PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability); try { @@ -147,7 +163,7 @@ public class PrimeOrderGpuHeuristicsTests ulong[] remainders = { 5UL, 6UL }; MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); - GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData); + GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders, divisorData); status.Should().Be(GpuPow2ModStatus.Overflow); remainders[0].Should().Be(0UL); @@ -155,6 +171,7 @@ public class PrimeOrderGpuHeuristicsTests } finally { + PrimeOrderCalculatorAccelerator.Return(gpu); PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting(); PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _); } @@ -164,6 +181,7 @@ public class PrimeOrderGpuHeuristicsTests public void TryPow2ModBatch_returns_overflow_when_any_exponent_exceeds_capability() { var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 3); + var gpu = PrimeOrderCalculatorAccelerator.Rent(1); PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability); try { @@ -172,7 +190,7 @@ public class PrimeOrderGpuHeuristicsTests const ulong prime = 193UL; MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); - GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData); + GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders, divisorData); status.Should().Be(GpuPow2ModStatus.Overflow); remainders[0].Should().Be(0UL); @@ -180,6 +198,7 @@ public class PrimeOrderGpuHeuristicsTests } finally { + PrimeOrderCalculatorAccelerator.Return(gpu); PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting(); } } diff --git a/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs b/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs index 9190ee1..faac3bd 100644 --- a/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs +++ b/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs @@ -79,7 +79,6 @@ public sealed class PrimeOrderCalculatorAccelerator stream.Synchronize(); stream.Dispose(); } - } #endregion @@ -105,21 +104,57 @@ public sealed class PrimeOrderCalculatorAccelerator public readonly Accelerator Accelerator; public readonly int AcceleratorIndex; + public MemoryBuffer1D Input; - public MemoryBuffer1D OutputUlong; + public ArrayView1D InputView; + public MemoryBuffer1D OutputByte; + public ArrayView1D OutputByteView; + + public MemoryBuffer1D OutputUlong; + public readonly MemoryBuffer1D OutputUlong2; + public ArrayView1D OutputUlongView; + public readonly ArrayView1D OutputUlongView2; + public MemoryBuffer1D OutputInt; + public readonly MemoryBuffer1D OutputInt2; + public ArrayView1D OutputIntView; + public readonly ArrayView1D OutputIntView2; + public MemoryBuffer1D, Stride1D.Dense> Pow2ModEntriesToTestOnDevice; - public readonly MemoryBuffer1D SmallPrimeFactorCountSlot; - public readonly MemoryBuffer1D SpecialMaxResult; + public ArrayView1D, Stride1D.Dense> Pow2ModEntriesToTestOnDeviceView; + + public readonly MemoryBuffer1D WorkFactorBuffer; + public readonly MemoryBuffer1D CandidateBuffer; + public readonly MemoryBuffer1D StackIndexBuffer; + public readonly MemoryBuffer1D StackExponentBuffer; + public readonly MemoryBuffer1D StackProductBuffer; + + public OrderKernelBuffers CalculateOrderKernelBuffers; + public readonly ArrayView1D WorkFactorBufferView; + public readonly ArrayView1D CandidateBufferView; + public readonly ArrayView1D StackIndexBufferView; + public readonly ArrayView1D StackExponentBufferView; + public readonly ArrayView1D StackProductBufferView; + + + + + public MemoryBuffer1D CalculateOrderWideExponentBuffer; + public MemoryBuffer1D CalculateOrderWideRemainderBuffer; + public ArrayView1D CalculateOrderWideExponentBufferView; + public ArrayView1D CalculateOrderWideRemainderBufferView; + public Dictionary Pow2ModEntriesToTestOnHost = []; - private static readonly CalculatorKernels[] _kernels =new CalculatorKernels[AcceleratorPool.Shared.Accelerators.Length]; + private static readonly CalculatorKernels[] _kernels = new CalculatorKernels[_accelerators.Length]; private sealed class CalculatorKernels(Accelerator accelerator) { - public readonly Kernel CheckFactorsKernel = KernelUtil.GetKernel(accelerator.LoadStreamKernel, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D>(PrimeOrderGpuHeuristics.CheckFactorsKernel)); + public readonly Action, ArrayView1D, int, OrderKernelBuffers> CalculateOrderKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, ArrayView1D, int, OrderKernelBuffers>(PrimeOrderKernels.CalculateOrderKernel)).CreateLauncherDelegate, ArrayView1D, int, OrderKernelBuffers>>(); + + public readonly Kernel CheckFactorsKernel = KernelUtil.GetKernel(accelerator.LoadStreamKernel, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D>(PrimeOrderKernels.CheckFactorsKernel)); public readonly Action, ulong, ulong, ulong, ulong, ulong, ArrayView1D> ConvertToStandardKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, ulong, ulong, ulong, ulong, ulong, ArrayView1D>(Pow2MontgomeryKernels.Pow2MontgomeryKernelConvertToStandard)).CreateLauncherDelegate, ulong, ulong, ulong, ulong, ulong, ArrayView1D>>(); @@ -127,9 +162,13 @@ public sealed class PrimeOrderCalculatorAccelerator public readonly Action, ulong, ulong, ulong, ulong, ulong, ArrayView1D> KeepMontgomeryKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, ulong, ulong, ulong, ulong, ulong, ArrayView1D>(Pow2MontgomeryKernels.Pow2MontgomeryKernelKeepMontgomery)).CreateLauncherDelegate, ulong, ulong, ulong, ulong, ulong, ArrayView1D>>(); + public readonly Action, ArrayView1D, int, int, ulong, uint, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D> PartialFactorKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, ArrayView1D, int, int, ulong, uint, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>(PrimeOrderKernels.PartialFactorKernel)).CreateLauncherDelegate, ArrayView1D, int, int, ulong, uint, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>>(); + public readonly Kernel PollardRhoKernel = KernelUtil.GetKernel(accelerator.LoadStreamKernel, ArrayView1D, ArrayView1D>(Pow2MontgomeryKernels.TryPollardRhoKernel)); - public readonly Action, GpuUInt128, ArrayView1D> Pow2ModWideKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, GpuUInt128, ArrayView1D>(PrimeOrderGpuHeuristics.Pow2ModKernelWide)).CreateLauncherDelegate, GpuUInt128, ArrayView1D>>(); + public readonly Action, ulong, ulong, ulong, ulong, ulong, ArrayView1D> Pow2ModKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, ulong, ulong, ulong, ulong, ulong, ArrayView1D>(PrimeOrderKernels.Pow2ModKernel)).CreateLauncherDelegate, ulong, ulong, ulong, ulong, ulong, ArrayView1D>>(); + + public readonly Action, GpuUInt128, ArrayView1D> Pow2ModWideKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, GpuUInt128, ArrayView1D>(PrimeOrderKernels.Pow2ModKernelWide)).CreateLauncherDelegate, GpuUInt128, ArrayView1D>>(); public readonly Action, ArrayView> SharesFactorKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, ArrayView>(PrimeTesterKernels.SharesFactorKernel)).CreateLauncherDelegate, ArrayView>>(); @@ -140,17 +179,38 @@ public sealed class PrimeOrderCalculatorAccelerator public readonly Action, int, ulong, ArrayView1D, ArrayView1D> SpecialMaxKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, int, ulong, ArrayView1D, ArrayView1D>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel)).CreateLauncherDelegate, int, ulong, ArrayView1D, ArrayView1D>>(); } + public readonly Action, ArrayView1D, int, OrderKernelBuffers> CalculateOrderKernelLauncher; public readonly Kernel CheckFactorsKernel; public readonly Action, ulong, ulong, ulong, ulong, ulong, ArrayView1D> ConvertToStandardKernelLauncher; public readonly Action, ulong, ulong, HeuristicCombinedGpuViews> HeuristicCombinedTrialDivisionKernelLauncher; public readonly Action, ulong, ulong, ulong, ulong, ulong, ArrayView1D> KeepMontgomeryKernelLauncher; + public readonly Action, ArrayView1D, int, int, ulong, uint, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D> PartialFactorKernelLauncher; public readonly Kernel PollardRhoKernel; + public readonly Action, ulong, ulong, ulong, ulong, ulong, ArrayView1D> Pow2ModKernelLauncher; public readonly Action, GpuUInt128, ArrayView1D> Pow2ModWideKernelLauncher; public readonly Action, ArrayView> SharesFactorKernelLauncher; public readonly Action, ArrayView1D, int, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D> SmallPrimeFactorKernelLauncher; public readonly Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> SmallPrimeSieveKernelLauncher; public readonly Action, int, ulong, ArrayView1D, ArrayView1D> SpecialMaxKernelLauncher; + public void EnsureCalculateOrderWideCapacity(int newSize) + { + if (newSize > Input.Length) + { + CalculateOrderWideExponentBuffer.Dispose(); + CalculateOrderWideRemainderBuffer.Dispose(); + + var accelerator = Accelerator; + // lock (accelerator) + // { + CalculateOrderWideExponentBuffer = accelerator.Allocate1D(newSize); + CalculateOrderWideRemainderBuffer = accelerator.Allocate1D(newSize); + CalculateOrderWideExponentBufferView = CalculateOrderWideExponentBuffer.View; + CalculateOrderWideRemainderBufferView = CalculateOrderWideRemainderBuffer.View; + // } + } + } + public void EnsureCapacity(int factorsCount, int primeTesterCapacity) { Accelerator accelerator = Accelerator; @@ -159,40 +219,76 @@ public sealed class PrimeOrderCalculatorAccelerator if (pow2ModEntriesToTestOnDevice.Length < factorsCount) { // lock (accelerator) - { + // { pow2ModEntriesToTestOnDevice.Dispose(); - Pow2ModEntriesToTestOnDevice = accelerator.Allocate1D>(factorsCount); - } + pow2ModEntriesToTestOnDevice = accelerator.Allocate1D>(factorsCount); + Pow2ModEntriesToTestOnDevice = pow2ModEntriesToTestOnDevice; + Pow2ModEntriesToTestOnDeviceView = pow2ModEntriesToTestOnDevice.View; + // } } MemoryBuffer1D input = Input; if (input.Length < primeTesterCapacity) { // lock (accelerator) - { + // { input.Dispose(); - Input = accelerator.Allocate1D(primeTesterCapacity); - } + + input = accelerator.Allocate1D(primeTesterCapacity); + Input = input; + InputView = input.View; + // } } MemoryBuffer1D outputByte = OutputByte; if (outputByte.Length < primeTesterCapacity) { // lock (accelerator) - { + // { outputByte.Dispose(); - OutputByte = accelerator.Allocate1D(primeTesterCapacity); - } + + outputByte = accelerator.Allocate1D(primeTesterCapacity); + OutputByte = outputByte; + OutputByteView = outputByte.View; + // } } MemoryBuffer1D outputInt = OutputInt; if (outputInt.Length < primeTesterCapacity) { // lock (accelerator) - { + // { outputInt.Dispose(); - OutputInt = accelerator.Allocate1D(primeTesterCapacity); - } + + outputInt = accelerator.Allocate1D(primeTesterCapacity); + OutputInt = outputInt; + OutputIntView = outputInt.View; + // } + } + } + + public void EnsurePartialFactorCapacity(int newSize) + { + var accelerator = Accelerator; + + var outputUlong = OutputUlong; + if (newSize > outputUlong.Length) + { + outputUlong.Dispose(); + + outputUlong = accelerator.Allocate1D(newSize); + OutputUlong = outputUlong; + OutputUlongView = outputUlong.View; + } + + var outputInt = OutputInt; + if (newSize > outputInt.Length) + { + outputInt.Dispose(); + + outputInt = accelerator.Allocate1D(newSize); + OutputInt = outputInt; + OutputIntView = outputInt.View; } } @@ -204,44 +300,56 @@ public sealed class PrimeOrderCalculatorAccelerator if (outputUlong.Length < newSize) { // lock (accelerator) - { + // { outputUlong.Dispose(); - OutputUlong = accelerator.Allocate1D(newSize); - } + + outputUlong = accelerator.Allocate1D(newSize); + OutputUlong = outputUlong; + OutputUlongView = outputUlong.View; + // } } MemoryBuffer1D outputInt = OutputInt; if (outputInt.Length < newSize) { // lock (accelerator) - { + // { outputInt.Dispose(); - OutputInt = accelerator.Allocate1D(newSize); - } + + outputInt = accelerator.Allocate1D(newSize); + OutputInt = outputInt; + OutputIntView = outputInt.View; + // } } } - public void EnsureSpecialMaxFactorsCapacity(int newSize) + public void EnsureUlongInputOutputCapacity(int newSize) { var accelerator = Accelerator; - MemoryBuffer1D input = Input; - if (input.Length < newSize) + MemoryBuffer1D buffer = Input; + if (buffer.Length < newSize) { // lock (accelerator) - { - input.Dispose(); - Input = accelerator.Allocate1D(newSize); - } + // { + buffer.Dispose(); + + buffer = accelerator.Allocate1D(newSize); + Input = buffer; + InputView = buffer.View; + // } } - MemoryBuffer1D outputUlong = OutputUlong; - if (outputUlong.Length < newSize) + buffer = OutputUlong; + if (buffer.Length < newSize) { // lock (accelerator) - { - outputUlong.Dispose(); - OutputUlong = accelerator.Allocate1D(newSize); - } + // { + buffer.Dispose(); + + buffer = accelerator.Allocate1D(newSize); + OutputUlong = buffer; + OutputUlongView = buffer.View; + // } } } @@ -250,15 +358,59 @@ public sealed class PrimeOrderCalculatorAccelerator var accelerator = _accelerators[acceleratorIndex]; Accelerator = accelerator; AcceleratorIndex = acceleratorIndex; + // lock (accelerator) - { - Input = accelerator.Allocate1D(Math.Max(specialMaxFactorCapacity, primeTesterCapacity)); + // { + Input = accelerator.Allocate1D(Math.Max(Math.Max(specialMaxFactorCapacity, primeTesterCapacity), PrimeOrderConstants.MaxGpuBatchSize)); + InputView = Input.View; + OutputByte = Accelerator.Allocate1D(primeTesterCapacity); - OutputInt = Accelerator.Allocate1D(Math.Max(primeTesterCapacity, smallPrimeFactorSlotCount)); - OutputUlong = accelerator.Allocate1D(Math.Max(specialMaxFactorCapacity, smallPrimeFactorSlotCount)); + OutputByteView = OutputByte.View; + + OutputInt = Accelerator.Allocate1D(Math.Max(Math.Max(primeTesterCapacity, smallPrimeFactorSlotCount), PrimeOrderConstants.GpuSmallPrimeFactorSlots)); + OutputIntView = OutputInt.View; + + OutputInt2 = accelerator.Allocate1D(PrimeOrderConstants.GpuSmallPrimeFactorSlots); + OutputIntView2 = OutputInt2.View; + + OutputUlong = accelerator.Allocate1D(Math.Max(Math.Max(Math.Max(specialMaxFactorCapacity, smallPrimeFactorSlotCount), PrimeOrderConstants.MaxGpuBatchSize), PrimeOrderConstants.GpuSmallPrimeFactorSlots)); + OutputUlongView = OutputUlong.View; + + OutputUlong2 = accelerator.Allocate1D(1); + OutputUlongView2 = OutputUlong2.View; + Pow2ModEntriesToTestOnDevice = accelerator.Allocate1D>(factorsCount); - SmallPrimeFactorCountSlot = accelerator.Allocate1D(1); - SpecialMaxResult = accelerator.Allocate1D(1); + Pow2ModEntriesToTestOnDeviceView = Pow2ModEntriesToTestOnDevice.View; + + WorkFactorBuffer = accelerator.Allocate1D(PrimeOrderConstants.GpuSmallPrimeFactorSlots); + CandidateBuffer = accelerator.Allocate1D(PrimeOrderConstants.HeuristicCandidateLimit); + StackIndexBuffer = accelerator.Allocate1D(PrimeOrderConstants.HeuristicStackCapacity); + StackExponentBuffer = accelerator.Allocate1D(PrimeOrderConstants.HeuristicStackCapacity); + StackProductBuffer = accelerator.Allocate1D(PrimeOrderConstants.HeuristicStackCapacity); + + WorkFactorBufferView = WorkFactorBuffer.View; + CandidateBufferView = CandidateBuffer.View; + StackIndexBufferView = StackIndexBuffer.View; + StackExponentBufferView = StackExponentBuffer.View; + StackProductBufferView = StackProductBuffer.View; + + CalculateOrderKernelBuffers = new OrderKernelBuffers( + OutputUlongView, + OutputIntView, + WorkFactorBufferView, + OutputIntView2, + CandidateBufferView, + StackIndexBufferView, + StackExponentBufferView, + StackProductBufferView, + OutputUlongView2, + OutputByteView + ); + + CalculateOrderWideExponentBuffer = accelerator.Allocate1D(PrimeOrderConstants.MaxGpuBatchSize); + CalculateOrderWideRemainderBuffer = accelerator.Allocate1D(PrimeOrderConstants.MaxGpuBatchSize); + CalculateOrderWideExponentBufferView = CalculateOrderWideExponentBuffer.View; + CalculateOrderWideRemainderBufferView = CalculateOrderWideRemainderBuffer.View; LastDigitGpuTables lastDigitSharedTables = LastDigitGpuTables.GetStaticTables(acceleratorIndex); DevicePrimesLastOne = lastDigitSharedTables.DevicePrimesLastOne.View; @@ -285,17 +437,20 @@ public sealed class PrimeOrderCalculatorAccelerator var kernels = _kernels[acceleratorIndex]; + CalculateOrderKernelLauncher = kernels.CalculateOrderKernelLauncher; CheckFactorsKernel = kernels.CheckFactorsKernel; ConvertToStandardKernelLauncher = kernels.ConvertToStandardKernelLauncher; HeuristicCombinedTrialDivisionKernelLauncher = kernels.HeuristicCombinedTrialDivisionKernelLauncher; KeepMontgomeryKernelLauncher = kernels.KeepMontgomeryKernelLauncher; + PartialFactorKernelLauncher = kernels.PartialFactorKernelLauncher; PollardRhoKernel = kernels.PollardRhoKernel; + Pow2ModKernelLauncher = kernels.Pow2ModKernelLauncher; Pow2ModWideKernelLauncher = kernels.Pow2ModWideKernelLauncher; SharesFactorKernelLauncher = kernels.SharesFactorKernelLauncher; SmallPrimeFactorKernelLauncher = kernels.SmallPrimeFactorKernelLauncher; SmallPrimeSieveKernelLauncher = kernels.SmallPrimeSieveKernelLauncher; SpecialMaxKernelLauncher = kernels.SpecialMaxKernelLauncher; - } + // } } public void Dispose() @@ -303,10 +458,20 @@ public sealed class PrimeOrderCalculatorAccelerator Input.Dispose(); OutputByte.Dispose(); OutputInt.Dispose(); + OutputInt2.Dispose(); OutputUlong.Dispose(); + OutputUlong2.Dispose(); + Pow2ModEntriesToTestOnDevice.Dispose(); - SmallPrimeFactorCountSlot.Dispose(); - SpecialMaxResult.Dispose(); + + WorkFactorBuffer.Dispose(); + CandidateBuffer.Dispose(); + StackIndexBuffer.Dispose(); + StackExponentBuffer.Dispose(); + StackProductBuffer.Dispose(); + + CalculateOrderWideExponentBuffer.Dispose(); + CalculateOrderWideRemainderBuffer.Dispose(); // These resources are shared between GPU leases // Stream.Dispose(); diff --git a/PerfectNumbers.Core/Gpu/CalculateOrderKernelConfig.cs b/PerfectNumbers.Core/Gpu/CalculateOrderKernelConfig.cs new file mode 100644 index 0000000..1f87ff8 --- /dev/null +++ b/PerfectNumbers.Core/Gpu/CalculateOrderKernelConfig.cs @@ -0,0 +1,10 @@ +namespace PerfectNumbers.Core.Gpu; + +public readonly struct CalculateOrderKernelConfig(ulong previousOrder, byte hasPreviousOrder, uint smallFactorLimit, int maxPowChecks, int mode) +{ + public readonly ulong PreviousOrder = previousOrder; + public readonly byte HasPreviousOrder = hasPreviousOrder; + public readonly uint SmallFactorLimit = smallFactorLimit; + public readonly int MaxPowChecks = maxPowChecks; + public readonly int Mode = mode; +} diff --git a/PerfectNumbers.Core/Gpu/Kernels/PrimeOrderKernelStatus.cs b/PerfectNumbers.Core/Gpu/Kernels/PrimeOrderKernelStatus.cs new file mode 100644 index 0000000..9ebfd96 --- /dev/null +++ b/PerfectNumbers.Core/Gpu/Kernels/PrimeOrderKernelStatus.cs @@ -0,0 +1,10 @@ +namespace PerfectNumbers.Core.Gpu.Kernels; + +public enum PrimeOrderKernelStatus : byte +{ + Fallback = 0, + Found = 1, + HeuristicUnresolved = 2, + PollardOverflow = 3, + FactoringFailure = 4, +} diff --git a/PerfectNumbers.Core/Gpu/Kernels/PrimeOrderKernels.cs b/PerfectNumbers.Core/Gpu/Kernels/PrimeOrderKernels.cs index 19c3ba1..951153d 100644 --- a/PerfectNumbers.Core/Gpu/Kernels/PrimeOrderKernels.cs +++ b/PerfectNumbers.Core/Gpu/Kernels/PrimeOrderKernels.cs @@ -6,7 +6,7 @@ using PerfectNumbers.Core.Gpu.Kernels; namespace PerfectNumbers.Core.Gpu; -internal static partial class PrimeOrderGpuHeuristics +internal static partial class PrimeOrderKernels { internal static void CheckFactorsKernel( int count, @@ -115,15 +115,6 @@ internal static partial class PrimeOrderGpuHeuristics fullyFactoredOut[0] = remainingLocal == 1UL ? (byte)1 : (byte)0; } - internal enum PrimeOrderKernelStatus : byte - { - Fallback = 0, - Found = 1, - HeuristicUnresolved = 2, - PollardOverflow = 3, - FactoringFailure = 4, - } - private readonly struct CandidateKey { public CandidateKey(int primary, long secondary, long tertiary) @@ -160,7 +151,7 @@ internal static partial class PrimeOrderGpuHeuristics internal static void CalculateOrderKernel( Index1D index, ulong prime, - OrderKernelConfig config, + CalculateOrderKernelConfig config, ulong divisorModulus, ulong divisorNPrime, ulong divisorMontgomeryOne, @@ -189,7 +180,7 @@ internal static partial class PrimeOrderGpuHeuristics ArrayView1D resultOut = buffers.Result; ArrayView1D statusOut = buffers.Status; - uint limit = config.SmallFactorLimit == 0 ? uint.MaxValue : config.SmallFactorLimit; + uint limit = config.SmallFactorLimit; ulong previousOrder = config.PreviousOrder; byte hasPreviousOrder = config.HasPreviousOrder; int maxPowChecks = config.MaxPowChecks; @@ -589,7 +580,7 @@ internal static partial class PrimeOrderGpuHeuristics SortFactors(workFactors, workExponents, factorCount); long candidateCapacity = candidates.Length; - int candidateLimit = candidateCapacity < HeuristicCandidateLimit ? (int)candidateCapacity : HeuristicCandidateLimit; + int candidateLimit = candidateCapacity < PrimeOrderConstants.HeuristicCandidateLimit ? (int)candidateCapacity : PrimeOrderConstants.HeuristicCandidateLimit; int candidateCount = BuildCandidatesKernel(order, workFactors, workExponents, factorCount, candidates, stackIndex, stackExponent, stackProduct, candidateLimit); if (candidateCount == 0) { @@ -1328,11 +1319,11 @@ internal static partial class PrimeOrderGpuHeuristics } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool ShouldUseSingleBit(GpuUInt128 exponent) => exponent.High == 0UL && exponent.Low <= Pow2WindowFallbackThreshold; + private static bool ShouldUseSingleBit(GpuUInt128 exponent) => exponent.High == 0UL && exponent.Low <= PrimeOrderConstants.Pow2WindowFallbackThreshold; private static int GetWindowSize(int bitLength) { - if (bitLength <= Pow2WindowSizeBits) + if (bitLength <= PrimeOrderConstants.Pow2WindowSizeBits) { return Math.Max(bitLength, 1); } @@ -1357,7 +1348,7 @@ internal static partial class PrimeOrderGpuHeuristics return 7; } - return Pow2WindowSizeBits; + return PrimeOrderConstants.Pow2WindowSizeBits; } private static GpuUInt128 Pow2MontgomeryModSingleBit(GpuUInt128 exponent, GpuUInt128 modulus, GpuUInt128 baseValue) diff --git a/PerfectNumbers.Core/Gpu/OrderKernelBuffers.cs b/PerfectNumbers.Core/Gpu/OrderKernelBuffers.cs new file mode 100644 index 0000000..50fe5c8 --- /dev/null +++ b/PerfectNumbers.Core/Gpu/OrderKernelBuffers.cs @@ -0,0 +1,28 @@ +using ILGPU; +using ILGPU.Runtime; + +namespace PerfectNumbers.Core.Gpu; + +public readonly struct OrderKernelBuffers( + ArrayView1D phiFactors, + ArrayView1D phiExponents, + ArrayView1D workFactors, + ArrayView1D workExponents, + ArrayView1D candidates, + ArrayView1D stackIndex, + ArrayView1D stackExponent, + ArrayView1D stackProduct, + ArrayView1D result, + ArrayView1D status) +{ + public readonly ArrayView1D PhiFactors = phiFactors; + public readonly ArrayView1D PhiExponents = phiExponents; + public readonly ArrayView1D WorkFactors = workFactors; + public readonly ArrayView1D WorkExponents = workExponents; + public readonly ArrayView1D Candidates = candidates; + public readonly ArrayView1D StackIndex = stackIndex; + public readonly ArrayView1D StackExponent = stackExponent; + public readonly ArrayView1D StackProduct = stackProduct; + public readonly ArrayView1D Result = result; + public readonly ArrayView1D Status = status; +} diff --git a/PerfectNumbers.Core/Gpu/Pow2MontgomeryGpuCalculator.cs b/PerfectNumbers.Core/Gpu/Pow2MontgomeryGpuCalculator.cs index 95fe1d4..da85329 100644 --- a/PerfectNumbers.Core/Gpu/Pow2MontgomeryGpuCalculator.cs +++ b/PerfectNumbers.Core/Gpu/Pow2MontgomeryGpuCalculator.cs @@ -15,18 +15,18 @@ public static class Pow2MontgomeryGpuCalculator ulong result = 0UL; int acceleratorIndex = gpu.AcceleratorIndex; Accelerator? accelerator = gpu.Accelerator; - MemoryBuffer1D exponentBuffer = gpu.Input; - MemoryBuffer1D resultBuffer = gpu.OutputUlong; + var exponentBufferView = gpu.InputView; + var resultBufferView = gpu.OutputUlongView; AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); - exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1); + exponentBufferView.CopyFromCPU(stream, ref exponent, 1); // We don't need to worry about any left-overs here. // resultBuffer.MemSetToZero(stream); var kernelLauncher = gpu.KeepMontgomeryKernelLauncher; - kernelLauncher(stream, 1, exponentBuffer.View, divisor.Modulus, divisor.NPrime, divisor.MontgomeryOne, divisor.MontgomeryTwo, divisor.MontgomeryTwoSquared, resultBuffer.View); + kernelLauncher(stream, 1, exponentBufferView, divisor.Modulus, divisor.NPrime, divisor.MontgomeryOne, divisor.MontgomeryTwo, divisor.MontgomeryTwoSquared, resultBufferView); - resultBuffer.View.CopyToCPU(stream, ref result, 1); + resultBufferView.CopyToCPU(stream, ref result, 1); stream.Synchronize(); AcceleratorStreamPool.Return(acceleratorIndex, stream); // GpuPrimeWorkLimiter.Release(); @@ -38,21 +38,21 @@ public static class Pow2MontgomeryGpuCalculator public static ulong CalculateConvert(PrimeOrderCalculatorAccelerator gpu, ulong exponent, in MontgomeryDivisorData divisor) { ulong result = 0UL; - MemoryBuffer1D exponentBuffer = gpu.Input; - MemoryBuffer1D resultBuffer = gpu.OutputUlong; + var exponentBufferView = gpu.InputView; + var resultBufferView = gpu.OutputUlongView; int acceleratorIndex = gpu.AcceleratorIndex; AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); - exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1); + exponentBufferView.CopyFromCPU(stream, ref exponent, 1); // We don't need to worry about any left-overs here. // resultBuffer.MemSetToZero(stream); var accelerator = gpu.Accelerator; var kernelLauncher = gpu.ConvertToStandardKernelLauncher; - kernelLauncher(stream, 1, exponentBuffer.View, divisor.Modulus, divisor.NPrime, divisor.MontgomeryOne, divisor.MontgomeryTwo, divisor.MontgomeryTwoSquared, resultBuffer.View); + kernelLauncher(stream, 1, exponentBufferView, divisor.Modulus, divisor.NPrime, divisor.MontgomeryOne, divisor.MontgomeryTwo, divisor.MontgomeryTwoSquared, resultBufferView); - resultBuffer.View.CopyToCPU(stream, ref result, 1); + resultBufferView.CopyToCPU(stream, ref result, 1); stream.Synchronize(); AcceleratorStreamPool.Return(acceleratorIndex, stream); diff --git a/PerfectNumbers.Core/Gpu/PrimeOrderConstants.cs b/PerfectNumbers.Core/Gpu/PrimeOrderConstants.cs new file mode 100644 index 0000000..1bb8678 --- /dev/null +++ b/PerfectNumbers.Core/Gpu/PrimeOrderConstants.cs @@ -0,0 +1,14 @@ +namespace PerfectNumbers.Core.Gpu; + +public static class PrimeOrderConstants +{ + public const int GpuSmallPrimeFactorSlots = 64; + public const int HeuristicCandidateLimit = 512; + public const int HeuristicStackCapacity = 256; + public const int MaxGpuBatchSize = 256; + public const ulong Pow2WindowFallbackThreshold = 32UL; + public const int Pow2WindowOddPowerCount = 1 << (Pow2WindowSizeBits - 1); + public const int Pow2WindowSizeBits = 8; + public const int WideStackThreshold = 12; + +} diff --git a/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs b/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs index f450593..39ea7f6 100644 --- a/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs +++ b/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs @@ -1,9 +1,7 @@ using System.Buffers; using System.Collections.Concurrent; using System.Runtime.InteropServices; -using ILGPU; using ILGPU.Runtime; -using ILGPU.Runtime.OpenCL; using PerfectNumbers.Core.Gpu.Accelerators; using PerfectNumbers.Core.Gpu.Kernels; @@ -26,58 +24,9 @@ internal static partial class PrimeOrderGpuHeuristics private static readonly ConcurrentDictionary OverflowedPrimes = new(); private static readonly ConcurrentDictionary OverflowedPrimesWide = new(); - [ThreadStatic] - private static Kernel[]? _pow2ModKernel; - - [ThreadStatic] - private static Kernel[]? _partialFactorKernel; - - [ThreadStatic] - private static Kernel[]? _orderKernel; - - public readonly struct OrderKernelConfig(ulong previousOrder, byte hasPreviousOrder, uint smallFactorLimit, int maxPowChecks, int mode) - { - public readonly ulong PreviousOrder = previousOrder; - public readonly byte HasPreviousOrder = hasPreviousOrder; - public readonly uint SmallFactorLimit = smallFactorLimit; - public readonly int MaxPowChecks = maxPowChecks; - public readonly int Mode = mode; - } - - public readonly struct OrderKernelBuffers( - ArrayView1D phiFactors, - ArrayView1D phiExponents, - ArrayView1D workFactors, - ArrayView1D workExponents, - ArrayView1D candidates, - ArrayView1D stackIndex, - ArrayView1D stackExponent, - ArrayView1D stackProduct, - ArrayView1D result, - ArrayView1D status) - { - public readonly ArrayView1D PhiFactors = phiFactors; - public readonly ArrayView1D PhiExponents = phiExponents; - public readonly ArrayView1D WorkFactors = workFactors; - public readonly ArrayView1D WorkExponents = workExponents; - public readonly ArrayView1D Candidates = candidates; - public readonly ArrayView1D StackIndex = stackIndex; - public readonly ArrayView1D StackExponent = stackExponent; - public readonly ArrayView1D StackProduct = stackProduct; - public readonly ArrayView1D Result = result; - public readonly ArrayView1D Status = status; - } - - private const int WideStackThreshold = 12; private static PrimeOrderGpuCapability s_capability = PrimeOrderGpuCapability.Default; - private const int Pow2WindowSizeBits = 8; - private const int Pow2WindowOddPowerCount = 1 << (Pow2WindowSizeBits - 1); - private const ulong Pow2WindowFallbackThreshold = 32UL; - private const int HeuristicCandidateLimit = 512; - private const int HeuristicStackCapacity = 256; - private const int GpuSmallPrimeFactorSlots = 64; internal static ConcurrentDictionary OverflowRegistry => OverflowedPrimes; internal static ConcurrentDictionary OverflowRegistryWide => OverflowedPrimesWide; @@ -92,22 +41,6 @@ internal static partial class PrimeOrderGpuHeuristics s_capability = PrimeOrderGpuCapability.Default; } - private static Kernel GetPartialFactorKernel(int acceleratorIndex) - { - var pool = _partialFactorKernel ??= []; - if (pool[acceleratorIndex] is {} cached) - { - return cached; - } - - var accelerator = _accelerators[acceleratorIndex]; - var loaded = accelerator.LoadAutoGroupedStreamKernel, ArrayView1D, int, int, ulong, uint, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>(PartialFactorKernel); - - cached = KernelUtil.GetKernel(loaded); - pool[acceleratorIndex] = cached; - return cached; - } - public static bool TryPartialFactor( PrimeOrderCalculatorAccelerator gpu, ulong value, @@ -167,94 +100,74 @@ internal static partial class PrimeOrderGpuHeuristics factorCount = 0; remaining = value; - try - { - // GpuPrimeWorkLimiter.Acquire(); - int acceleratorIndex = AcceleratorPool.Shared.Rent(); - Accelerator accelerator = _accelerators[acceleratorIndex]; - AcceleratorStream stream = accelerator.CreateStream(); + // GpuPrimeWorkLimiter.Acquire(); + int acceleratorIndex = gpu.AcceleratorIndex; + var smallPrimesView = gpu.SmallPrimeFactorPrimes; + var smallSquaresView = gpu.SmallPrimeFactorSquares; - var smallPrimesView = gpu.SmallPrimeFactorPrimes; - var smallSquaresView = gpu.SmallPrimeFactorSquares; + var kernelLauncher = gpu.PartialFactorKernelLauncher; - // TODO: We should create / reallocate these buffer only once or if the new required length is bigger than capacity. - MemoryBuffer1D? factorBuffer = accelerator.Allocate1D(primeTargets.Length); - MemoryBuffer1D? exponentBuffer = accelerator.Allocate1D(exponentTargets.Length); - MemoryBuffer1D? countBuffer = accelerator.Allocate1D(1); - MemoryBuffer1D? remainingBuffer = accelerator.Allocate1D(1); - MemoryBuffer1D? fullyFactoredBuffer = accelerator.Allocate1D(1); + int primeTargetsLength = primeTargets.Length; + gpu.EnsurePartialFactorCapacity(primeTargetsLength); - // There is no need to clear these buffers because the kernel will always assign values within the required bounds. Keep it commented out. - // factorBuffer.MemSetToZero(stream); - // exponentBuffer.MemSetToZero(stream); - // countBuffer.MemSetToZero(stream); - // remainingBuffer.MemSetToZero(stream); - // fullyFactoredBuffer.MemSetToZero(stream); + var factorBufferView = gpu.OutputUlongView; + var exponentBufferView = gpu.OutputIntView; + var countBufferView = gpu.OutputIntView2; + var remainingBufferView = gpu.OutputUlongView2; + var fullyFactoredBufferView = gpu.OutputByteView; - var partialFactorKernel = GetPartialFactorKernel(acceleratorIndex); - var kernelLauncher = partialFactorKernel.CreateLauncherDelegate, ArrayView1D, int, int, ulong, uint, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>>(); + // There is no need to clear these buffers because the kernel will always assign values within the required bounds. Keep it commented out. + // factorBufferView.MemSetToZero(stream); + // exponentBufferView.MemSetToZero(stream); + // countBufferView.MemSetToZero(stream); + // remainingBufferView.MemSetToZero(stream); + // fullyFactoredBufferView.MemSetToZero(stream); - kernelLauncher( - stream, - 1, - smallPrimesView, - smallSquaresView, - (int)smallPrimesView.Length, - primeTargets.Length, - value, - limit, - factorBuffer.View, - exponentBuffer.View, - countBuffer.View, - remainingBuffer.View, - fullyFactoredBuffer.View); + AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); + kernelLauncher( + stream, + 1, + smallPrimesView, + smallSquaresView, + (int)smallPrimesView.Length, + primeTargetsLength, + value, + limit, + factorBufferView, + exponentBufferView, + countBufferView, + remainingBufferView, + fullyFactoredBufferView); - countBuffer.View.CopyToCPU(stream, ref factorCount, 1); - factorBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(primeTargets), primeTargets.Length); - exponentBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(exponentTargets), exponentTargets.Length); - remainingBuffer.View.CopyToCPU(stream, ref remaining, 1); + byte fullyFactoredFlag = 0; + factorBufferView.CopyToCPU(stream, primeTargets); + exponentBufferView.CopyToCPU(stream, exponentTargets); + countBufferView.CopyToCPU(stream, ref factorCount, 1); + remainingBufferView.CopyToCPU(stream, ref remaining, 1); + fullyFactoredBufferView.CopyToCPU(stream, ref fullyFactoredFlag, 1); - byte fullyFactoredFlag = 0; - fullyFactoredBuffer.View.CopyToCPU(stream, ref fullyFactoredFlag, 1); - stream.Synchronize(); - stream.Dispose(); + stream.Synchronize(); + AcceleratorStreamPool.Return(acceleratorIndex, stream); - factorCount = Math.Min(factorCount, primeTargets.Length); + factorCount = Math.Min(factorCount, primeTargetsLength); - // TODO: These buffers shouldn't be disposed but rather reused accross call with the assigned accelerator. - factorBuffer.Dispose(); - exponentBuffer.Dispose(); - countBuffer.Dispose(); - remainingBuffer.Dispose(); - fullyFactoredBuffer.Dispose(); - // GpuPrimeWorkLimiter.Release(); - return true; - } - catch (CLException ex) - { - Console.WriteLine($"GPU ERROR ({ex.Error}): {ex.Message}"); - throw; - } - catch (Exception ex) when (ex is AcceleratorException or InternalCompilerException or NotSupportedException or InvalidOperationException or AggregateException) - { - Console.WriteLine($"GPU ERROR: {ex.Message}"); - throw; - } + // GpuPrimeWorkLimiter.Release(); + return true; } - public static GpuPow2ModStatus TryPow2Mod(ulong exponent, ulong prime, out ulong remainder, in MontgomeryDivisorData? divisorData) + public static GpuPow2ModStatus TryPow2Mod(PrimeOrderCalculatorAccelerator gpu, ulong exponent, ulong prime, out ulong remainder, in MontgomeryDivisorData? divisorData) { Span exponents = stackalloc ulong[1]; Span remainders = stackalloc ulong[1]; exponents[0] = exponent; - GpuPow2ModStatus status = TryPow2ModBatch(exponents, prime, remainders, divisorData); + GpuPow2ModStatus status = TryPow2ModBatch(gpu, exponents, prime, remainders, divisorData); remainder = remainders[0]; return status; } - public static GpuPow2ModStatus TryPow2ModBatch(ReadOnlySpan exponents, ulong prime, Span remainders, in MontgomeryDivisorData? divisorData) + public static GpuPow2ModStatus TryPow2ModBatch(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan exponents, ulong prime, Span remainders, in MontgomeryDivisorData? divisorData) { // This will never occur in production code // if (exponents.Length == 0) @@ -305,7 +218,7 @@ internal static partial class PrimeOrderGpuHeuristics } } - bool computed = TryComputeOnGpu(exponents, prime, divisorData, remainders); + bool computed = TryComputeOnGpu(gpu, exponents, prime, divisorData, remainders); return computed ? GpuPow2ModStatus.Success : GpuPow2ModStatus.Unavailable; } @@ -336,71 +249,41 @@ internal static partial class PrimeOrderGpuHeuristics order = 0UL; // GpuPrimeWorkLimiter.Acquire(); - int acceleratorIndex = AcceleratorPool.Shared.Rent(); - Accelerator accelerator = _accelerators[acceleratorIndex]; - AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); + int acceleratorIndex = gpu.AcceleratorIndex; var smallPrimesView = gpu.SmallPrimeFactorPrimes; var smallSquaresView = gpu.SmallPrimeFactorSquares; - - // TODO: These buffers should be created reallocated once and assigned to an accelerator. Callers should use their own - // thread static cache to prevent other threads from using taking accelerators with pre-allocated buffers if they don't - // use them. - MemoryBuffer1D? phiFactorBuffer; - MemoryBuffer1D? phiExponentBuffer; - MemoryBuffer1D? workFactorBuffer; - MemoryBuffer1D? workExponentBuffer; - MemoryBuffer1D? candidateBuffer; - MemoryBuffer1D? stackIndexBuffer; - MemoryBuffer1D? stackExponentBuffer; - MemoryBuffer1D? stackProductBuffer; - MemoryBuffer1D? resultBuffer; - MemoryBuffer1D? statusBuffer; - - // lock (accelerator) - { - phiFactorBuffer = accelerator.Allocate1D(GpuSmallPrimeFactorSlots); - phiExponentBuffer = accelerator.Allocate1D(GpuSmallPrimeFactorSlots); - workFactorBuffer = accelerator.Allocate1D(GpuSmallPrimeFactorSlots); - workExponentBuffer = accelerator.Allocate1D(GpuSmallPrimeFactorSlots); - candidateBuffer = accelerator.Allocate1D(HeuristicCandidateLimit); - stackIndexBuffer = accelerator.Allocate1D(HeuristicStackCapacity); - stackExponentBuffer = accelerator.Allocate1D(HeuristicStackCapacity); - stackProductBuffer = accelerator.Allocate1D(HeuristicStackCapacity); - resultBuffer = accelerator.Allocate1D(1); - statusBuffer = accelerator.Allocate1D(1); - } - - // TODO: Remove the cleaning after the order kernel is modified to always set the result. - phiFactorBuffer.MemSetToZero(stream); - phiExponentBuffer.MemSetToZero(stream); - workFactorBuffer.MemSetToZero(stream); - workExponentBuffer.MemSetToZero(stream); - candidateBuffer.MemSetToZero(stream); - stackIndexBuffer.MemSetToZero(stream); - stackExponentBuffer.MemSetToZero(stream); - stackProductBuffer.MemSetToZero(stream); - resultBuffer.MemSetToZero(stream); - statusBuffer.MemSetToZero(stream); + // var phiFactorBufferView = gpu.PhiFactorBufferView; + // var phiExponentBufferView = gpu.PhiExponentBufferView; + // var workFactorBufferView = gpu.WorkFactorBufferView; + // var workExponentBufferView = gpu.WorkExponentBufferView; + // var candidateBufferView = gpu.CandidateBufferView; + // var stackIndexBufferView = gpu.StackIndexBufferView; + // var stackExponentBufferView = gpu.StackExponentBufferView; + // var stackProductBufferView = gpu.StackProductBufferView; + var resultBufferView = gpu.OutputUlongView2; + var statusBufferView = gpu.OutputByteView; uint limit = config.SmallFactorLimit == 0 ? uint.MaxValue : config.SmallFactorLimit; byte hasPrevious = previousOrder.HasValue ? (byte)1 : (byte)0; ulong previousValue = previousOrder ?? 0UL; - var kernelConfig = new OrderKernelConfig(previousValue, hasPrevious, limit, config.MaxPowChecks, (int)config.Mode); - var buffers = new OrderKernelBuffers( - phiFactorBuffer.View, - phiExponentBuffer.View, - workFactorBuffer.View, - workExponentBuffer.View, - candidateBuffer.View, - stackIndexBuffer.View, - stackExponentBuffer.View, - stackProductBuffer.View, - resultBuffer.View, - statusBuffer.View); + var kernelConfig = new CalculateOrderKernelConfig(previousValue, hasPrevious, limit, config.MaxPowChecks, (int)config.Mode); + ref var buffers = ref gpu.CalculateOrderKernelBuffers; - var orderKernel = GetOrderKernel(acceleratorIndex); - var kernelLauncher = orderKernel.CreateLauncherDelegate, ArrayView1D, int, OrderKernelBuffers>>(); + var kernelLauncher = gpu.CalculateOrderKernelLauncher; + + AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); + // TODO: Remove the cleaning after the order kernel is modified to always set the result. + // phiFactorBufferView.MemSetToZero(stream); + // phiExponentBufferView.MemSetToZero(stream); + // workFactorBufferView.MemSetToZero(stream); + // workExponentBufferView.MemSetToZero(stream); + // candidateBufferView.MemSetToZero(stream); + // stackIndexBufferView.MemSetToZero(stream); + // stackExponentBufferView.MemSetToZero(stream); + // stackProductBufferView.MemSetToZero(stream); + resultBufferView.MemSetToZero(stream); + statusBufferView.MemSetToZero(stream); kernelLauncher( stream, @@ -419,21 +302,11 @@ internal static partial class PrimeOrderGpuHeuristics byte status = 0; - statusBuffer.View.CopyToCPU(stream, ref status, 1); - resultBuffer.View.CopyToCPU(stream, ref order, 1); + statusBufferView.CopyToCPU(stream, ref status, 1); + resultBufferView.CopyToCPU(stream, ref order, 1); stream.Synchronize(); AcceleratorStreamPool.Return(acceleratorIndex, stream); - phiFactorBuffer.Dispose(); - phiExponentBuffer.Dispose(); - workFactorBuffer.Dispose(); - workExponentBuffer.Dispose(); - candidateBuffer.Dispose(); - stackIndexBuffer.Dispose(); - stackExponentBuffer.Dispose(); - stackProductBuffer.Dispose(); - resultBuffer.Dispose(); - statusBuffer.Dispose(); // GpuPrimeWorkLimiter.Release(); PrimeOrderKernelStatus kernelStatus = (PrimeOrderKernelStatus)status; @@ -455,22 +328,6 @@ internal static partial class PrimeOrderGpuHeuristics return order != 0UL; } - private static Kernel GetOrderKernel(int acceleratorIndex) - { - var pool = _orderKernel ??= []; - if (pool[acceleratorIndex] is {} cached) - { - return cached; - } - - var accelerator = _accelerators[acceleratorIndex]; - var loaded = accelerator.LoadAutoGroupedStreamKernel, ArrayView1D, int, OrderKernelBuffers>(CalculateOrderKernel); - - var kernel = KernelUtil.GetKernel(loaded); - pool[acceleratorIndex] = kernel; - return kernel; - } - private static GpuPow2ModStatus TryPow2ModBatchInternal(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan exponents, UInt128 prime, Span remainders) { if (exponents.Length == 0) @@ -513,58 +370,39 @@ internal static partial class PrimeOrderGpuHeuristics return GpuPow2ModStatus.Success; } - private static bool TryComputeOnGpu(ReadOnlySpan exponents, ulong prime, MontgomeryDivisorData? divisorData, Span results) + private static bool TryComputeOnGpu(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan exponents, ulong prime, MontgomeryDivisorData? divisorData, Span results) { // GpuPrimeWorkLimiter.Acquire(); - int acceleratorIndex = AcceleratorPool.Shared.Rent(); - Accelerator accelerator = _accelerators[acceleratorIndex]; - AcceleratorStream? stream = accelerator.CreateStream(); - // TODO: These buffers should be allocated once per accelerator and only reallocated when the new length exceeds capacity. + int acceleratorIndex = gpu.AcceleratorIndex; + Accelerator accelerator = gpu.Accelerator; + var kernelLauncher = gpu.Pow2ModKernelLauncher; + // Modify the callers to use their own pool of buffers per accelerator, so that other threads don't use the accelerators // with out preallocated buffers. Share the pool with Pow2ModWide kernel. - MemoryBuffer1D? exponentBuffer; - MemoryBuffer1D? remainderBuffer; - // lock (accelerator) + + gpu.EnsureUlongInputOutputCapacity(exponents.Length); + + var exponentBufferView = gpu.InputView; + var remainderBufferView = gpu.OutputUlongView; + + if (divisorData is null) { - exponentBuffer = accelerator.Allocate1D(exponents.Length); - remainderBuffer = accelerator.Allocate1D(exponents.Length); + divisorData = MontgomeryDivisorData.Empty; } - exponentBuffer.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(exponents), exponents.Length); + AcceleratorStream? stream = AcceleratorStreamPool.Rent(acceleratorIndex); + exponentBufferView.CopyFromCPU(stream, exponents); // Pow2Mod kernel always assigns the required output elements so we don't need to worry about clearing these. // remainderBuffer.MemSetToZero(stream); - var pow2ModKernel = GetPow2ModKernel(acceleratorIndex); - var kernelLauncher = pow2ModKernel.CreateLauncherDelegate, ulong, ulong, ulong, ulong, ulong, ArrayView1D>>(); + kernelLauncher(stream, exponents.Length, exponentBufferView, divisorData.Modulus, divisorData.NPrime, divisorData.MontgomeryOne, divisorData.MontgomeryTwo, divisorData.MontgomeryTwoSquared, remainderBufferView); - try - { - if (divisorData is null) - { - divisorData = MontgomeryDivisorData.Empty; - } + remainderBufferView.CopyToCPU(stream, results); + stream.Synchronize(); - kernelLauncher(stream, exponents.Length, exponentBuffer.View, divisorData.Modulus, divisorData.NPrime, divisorData.MontgomeryOne, divisorData.MontgomeryTwo, divisorData.MontgomeryTwoSquared, remainderBuffer.View); - - remainderBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(results), exponents.Length); - stream.Synchronize(); - - stream.Dispose(); - stream = null; - exponentBuffer.Dispose(); - remainderBuffer.Dispose(); - // GpuPrimeWorkLimiter.Release(); - } - catch (Exception) - { - Console.WriteLine($"Exception for {prime} and exponents: {string.Join(",", exponents.ToArray())}."); - stream?.Dispose(); - exponentBuffer.Dispose(); - remainderBuffer.Dispose(); - // GpuPrimeWorkLimiter.Release(); - throw; - } + AcceleratorStreamPool.Return(acceleratorIndex, stream); + // GpuPrimeWorkLimiter.Release(); return true; } @@ -585,11 +423,11 @@ internal static partial class PrimeOrderGpuHeuristics GpuUInt128[]? rentedExponents = null; GpuUInt128[]? rentedResults = null; ArrayPool gpuUInt128Pool = ThreadStaticPools.GpuUInt128Pool; - bool poolingRequired = length > WideStackThreshold; + bool poolingRequired = length > PrimeOrderConstants.WideStackThreshold; Span exponentSpan = poolingRequired ? stackalloc GpuUInt128[length] : new Span(rentedExponents = gpuUInt128Pool.Rent(length), 0, length); - Span resultSpan = length <= WideStackThreshold + Span resultSpan = length <= PrimeOrderConstants.WideStackThreshold ? stackalloc GpuUInt128[length] : new Span(rentedResults = gpuUInt128Pool.Rent(length), 0, length); @@ -603,32 +441,26 @@ internal static partial class PrimeOrderGpuHeuristics int acceleratorIndex = gpu.AcceleratorIndex; Accelerator accelerator = gpu.Accelerator; - AcceleratorStream? stream = AcceleratorStreamPool.Rent(acceleratorIndex); - MemoryBuffer1D exponentBuffer; - MemoryBuffer1D remainderBuffer; + gpu.EnsureCalculateOrderWideCapacity(length); - // lock (accelerator) - { - exponentBuffer = accelerator.Allocate1D(length); - remainderBuffer = accelerator.Allocate1D(length); - } + var exponentBufferView = gpu.CalculateOrderWideExponentBufferView; + var remainderBufferView = gpu.CalculateOrderWideRemainderBufferView; - exponentBuffer.View.CopyFromCPU(stream, exponentSpan); + AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); + exponentBufferView.CopyFromCPU(stream, exponentSpan); var kernelLauncher = gpu.Pow2ModWideKernelLauncher; - kernelLauncher(stream, length, exponentBuffer.View, modulus, remainderBuffer.View); - remainderBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(resultSpan), length); + kernelLauncher(stream, length, exponentBufferView, modulus, remainderBufferView); + remainderBufferView.CopyToCPU(stream, ref MemoryMarshal.GetReference(resultSpan), length); stream.Synchronize(); AcceleratorStreamPool.Return(acceleratorIndex, stream); + for (int i = 0; i < length; i++) { results[i] = (UInt128)resultSpan[i]; } - remainderBuffer.Dispose(); - exponentBuffer.Dispose(); - if (rentedExponents is not null) { gpuUInt128Pool.Return(rentedExponents, clearArray: false); @@ -655,21 +487,6 @@ internal static partial class PrimeOrderGpuHeuristics return divisorData.Pow2MontgomeryModWindowedGpuConvertToStandard(exponent); } - private static Kernel GetPow2ModKernel(int acceleratorIndex) - { - var pool = _pow2ModKernel ??= []; - if (pool[acceleratorIndex] is {} cached) - { - return cached; - } - - var accelerator = _accelerators[acceleratorIndex]; - var loaded = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, ulong, ulong, ulong, ulong, ulong, ArrayView1D>(Pow2ModKernel)); - - pool[acceleratorIndex] = loaded; - return loaded; - } - internal readonly record struct PrimeOrderGpuCapability(int ModulusBits, int ExponentBits) { public static PrimeOrderGpuCapability Default => new(128, 128); diff --git a/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs b/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs index b718628..7d0db10 100644 --- a/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs +++ b/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs @@ -225,24 +225,24 @@ public sealed class HeuristicCombinedPrimeTester private static bool HeuristicTrialDivisionGpuDetectsDivisor(PrimeOrderCalculatorAccelerator gpu, ulong n, ulong maxDivisorSquare, byte nMod10) { var acceleratorIndex = gpu.AcceleratorIndex; - var flagView1D = gpu.OutputInt!.View; + var flagView1DView = gpu.OutputIntView; + var kernelLauncher = gpu.HeuristicCombinedTrialDivisionKernelLauncher; bool compositeDetected; Span compositeFlag = stackalloc int[1]; // int compositeFlag = 0; var stream = AcceleratorStreamPool.Rent(acceleratorIndex); - flagView1D.MemSetToZero(stream); + flagView1DView.MemSetToZero(stream); - var kernelLauncher = gpu.HeuristicCombinedTrialDivisionKernelLauncher; kernelLauncher( stream, 1, nMod10, - flagView1D, + flagView1DView, n, maxDivisorSquare, gpu.DivisorTables); - flagView1D.CopyToCPU(stream, compositeFlag); + flagView1DView.CopyToCPU(stream, compositeFlag); stream.Synchronize(); AcceleratorStreamPool.Return(acceleratorIndex, stream); diff --git a/PerfectNumbers.Core/HeuristicPrimeTester.cs b/PerfectNumbers.Core/HeuristicPrimeTester.cs index 6a99a7e..e7093b6 100644 --- a/PerfectNumbers.Core/HeuristicPrimeTester.cs +++ b/PerfectNumbers.Core/HeuristicPrimeTester.cs @@ -244,12 +244,12 @@ public sealed class HeuristicPrimeTester // GpuPrimeWorkLimiter.Acquire(); int acceleratorIndex = gpu.AcceleratorIndex; var stream = AcceleratorStreamPool.Rent(acceleratorIndex); - var flagView1D = gpu.OutputInt!.View; + var flagView1DView = gpu.OutputIntView; bool compositeDetected; int compositeFlag = 0; - flagView1D.CopyFromCPU(stream, ref compositeFlag, 1); + flagView1DView.CopyFromCPU(stream, ref compositeFlag, 1); // Index1D index, // ArrayView resultFlag, @@ -263,13 +263,13 @@ public sealed class HeuristicPrimeTester stream, 1, nMod10, - flagView1D, + flagView1DView, n, maxDivisorSquare, gpu.DivisorTables); - flagView1D.CopyToCPU(stream, ref compositeFlag, 1); - stream!.Synchronize(); + flagView1DView.CopyToCPU(stream, ref compositeFlag, 1); + stream.Synchronize(); compositeDetected = compositeFlag != 0; // compositeDetected = compositeFlag != 0; diff --git a/PerfectNumbers.Core/MersenneDivisorCycles.cs b/PerfectNumbers.Core/MersenneDivisorCycles.cs index 5be42e5..c66aece 100644 --- a/PerfectNumbers.Core/MersenneDivisorCycles.cs +++ b/PerfectNumbers.Core/MersenneDivisorCycles.cs @@ -415,8 +415,9 @@ public class MersenneDivisorCycles // Console.WriteLine($"MersenneDivisorCycles.TryFactorIntoCountsInternal hits {Volatile.Read(ref _tryFactorIntoCountsInternalHits)}"); // HeuristicPrimeTester primeTester = _primeTester ??= new(); + if (HeuristicCombinedPrimeTester.IsPrimeCpu(remaining)) // if (HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, remaining)) - if (PrimeTester.IsPrime(remaining)) + // if (PrimeTester.IsPrime(remaining)) { AddFactor(counts, remaining); return true; @@ -965,7 +966,7 @@ public class MersenneDivisorCycles // HeuristicPrimeTester primeTester = _primeTester ??= new(); // if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.Exclusive.IsPrimeCpu(divisor, CancellationToken.None)) - if (!skipPrimeOrderHeuristic && PrimeTester.IsPrime(divisor)) + if (!skipPrimeOrderHeuristic && HeuristicCombinedPrimeTester.IsPrimeCpu(divisor)) // if (!skipPrimeOrderHeuristic && HeuristicCombinedPrimeTester.IsPrimeGpu(divisor)) { ulong computedOrder = PrimeOrderCalculator.Calculate( diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs index 53b4e08..42e1334 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs @@ -54,7 +54,8 @@ internal static partial class PrimeOrderCalculator ulong phi = prime - 1UL; - if (IsGpuHeuristicDevice && PrimeOrderGpuHeuristics.TryCalculateOrder(gpu, prime, previousOrder, config, divisorData, out ulong gpuOrder)) + if (PrimeOrderGpuHeuristics.TryCalculateOrder(gpu, prime, previousOrder, config, divisorData, out ulong gpuOrder)) + // if (IsGpuHeuristicDevice && PrimeOrderGpuHeuristics.TryCalculateOrder(gpu, prime, previousOrder, config, divisorData, out ulong gpuOrder)) { return gpuOrder; } @@ -567,7 +568,6 @@ internal static partial class PrimeOrderCalculator // DebugLog(() => $"Checking candidates ({candidateCount} candidates, {powBudget} pow budget)"); int index = 0; - const int MaxGpuBatchSize = 256; const int StackGpuBatchSize = 64; Span stackGpuRemainders = stackalloc ulong[StackGpuBatchSize]; ArrayPool pool = ThreadStaticPools.UlongPool; @@ -575,7 +575,7 @@ internal static partial class PrimeOrderCalculator { int remaining = candidateCount - index; int budgetRemaining = powBudget - powUsed; - int batchSize = Math.Min(remaining, Math.Min(budgetRemaining, MaxGpuBatchSize)); + int batchSize = Math.Min(remaining, Math.Min(budgetRemaining, PrimeOrderConstants.MaxGpuBatchSize)); if (batchSize <= 0) { break; @@ -593,7 +593,7 @@ internal static partial class PrimeOrderCalculator if (batchSize <= StackGpuBatchSize) { Span localRemainders = stackGpuRemainders[..batchSize]; - status = PrimeOrderGpuHeuristics.TryPow2ModBatch(batch, prime, localRemainders, divisorData); + status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, batch, prime, localRemainders, divisorData); if (status == GpuPow2ModStatus.Success) { gpuSuccess = true; @@ -604,7 +604,7 @@ internal static partial class PrimeOrderCalculator { gpuPool = pool.Rent(batchSize); Span pooledRemainders = gpuPool.AsSpan(0, batchSize); - status = PrimeOrderGpuHeuristics.TryPow2ModBatch(batch, prime, pooledRemainders, divisorData); + status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, batch, prime, pooledRemainders, divisorData); if (status == GpuPow2ModStatus.Success) { pooledGpuRemainders = pooledRemainders; @@ -1086,7 +1086,7 @@ internal static partial class PrimeOrderCalculator return PartialFactorResult.Empty; } - const int FactorSlotCount = GpuSmallPrimeFactorSlots; + const int FactorSlotCount = PrimeOrderConstants.GpuSmallPrimeFactorSlots; // stackalloc is faster than pooling // ulong[] primeSlotsArray = ThreadStaticPools.UlongPool.Rent(FactorSlotCount); @@ -1176,7 +1176,7 @@ internal static partial class PrimeOrderCalculator // 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 = !runOnGpu ? HeuristicCombinedPrimeTester.IsPrimeCpu(composite) : HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite); // bool isPrime = PrimeTester.IsPrime(composite); // bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite); @@ -1252,7 +1252,7 @@ internal static partial class PrimeOrderCalculator bool isPrime = RunOnGpu() ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite) - : PrimeTester.IsPrime(composite); + : HeuristicCombinedPrimeTester.IsPrimeCpu(composite); // bool isPrime = PrimeTester.IsPrime(composite); // bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite); // bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite); @@ -1297,7 +1297,7 @@ internal static partial class PrimeOrderCalculator // cofactorIsPrime = PrimeTester.IsPrimeGpu(gpu, cofactor); cofactorIsPrime = RunOnGpu() ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, cofactor) - : PrimeTester.IsPrime(cofactor); + : HeuristicCombinedPrimeTester.IsPrimeCpu(cofactor); // cofactorIsPrime = PrimeTester.IsPrime(cofactor); // cofactorIsPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(cofactor); } diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs index 1857a52..dd31835 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs @@ -36,20 +36,21 @@ internal static partial class PrimeOrderCalculator ulong order = phi; var acceleratorIndex = gpu.AcceleratorIndex; + var kernel = gpu.CheckFactorsKernel; + gpu.EnsureCapacity(entryCount, 1); + var pow2ModEntriesToTestOnDeviceView = gpu.Pow2ModEntriesToTestOnDeviceView; // GpuPrimeWorkLimiter.Acquire(); AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); - gpu.Pow2ModEntriesToTestOnDevice.View.CopyFromCPU(stream, entries); + pow2ModEntriesToTestOnDeviceView.CopyFromCPU(stream, entries); - // var kernelLauncher = gpu.CheckFactorsKernelLauncher; - var kernel = gpu.CheckFactorsKernel; - kernel.Launch(stream, 1, entryCount, phi, gpu.Pow2ModEntriesToTestOnDevice.View, divisorData.Modulus, divisorData.NPrime, divisorData.MontgomeryOne, divisorData.MontgomeryTwo, divisorData.MontgomeryTwoSquared, gpu.OutputUlong.View); + kernel.Launch(stream, 1, entryCount, phi, pow2ModEntriesToTestOnDeviceView, divisorData.Modulus, divisorData.NPrime, divisorData.MontgomeryOne, divisorData.MontgomeryTwo, divisorData.MontgomeryTwoSquared, gpu.OutputUlongView); - gpu.OutputUlong.View.CopyToCPU(stream, ref order, 1); + gpu.OutputUlongView.CopyToCPU(stream, ref order, 1); stream.Synchronize(); - AcceleratorStreamPool.Return(acceleratorIndex, stream); + AcceleratorStreamPool.Return(acceleratorIndex, stream); // GpuPrimeWorkLimiter.Release(); return order; } @@ -67,10 +68,10 @@ internal static partial class PrimeOrderCalculator var accelerator = gpu.Accelerator; var kernelLauncher = gpu.SmallPrimeFactorKernelLauncher; - ArrayView1D smallPrimeFactorCountSlotView = gpu.SmallPrimeFactorCountSlot.View; - ArrayView1D smallPrimeFactorRemainingSlotView = gpu.Input.View; - ArrayView1D smallPrimeFactorPrimeSlotsView = gpu.OutputUlong.View; - ArrayView1D smallPrimeFactorExponentSlotsView = gpu.OutputInt.View; + ArrayView1D smallPrimeFactorCountSlotView = gpu.OutputIntView2; + ArrayView1D smallPrimeFactorRemainingSlotView = gpu.InputView; + ArrayView1D smallPrimeFactorPrimeSlotsView = gpu.OutputUlongView; + ArrayView1D smallPrimeFactorExponentSlotsView = gpu.OutputIntView; ArrayView1D smallPrimeFactorsSquaresView = gpu.SmallPrimeFactorSquares; ArrayView1D smallPrimeFactorsPrimesView = gpu.SmallPrimeFactorPrimes; @@ -128,18 +129,17 @@ internal static partial class PrimeOrderCalculator in MontgomeryDivisorData divisorData) { int factorCount = factors.Length; - gpu.EnsureSpecialMaxFactorsCapacity(factorCount); + gpu.EnsureUlongInputOutputCapacity(factorCount); int acceleratorIndex = gpu.AcceleratorIndex; // GpuPrimeWorkLimiter.Acquire(); - ArrayView1D specialMaxFactorsView = gpu.Input.View; + ArrayView1D specialMaxFactorsView = gpu.InputView; + ArrayView1D specialMaxResultView = gpu.OutputUlongView2; + var kernelLauncher = gpu.SpecialMaxKernelLauncher; var stream = AcceleratorStreamPool.Rent(acceleratorIndex); specialMaxFactorsView.SubView(0, factorCount).CopyFromCPU(stream, factors); - ArrayView1D specialMaxResultView = gpu.SpecialMaxResult.View; - - var kernelLauncher = gpu.SpecialMaxKernelLauncher; kernelLauncher( stream, 1, @@ -147,7 +147,7 @@ internal static partial class PrimeOrderCalculator specialMaxFactorsView, factorCount, divisorData.Modulus, - gpu.OutputUlong.View, + gpu.OutputUlongView, specialMaxResultView); ulong result = 0UL; @@ -168,16 +168,16 @@ internal static partial class PrimeOrderCalculator Span factorSpan = stackalloc ulong[1]; int acceleratorIndex = gpu.AcceleratorIndex; - AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); - gpu.Input.View.CopyFromCPU(stream, randomStateSpan); - var kernel = gpu.PollardRhoKernel; - kernel.Launch(stream, 1, n, 1, gpu.Input.View, gpu.OutputByte.View, gpu.OutputUlong.View); + AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); + gpu.InputView.CopyFromCPU(stream, randomStateSpan); - gpu.OutputByte.View.CopyToCPU(stream, factoredSpan); - gpu.OutputUlong.View.CopyToCPU(stream, factorSpan); - gpu.Input.View.CopyToCPU(stream, randomStateSpan); + kernel.Launch(stream, 1, n, 1, gpu.InputView, gpu.OutputByteView, gpu.OutputUlongView); + + gpu.OutputByteView.CopyToCPU(stream, factoredSpan); + gpu.OutputUlongView.CopyToCPU(stream, factorSpan); + gpu.InputView.CopyToCPU(stream, randomStateSpan); stream.Synchronize(); AcceleratorStreamPool.Return(acceleratorIndex, stream); diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Shared.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Shared.cs index 951fc18..465b633 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Shared.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Shared.cs @@ -1042,7 +1042,7 @@ internal static partial class PrimeOrderCalculator { ulong prime64 = (ulong)modulus; ulong exponent64 = (ulong)exponent; - GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(exponent64, prime64, out ulong remainder, divisorData); + GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, exponent64, prime64, out ulong remainder, divisorData); if (status == GpuPow2ModStatus.Success) { return remainder; diff --git a/PerfectNumbers.Core/PrimeTester.cs b/PerfectNumbers.Core/PrimeTester.cs index 489a852..0ef6df5 100644 --- a/PerfectNumbers.Core/PrimeTester.cs +++ b/PerfectNumbers.Core/PrimeTester.cs @@ -91,10 +91,8 @@ public sealed class PrimeTester public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n) { byte flag = 0; - var input = gpu.Input; - var inputView = input.View; - var output = gpu.OutputByte; - var outputView = output.View; + var inputView = gpu.InputView; + var outputView = gpu.OutputByteView; int acceleratorIndex = gpu.AcceleratorIndex; AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); @@ -156,10 +154,8 @@ public sealed class PrimeTester int totalLength = values.Length; int batchSize = GpuBatchSize; - var input = gpu.Input; - var output = gpu.OutputByte; - var inputView = input.View; - var outputView = output.View; + var inputView = gpu.InputView; + var outputView = gpu.OutputByteView; int pos = 0; AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); @@ -235,16 +231,16 @@ public sealed class PrimeTester int acceleratorIndex = gpu.AcceleratorIndex; gpu.EnsureCapacity(0, length); - MemoryBuffer1D? inputBuffer = gpu.Input; + var inputBufferView = gpu.InputView; + var resultBufferView = gpu.OutputByteView; + var kernelLauncher = gpu.SharesFactorKernelLauncher; AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); - inputBuffer.View.CopyFromCPU(stream, values); + inputBufferView.CopyFromCPU(stream, values); - MemoryBuffer1D? resultBuffer = gpu.OutputByte; - var kernelLauncher = gpu.SharesFactorKernelLauncher; - kernelLauncher(stream, length, inputBuffer.View, resultBuffer.View); + kernelLauncher(stream, length, inputBufferView, resultBufferView); - resultBuffer.View.CopyToCPU(stream, in results); + resultBufferView.CopyToCPU(stream, in results); stream.Synchronize(); AcceleratorStreamPool.Return(acceleratorIndex, stream);