PrimeTesterByLastDigitAccelerator integrated into Pow2MontgomeryGpuCalculator for better performance.

This commit is contained in:
mlemanczyk committed 2025-11-11 13:36:21 +01:00
1 parent 0de9871529
commit cf5f78c2ef
11 files changed
+222 -241

No files matched your search

+15 -2
View File
@@ -28,7 +28,7 @@ namespace PerfectNumbers.Core.Gpu
// Intentionally left empty - there's nothing to do here. We're not really renting anything.
}
public static readonly AcceleratorPool Shared = new(PerfectNumberConstants.RollingAccelerators);
public static readonly AcceleratorPool Shared = new(1);
private readonly int Capacity;
public AcceleratorPool(int capacity)
@@ -38,7 +38,20 @@ namespace PerfectNumbers.Core.Gpu
var device = context.GetPreferredDevice(false);
Context = context;
Device = device;
Accelerators = [.. Enumerable.Range(0, capacity).Select(_ => device.CreateAccelerator(context))];
int acceleratorsInContext = 0;
Accelerators = [.. Enumerable.Range(0, capacity).Select(_ =>
{
Accelerator accelerator = device.CreateAccelerator(context);
acceleratorsInContext++;
if (acceleratorsInContext >= 128)
{
context = Context.CreateDefault();
device = context.GetPreferredDevice(false);
acceleratorsInContext = 0;
}
return accelerator;
})];
}
}
}
@@ -6,7 +6,7 @@ namespace PerfectNumbers.Core.Gpu.Accelerators;
internal sealed class HeuristicGpuTables
{
private static readonly ConcurrentDictionary<Accelerator, HeuristicGpuTables> _sharedTables = new(20_480, PerfectNumberConstants.RollingAccelerators);
private static readonly ConcurrentDictionary<Accelerator, HeuristicGpuTables> _sharedTables = new(20_480, 1);
internal static HeuristicGpuTables EnsureStaticTables(Accelerator accelerator, AcceleratorStream stream)
=> _sharedTables.GetOrAdd(accelerator, _ => new HeuristicGpuTables(accelerator, stream));
@@ -6,7 +6,7 @@ namespace PerfectNumbers.Core.Gpu.Accelerators;
internal sealed class LastDigitGpuTables
{
private static readonly ConcurrentDictionary<Accelerator, LastDigitGpuTables> _sharedTables = new(20_480, PerfectNumberConstants.RollingAccelerators);
private static readonly ConcurrentDictionary<Accelerator, LastDigitGpuTables> _sharedTables = new(20_480, PerfectNumberConstants.RollingAccelerators * 2);
internal static LastDigitGpuTables EnsureStaticTables(Accelerator accelerator, AcceleratorStream stream)
=> _sharedTables.GetOrAdd(accelerator, _ => new LastDigitGpuTables(accelerator, stream));
@@ -12,20 +12,21 @@ public sealed class Pow2MontgomeryAccelerator
[ThreadStatic]
private static Queue<Pow2MontgomeryAccelerator>? _pool;
private static AcceleratorPool _accelerators = new(PerfectNumberConstants.RollingAccelerators);
private static AcceleratorPool _accelerators = new(PerfectNumberConstants.RollingAccelerators << 1);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Pow2MontgomeryAccelerator Rent()
public static Pow2MontgomeryAccelerator Rent(int primeTesterCapacity)
{
var pool = _pool ??= [];
if (!pool.TryDequeue(out var gpu))
{
Accelerator accelerator = _accelerators.Rent();
gpu = new(accelerator, PerfectNumberConstants.DefaultFactorsBuffer);
gpu = new(accelerator, PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity);
}
else
{
gpu.Stream = gpu.Accelerator.CreateStream();
gpu.EnsureCapacity(PerfectNumberConstants.DefaultFactorsBuffer,primeTesterCapacity);
}
return gpu;
@@ -39,13 +40,69 @@ public sealed class Pow2MontgomeryAccelerator
_pool!.Enqueue(gpu);
}
internal static void Clear()
{
Queue<Pow2MontgomeryAccelerator>? pool = _pool;
if (pool != null)
{
while (pool.TryDequeue(out var lease))
{
lease.Dispose();
}
}
}
internal static void DisposeAll()
{
Queue<Pow2MontgomeryAccelerator>? pool = _pool;
if (pool != null)
{
while (pool.TryDequeue(out var lease))
{
lease.Dispose();
}
}
}
internal static void WarmUp()
{
Accelerator[]? accelerators = _accelerators.Accelerators;
int acceleratorCount = accelerators.Length;
for (var i = 0; i < acceleratorCount; i++)
{
Console.WriteLine($"Preparing accelerator {i}...");
var accelerator = accelerators[i];
AcceleratorStream stream = accelerator.CreateStream();
LastDigitGpuTables.EnsureStaticTables(accelerator, stream);
// SharedHeuristicGpuTables.EnsureStaticTables(accelerator, stream);
// _ = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.None);
// KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream);
// GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream);
stream.Synchronize();
stream.Dispose();
}
}
#endregion
public readonly Accelerator Accelerator;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> Input;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> Output;
public MemoryBuffer1D<ulong, Stride1D.Dense> PrimeTestInput;
public MemoryBuffer1D<byte, Stride1D.Dense> PrimeTestOutput;
public MemoryBuffer1D<int, Stride1D.Dense> HeuristicFlag;
public MemoryBuffer1D<KeyValuePair<ulong, int>, Stride1D.Dense> Pow2ModEntriesToTestOnDevice;
public Dictionary<ulong, int> Pow2ModEntriesToTestOnHost = [];
public readonly MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastOne;
public readonly MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastSeven;
public readonly MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastThree;
public readonly MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastNine;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastOne;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastSeven;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastThree;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastNine;
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>> SmallPrimeSieveKernel;
public AcceleratorStream? Stream;
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>> ConvertToStandardKernel;
@@ -53,7 +110,23 @@ public sealed class Pow2MontgomeryAccelerator
public readonly Kernel CheckFactorsKernel;
public void EnsureCapacity(int factorsCount)
[ThreadStatic]
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>? SmallPrimeSieveKernelPool;
internal static Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> GetSmallPrimeSieveKernel(Accelerator accelerator)
{
var pool = SmallPrimeSieveKernelPool ??= [];
if (pool.TryGetValue(accelerator, out var cached))
{
return cached;
}
cached = 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>>>();
pool[accelerator] = cached;
return cached;
}
public void EnsureCapacity(int factorsCount, int primeTesterCapacity)
{
if (factorsCount > Pow2ModEntriesToTestOnDevice.Length)
{
@@ -61,16 +134,45 @@ public sealed class Pow2MontgomeryAccelerator
Pow2ModEntriesToTestOnDevice.Dispose();
Pow2ModEntriesToTestOnDevice = accelerator.Allocate1D<KeyValuePair<ulong, int>>(factorsCount);
}
if (primeTesterCapacity > PrimeTestInput.Length)
{
PrimeTestInput?.Dispose();
PrimeTestOutput?.Dispose();
HeuristicFlag?.Dispose();
// lock (Accelerator)
{
PrimeTestInput = Accelerator.Allocate1D<ulong>(primeTesterCapacity);
PrimeTestOutput = Accelerator.Allocate1D<byte>(primeTesterCapacity);
HeuristicFlag = Accelerator.Allocate1D<int>(primeTesterCapacity);
}
}
}
public Pow2MontgomeryAccelerator(Accelerator accelerator, int factorsCount)
public Pow2MontgomeryAccelerator(Accelerator accelerator, int factorsCount, int primeTesterCapacity)
{
Accelerator = accelerator;
Stream = accelerator.CreateStream();
AcceleratorStream stream = accelerator.CreateStream();
Stream = stream;
Input = accelerator.Allocate1D<ulong>(1);
Output = accelerator.Allocate1D<ulong>(1);
Pow2ModEntriesToTestOnDevice = accelerator.Allocate1D<KeyValuePair<ulong, int>>(factorsCount);
PrimeTestInput = Accelerator.Allocate1D<ulong>(primeTesterCapacity);
PrimeTestOutput = Accelerator.Allocate1D<byte>(primeTesterCapacity);
HeuristicFlag = Accelerator.Allocate1D<int>(primeTesterCapacity);
var sharedTables = LastDigitGpuTables.EnsureStaticTables(accelerator, stream);
DevicePrimesLastOne = sharedTables.DevicePrimesLastOne;
DevicePrimesLastSeven = sharedTables.DevicePrimesLastSeven;
DevicePrimesLastThree = sharedTables.DevicePrimesLastThree;
DevicePrimesLastNine = sharedTables.DevicePrimesLastNine;
DevicePrimesPow2LastOne = sharedTables.DevicePrimesPow2LastOne;
DevicePrimesPow2LastSeven = sharedTables.DevicePrimesPow2LastSeven;
DevicePrimesPow2LastThree = sharedTables.DevicePrimesPow2LastThree;
DevicePrimesPow2LastNine = sharedTables.DevicePrimesPow2LastNine;
var keepKernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelKeepMontgomery);
var keepLauncher = KernelUtil.GetKernel(keepKernel);
@@ -84,6 +186,8 @@ public sealed class Pow2MontgomeryAccelerator
var checkFactorsKernel = accelerator.LoadStreamKernel<int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderGpuHeuristics.CheckFactorsKernel);
CheckFactorsKernel = KernelUtil.GetKernel(checkFactorsKernel);
SmallPrimeSieveKernel = (Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>)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>>>().Clone();
}
public void Dispose()
@@ -92,7 +196,17 @@ public sealed class Pow2MontgomeryAccelerator
Output.Dispose();
Stream?.Dispose();
Pow2ModEntriesToTestOnDevice.Dispose();
// Accelerator is shared with other threads
PrimeTestInput?.Dispose();
PrimeTestOutput?.Dispose();
HeuristicFlag?.Dispose();
// Stream is disposed upon return to the queue, so we don't need to dispose it here.
// Stream.Dispose();
// These resources are shared between GPU leases
// ReleaseSharedTables(_sharedTables);
// Accelerator.Dispose();
// Context.Dispose();
// Accelerator.Dispose();
}
}
@@ -1,192 +0,0 @@
using System.Runtime.CompilerServices;
using ILGPU;
using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu.Accelerators;
internal sealed class PrimeTesterByLastDigitAccelerator
{
#region Pool
[ThreadStatic]
private static Queue<PrimeTesterByLastDigitAccelerator>? Pool;
// internal static void EnsureStaticTables(Accelerator accelerator)
// {
// var tables = CreateSharedTables(accelerator);
// }
internal static void WarmUp()
{
Accelerator[]? accelerators = AcceleratorPool.Shared.Accelerators;
int acceleratorCount = accelerators.Length;
Span<AcceleratorStream> streams = new(new AcceleratorStream[acceleratorCount]);
for (var i = 0; i < acceleratorCount; i++)
{
Console.WriteLine($"Preparing accelerator {i}...");
var accelerator = accelerators[i];
AcceleratorStream stream = accelerator.CreateStream();
LastDigitGpuTables.EnsureStaticTables(accelerator, stream);
// SharedHeuristicGpuTables.EnsureStaticTables(accelerator, stream);
// _ = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.None);
// KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream);
// GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream);
streams[i] = stream;
}
for (var i = 0; i < acceleratorCount; i++)
{
AcceleratorStream stream = streams[i];
stream.Synchronize();
stream.Dispose();
Console.WriteLine($"Accelerator {i} is ready");
}
}
internal static PrimeTesterByLastDigitAccelerator Rent(int minBufferCapacity = 1)
{
var pool = Pool ??= new();
if (!pool.TryDequeue(out var lease))
{
lease = new PrimeTesterByLastDigitAccelerator(minBufferCapacity);
}
else
{
lease.EnsureCapacity(minBufferCapacity);
}
return lease;
}
internal static void Return(PrimeTesterByLastDigitAccelerator lease)
{
var pool = Pool ??= new();
pool.Enqueue(lease);
}
internal static void DisposeAll()
{
if (Pool != null)
{
while (Pool.TryDequeue(out var lease))
{
lease.DisposeResources();
}
}
}
internal static void Clear()
{
if (Pool != null)
{
var retained = new List<PrimeTesterByLastDigitAccelerator>();
while (Pool.TryDequeue(out var lease))
{
lease.DisposeResources();
}
}
}
#endregion
private readonly LastDigitGpuTables _sharedTables;
internal readonly Context Context;
public readonly Accelerator Accelerator;
private AcceleratorStream? _stream;
public AcceleratorStream Stream
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _stream ?? Accelerator.CreateStream();
}
[ThreadStatic]
public static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>? SmallPrimeSieveKernel;
internal static Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> GetSmallPrimeSieveKernel(Accelerator accelerator)
{
var pool = SmallPrimeSieveKernel ??= [];
if (pool.TryGetValue(accelerator, out var cached))
{
return cached;
}
cached = 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>>>();
pool[accelerator] = cached;
return cached;
}
[ThreadStatic]
public static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables>>? HeuristicTrialDivisionKernel;
public MemoryBuffer1D<ulong, Stride1D.Dense> Input = null!;
public MemoryBuffer1D<byte, Stride1D.Dense> Output = null!;
public MemoryBuffer1D<int, Stride1D.Dense> HeuristicFlag = null!;
public int BufferCapacity;
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastOne => _sharedTables.DevicePrimesLastOne;
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastSeven => _sharedTables.DevicePrimesLastSeven;
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastThree => _sharedTables.DevicePrimesLastThree;
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastNine => _sharedTables.DevicePrimesLastNine;
public MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastOne => _sharedTables.DevicePrimesPow2LastOne;
public MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastSeven => _sharedTables.DevicePrimesPow2LastSeven;
public MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastThree => _sharedTables.DevicePrimesPow2LastThree;
public MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastNine => _sharedTables.DevicePrimesPow2LastNine;
internal PrimeTesterByLastDigitAccelerator(int minBufferCapacity)
{
Accelerator accelerator = AcceleratorPool.Shared.Rent();
Context = accelerator.Context;
Accelerator = accelerator;
AcceleratorStream stream = accelerator.CreateStream();
var sharedTables = LastDigitGpuTables.EnsureStaticTables(accelerator, stream);
_sharedTables = sharedTables;
EnsureCapacity(minBufferCapacity);
stream.Synchronize();
stream.Dispose();
}
public void EnsureCapacity(int minCapacity)
{
if (minCapacity <= BufferCapacity)
{
return;
}
Input?.Dispose();
Output?.Dispose();
HeuristicFlag?.Dispose();
// lock (Accelerator)
{
Input = Accelerator.Allocate1D<ulong>(minCapacity);
Output = Accelerator.Allocate1D<byte>(minCapacity);
HeuristicFlag = Accelerator.Allocate1D<int>(minCapacity);
}
BufferCapacity = minCapacity;
}
public void Dispose()
{
_stream?.Dispose();
_stream = null;
Return(this);
}
internal void DisposeResources()
{
Input?.Dispose();
Output?.Dispose();
HeuristicFlag?.Dispose();
_stream?.Dispose();
_stream = null;
// ReleaseSharedTables(_sharedTables);
// These resources are shared between GPU leases
// Accelerator.Dispose();
// AcceleratorContext.Dispose();
}
}
@@ -4,7 +4,7 @@ using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu;
internal readonly struct HeuristicGpuDivisorTables
public readonly struct HeuristicGpuDivisorTables
{
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding1;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding3;
@@ -5,7 +5,7 @@ using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu;
internal enum HeuristicGpuDivisorTableKind : byte
public enum HeuristicGpuDivisorTableKind : byte
{
GroupA = 0,
GroupBEnding1 = 1,
@@ -10,9 +10,8 @@ public static class Pow2MontgomeryGpuCalculator
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong CalculateKeep(ulong exponent, in MontgomeryDivisorData divisor)
{
GpuPrimeWorkLimiter.Acquire();
var gpu = Pow2MontgomeryAccelerator.Rent();
var gpu = Pow2MontgomeryAccelerator.Rent(1);
Accelerator? accelerator = gpu.Accelerator;
AcceleratorStream stream = gpu.Stream!;
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input;
@@ -23,23 +22,24 @@ public static class Pow2MontgomeryGpuCalculator
// resultBuffer.MemSetToZero(stream);
var kernel = gpu.KeepMontgomeryKernel;
GpuPrimeWorkLimiter.Acquire();
kernel(stream, 1, exponentBuffer.View, divisor, resultBuffer.View);
ulong result = 0UL;
resultBuffer.View.CopyToCPU(stream, ref result, 1);
stream.Synchronize();
GpuPrimeWorkLimiter.Release();
Pow2MontgomeryAccelerator.Return(gpu);
GpuPrimeWorkLimiter.Release();
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong CalculateConvert(ulong exponent, in MontgomeryDivisorData divisor)
{
GpuPrimeWorkLimiter.Acquire();
var gpu = Pow2MontgomeryAccelerator.Rent();
var gpu = Pow2MontgomeryAccelerator.Rent(1);
AcceleratorStream stream = gpu.Stream!;
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input;
MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.Output;
@@ -49,14 +49,16 @@ public static class Pow2MontgomeryGpuCalculator
// resultBuffer.MemSetToZero(stream);
var kernel = gpu.ConvertToStandardKernel;
GpuPrimeWorkLimiter.Acquire();
kernel(stream, 1, exponentBuffer.View, divisor, resultBuffer.View);
ulong result = 0UL;
resultBuffer.View.CopyToCPU(stream, ref result, 1);
stream.Synchronize();
GpuPrimeWorkLimiter.Release();
Pow2MontgomeryAccelerator.Return(gpu);
GpuPrimeWorkLimiter.Release();
return result;
}
+22 -14
View File
@@ -52,14 +52,14 @@ internal static partial class PrimeOrderCalculator
ulong phi = prime - 1UL;
Pow2MontgomeryAccelerator gpu = Pow2MontgomeryAccelerator.Rent();
Pow2MontgomeryAccelerator gpu = Pow2MontgomeryAccelerator.Rent(1);
if (IsGpuHeuristicDevice && PrimeOrderGpuHeuristics.TryCalculateOrder(prime, previousOrder, config, divisorData, out ulong gpuOrder))
{
Pow2MontgomeryAccelerator.Return(gpu);
return gpuOrder;
}
PartialFactorResult phiFactors = PartialFactor(phi, config);
PartialFactorResult phiFactors = PartialFactor(gpu, phi, config);
ulong result;
if (phiFactors.Factors is null)
@@ -106,7 +106,7 @@ internal static partial class PrimeOrderCalculator
return CalculateByFactorizationGpu(gpu, prime, divisorData);
}
if (TryHeuristicFinishCpu(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;
@@ -347,7 +347,7 @@ internal static partial class PrimeOrderCalculator
return false;
}
PartialFactorResult factorization = PartialFactor(order, config);
PartialFactorResult factorization = PartialFactor(gpu, order, config);
if (factorization.Factors is null)
{
factorization.Dispose();
@@ -488,6 +488,7 @@ internal static partial class PrimeOrderCalculator
}
private static bool TryHeuristicFinishCpu(
Pow2MontgomeryAccelerator gpu,
ulong prime,
ulong order,
ulong? previousOrder,
@@ -505,7 +506,7 @@ internal static partial class PrimeOrderCalculator
}
// Reuse the partial factorization from TryConfirmOrderCpu when available.
PartialFactorResult orderFactors = cachedOrderFactors ?? PartialFactor(order, config);
PartialFactorResult orderFactors = cachedOrderFactors ?? PartialFactor(gpu, order, config);
try
{
if (orderFactors.Factors is null)
@@ -621,7 +622,7 @@ internal static partial class PrimeOrderCalculator
bool equalsOne = powStepper.InitializeCpuIsUnity(candidate);
powStepperInitialized = true;
if (equalsOne && TryConfirmCandidateCpu(prime, candidate, divisorData, config, ref powUsed, powBudget))
if (equalsOne && TryConfirmCandidateCpu(gpu, prime, candidate, divisorData, config, ref powUsed, powBudget))
{
if (gpuPool is not null)
{
@@ -658,7 +659,7 @@ internal static partial class PrimeOrderCalculator
continue;
}
if (!TryConfirmCandidateCpu(prime, candidate, divisorData, config, ref powUsed, powBudget))
if (!TryConfirmCandidateCpu(gpu, prime, candidate, divisorData, config, ref powUsed, powBudget))
{
continue;
}
@@ -863,9 +864,9 @@ internal static partial class PrimeOrderCalculator
}
}
private static bool TryConfirmCandidateCpu(ulong prime, ulong candidate, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config, ref int powUsed, int powBudget)
private static bool TryConfirmCandidateCpu(Pow2MontgomeryAccelerator gpu, ulong prime, ulong candidate, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config, ref int powUsed, int powBudget)
{
PartialFactorResult factorization = PartialFactor(candidate, config);
PartialFactorResult factorization = PartialFactor(gpu, candidate, config);
try
{
if (factorization.Factors is null)
@@ -1056,7 +1057,7 @@ internal static partial class PrimeOrderCalculator
// private static ulong _partialFactorPendingHits;
// private static ulong _partialFactorCofactorHits;
private static PartialFactorResult PartialFactor(ulong value, in PrimeOrderSearchConfig config)
private static PartialFactorResult PartialFactor(Pow2MontgomeryAccelerator gpu, ulong value, in PrimeOrderSearchConfig config)
{
if (value <= 1UL)
{
@@ -1125,6 +1126,8 @@ internal static partial class PrimeOrderCalculator
bool pollardRhoDeadlineReached = Stopwatch.GetTimestamp() > deadlineTimestamp;
if (!pollardRhoDeadlineReached)
{
GpuPrimeWorkLimiter.Acquire();
while (compositeStack.Count > 0)
{
ulong composite = compositeStack.Pop();
@@ -1141,7 +1144,7 @@ internal static partial class PrimeOrderCalculator
// Console.WriteLine($"Partial factor hits {Volatile.Read(ref _partialFactorHits)}");
// bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None);
bool isPrime = PrimeTester.IsPrime(composite);
bool isPrime = PrimeTester.IsPrimeGpu(gpu, composite);
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
if (isPrime)
@@ -1173,6 +1176,8 @@ internal static partial class PrimeOrderCalculator
{
pollardRhoDeadlineReached = Stopwatch.GetTimestamp() > deadlineTimestamp;
}
GpuPrimeWorkLimiter.Release();
}
if (pollardRhoDeadlineReached)
@@ -1188,6 +1193,7 @@ internal static partial class PrimeOrderCalculator
bool cofactorContainsComposite = false;
int pendingCount = pending.Count;
int index = 0;
GpuPrimeWorkLimiter.Acquire();
for (; index < pendingCount; index++)
{
PendingEntry entry = pending[index];
@@ -1209,7 +1215,7 @@ internal static partial class PrimeOrderCalculator
// Console.WriteLine($"Partial factor pending hits {Volatile.Read(ref _partialFactorPendingHits)}");
// bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None);
bool isPrime = PrimeTester.IsPrimeGpu(composite);
bool isPrime = PrimeTester.IsPrimeGpu(gpu, composite);
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
entry = entry.WithPrimality(isPrime);
@@ -1228,7 +1234,7 @@ internal static partial class PrimeOrderCalculator
cofactorContainsComposite = true;
}
}
bool cofactorIsPrime;
if (cofactor <= 1UL)
{
@@ -1246,11 +1252,13 @@ internal static partial class PrimeOrderCalculator
// Atomic.Add(ref _partialFactorCofactorHits, 1UL);
// Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}");
cofactorIsPrime = PrimeTester.IsPrimeGpu(cofactor);
cofactorIsPrime = PrimeTester.IsPrimeGpu(gpu, cofactor);
// cofactorIsPrime = PrimeTester.IsPrimeGpu(cofactor);
// cofactorIsPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(cofactor);
}
GpuPrimeWorkLimiter.Release();
ArrayPool<FactorEntry> pool = ThreadStaticPools.FactorEntryPool;
bool temp;
if (factorCount == 0)
@@ -38,7 +38,7 @@ internal static partial class PrimeOrderCalculator
entries.Sort(entries, static (a, b) => a.Key.CompareTo(b.Key));
AcceleratorStream stream = gpu.Stream!;
gpu.EnsureCapacity(entryCount);
gpu.EnsureCapacity(entryCount, 1);
gpu.Pow2ModEntriesToTestOnDevice.View.CopyFromCPU(stream, entries);
ulong order = phi;
+51 -15
View File
@@ -94,17 +94,17 @@ public sealed class PrimeTester
public static bool IsPrimeGpu(ulong n)
{
GpuPrimeWorkLimiter.Acquire();
var gpu = PrimeTesterByLastDigitAccelerator.Rent(1);
var gpu = Pow2MontgomeryAccelerator.Rent(1);
var accelerator = gpu.Accelerator;
var stream = gpu.Stream;
var kernel = PrimeTesterByLastDigitAccelerator.GetSmallPrimeSieveKernel(accelerator);
var stream = gpu.Stream!;
var kernel = gpu.SmallPrimeSieveKernel;
ulong value = n;
// Span<byte> flag = stackalloc byte[1];
byte flag = 0;
var input = gpu.Input;
var output = gpu.Output;
var input = gpu.PrimeTestInput;
var output = gpu.PrimeTestOutput;
var inputView = input.View;
var outputView = output.View;
@@ -131,6 +131,42 @@ public sealed class PrimeTester
return flag != 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsPrimeGpu(Pow2MontgomeryAccelerator gpu, ulong n)
{
var accelerator = gpu.Accelerator;
var stream = gpu.Stream!;
Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>? kernel = gpu.SmallPrimeSieveKernel;
ulong value = n;
// Span<byte> flag = stackalloc byte[1];
byte flag = 0;
var input = gpu.PrimeTestInput;
var output = gpu.PrimeTestOutput;
var inputView = input.View;
var outputView = output.View;
inputView.CopyFromCPU(stream, ref value, 1);
kernel(
stream,
1,
inputView,
gpu.DevicePrimesLastOne.View,
gpu.DevicePrimesLastSeven.View,
gpu.DevicePrimesLastThree.View,
gpu.DevicePrimesLastNine.View,
gpu.DevicePrimesPow2LastOne.View,
gpu.DevicePrimesPow2LastSeven.View,
gpu.DevicePrimesPow2LastThree.View,
gpu.DevicePrimesPow2LastNine.View,
outputView);
outputView.CopyToCPU(stream, ref flag, 1);
stream.Synchronize();
return flag != 0;
}
public static int GpuBatchSize { get; set; } = 262_144;
private static readonly object GpuWarmUpLock = new();
@@ -151,7 +187,7 @@ public sealed class PrimeTester
return;
}
PrimeTesterByLastDigitAccelerator.WarmUp();
Pow2MontgomeryAccelerator.WarmUp();
WarmedGpuLeaseCount = target;
}
}
@@ -160,15 +196,15 @@ public sealed class PrimeTester
public static void IsPrimeBatchGpu(ReadOnlySpan<ulong> values, Span<byte> results)
{
GpuPrimeWorkLimiter.Acquire();
var gpu = PrimeTesterByLastDigitAccelerator.Rent(GpuBatchSize);
var gpu = Pow2MontgomeryAccelerator.Rent(GpuBatchSize);
var accelerator = gpu.Accelerator;
var stream = gpu.Stream;
var kernel = PrimeTesterByLastDigitAccelerator.GetSmallPrimeSieveKernel(accelerator);
var stream = gpu.Stream!;
var kernel = gpu.SmallPrimeSieveKernel;
int totalLength = values.Length;
int batchSize = GpuBatchSize;
var input = gpu.Input;
var output = gpu.Output;
var input = gpu.PrimeTestInput;
var output = gpu.PrimeTestOutput;
var inputView = input.View;
var outputView = output.View;
@@ -211,12 +247,12 @@ public sealed class PrimeTester
// Expose cache clearing for accelerator disposal coordination
public static void ClearGpuCaches()
{
PrimeTesterByLastDigitAccelerator.Clear();
Pow2MontgomeryAccelerator.Clear();
}
internal static void DisposeGpuContexts()
{
PrimeTesterByLastDigitAccelerator.DisposeAll();
Pow2MontgomeryAccelerator.DisposeAll();
lock (GpuWarmUpLock)
{
WarmedGpuLeaseCount = 0;
@@ -241,9 +277,9 @@ public sealed class PrimeTester
// 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.
var gpu = PrimeTesterByLastDigitAccelerator.Rent();
var gpu = Pow2MontgomeryAccelerator.Rent(1);
var accelerator = gpu.Accelerator;
var stream = gpu.Stream;
var stream = gpu.Stream!;
int length = values.Length;
ArrayPool<ulong> pool = ThreadStaticPools.UlongPool;