Stabilization improvements. Stream will be released when releasing GPU lease to prevent application crashes.

This commit is contained in:
mlemanczyk committed 2025-11-10 14:30:30 +01:00
1 parent 2726c369f6
commit fa89958736
8 files changed
+1414 -1294

No files matched your search

+18 -7
View File
@@ -3,21 +3,21 @@ using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu
{
public struct AcceleratorPool(int capacity)
public struct AcceleratorPool
{
internal readonly Accelerator[] Accelerators = [.. Enumerable.Range(1, capacity).Select(_ => {
return SharedGpuContext.CreateAccelerator();
})];
internal readonly Context Context;
internal readonly Device Device;
internal readonly Accelerator[] Accelerators;
private int _index;
public Accelerator Rent()
{
Atomic.CompareExchange(ref _index, capacity, 0);
Atomic.CompareExchange(ref _index, Capacity, 0);
var index = Atomic.Add(ref _index, 1);
if (index >= capacity)
if (index >= Capacity)
{
index -= capacity;
index -= Capacity;
}
return Accelerators[index];
@@ -29,5 +29,16 @@ namespace PerfectNumbers.Core.Gpu
}
public static readonly AcceleratorPool Shared = new(PerfectNumberConstants.RollingAccelerators);
private readonly int Capacity;
public AcceleratorPool(int capacity)
{
Capacity = capacity;
var context = Context.CreateDefault();
var device = context.GetPreferredDevice(false);
Context = context;
Device = device;
Accelerators = [.. Enumerable.Range(0, capacity).Select(_ => device.CreateAccelerator(context))];
}
}
}
@@ -8,7 +8,7 @@ namespace PerfectNumbers.Core.Gpu.Accelerators;
public sealed class Pow2MontgomeryAccelerator
{
#region Pool
[ThreadStatic]
private static Queue<Pow2MontgomeryAccelerator>? _pool;
@@ -21,7 +21,11 @@ public sealed class Pow2MontgomeryAccelerator
if (!pool.TryDequeue(out var gpu))
{
Accelerator accelerator = _accelerators.Rent();
gpu = new(accelerator);
gpu = new(accelerator, PerfectNumberConstants.DefaultFactorsBuffer);
}
else
{
gpu.Stream = gpu.Accelerator.CreateStream();
}
return gpu;
@@ -40,17 +44,33 @@ public sealed class Pow2MontgomeryAccelerator
public readonly Accelerator Accelerator;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> Input;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> Output;
public MemoryBuffer1D<KeyValuePair<ulong, int>, Stride1D.Dense> ToTestOnDevice;
public Dictionary<ulong, int> ToTestOnHost = [];
public AcceleratorStream? Stream;
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>> ConvertToStandardKernel;
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>> KeepMontgomeryKernel;
public Pow2MontgomeryAccelerator(Accelerator accelerator)
public readonly Kernel CheckFactorsKernel;
public void EnsureCapacity(int factorsCount)
{
if (factorsCount > ToTestOnDevice.Length)
{
Accelerator accelerator = Accelerator;
ToTestOnDevice.Dispose();
ToTestOnDevice = accelerator.Allocate1D<KeyValuePair<ulong, int>>(factorsCount);
}
}
public Pow2MontgomeryAccelerator(Accelerator accelerator, int factorsCount)
{
Accelerator = accelerator;
Stream = accelerator.CreateStream();
Input = accelerator.Allocate1D<ulong>(1);
Output = accelerator.Allocate1D<ulong>(1);
Stream = accelerator.CreateStream();
ToTestOnDevice = accelerator.Allocate1D<KeyValuePair<ulong, int>>(factorsCount);
var keepKernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelKeepMontgomery);
var keepLauncher = KernelUtil.GetKernel(keepKernel);
@@ -61,6 +81,9 @@ public sealed class Pow2MontgomeryAccelerator
var convertLauncher = KernelUtil.GetKernel(convertKernel);
ConvertToStandardKernel = convertLauncher.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
var checkFactorsKernel = accelerator.LoadStreamKernel<int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderGpuHeuristics.CheckFactorsKernel);
CheckFactorsKernel = KernelUtil.GetKernel(checkFactorsKernel);
}
public void Dispose()
@@ -68,7 +91,8 @@ public sealed class Pow2MontgomeryAccelerator
Input.Dispose();
Output.Dispose();
Stream?.Dispose();
ToTestOnDevice.Dispose();
// Accelerator is shared with other threads
// Accelerator.Dispose();
}
}
}
File diff suppressed because it is too large. Load diff
@@ -293,7 +293,7 @@ public static class MersenneNumberDivisorByDivisorTester
tasks[taskIndex++] = Task.Factory.StartNew(
() =>
{
// startGate.Wait();
startGate.Wait();
Console.WriteLine($"Task started for range {rangeStart}");
for (int index = rangeStart; index < rangeEnd; index++)
@@ -5,7 +5,8 @@ namespace PerfectNumbers.Core;
public static class PerfectNumberConstants
{
public const ulong BiggestKnownEvenPerfectP = 136279841UL;
public const int ConsoleInterval = 100_000;
public const int ConsoleInterval = 100_000;
public const int DefaultFactorsBuffer = 32;
public const ulong ExtraDivisorCycleSearchLimit = 64UL;
public const int MaxQForDivisorCycles = 4_000_000;
public static readonly uint PrimesLimit = 1_000_000; //(ulong)Array.MaxLength;// 1_000_000;
@@ -64,7 +64,7 @@ internal static partial class PrimeOrderCalculator
ulong result;
if (phiFactors.Factors is null)
{
result = CalculateByFactorizationCpu(gpu, prime, divisorData);
result = CalculateByFactorizationGpu(gpu, prime, divisorData);
phiFactors.Dispose();
return result;
}
@@ -103,7 +103,7 @@ internal static partial class PrimeOrderCalculator
{
orderFactors?.Dispose();
GpuPrimeWorkLimiter.Release();
return CalculateByFactorizationCpu(gpu, prime, divisorData);
return CalculateByFactorizationGpu(gpu, prime, divisorData);
}
if (TryHeuristicFinishCpu(prime, candidateOrder, previousOrder, divisorData, config, phiFactors, orderFactors, out ulong order))
@@ -1658,17 +1658,17 @@ internal static partial class PrimeOrderCalculator
{
ulong phi = prime - 1UL;
Dictionary<ulong, int> counts = new(capacity: 8);
FactorCompletely(phi, counts);
FactorCompletelyCpu(phi, counts);
if (counts.Count == 0)
{
return phi;
}
List<KeyValuePair<ulong, int>> entries = [.. counts];
entries.Sort(static (a, b) => a.Key.CompareTo(b.Key));
KeyValuePair<ulong, int>[] entries = [.. counts];
Array.Sort(entries, static (a, b) => a.Key.CompareTo(b.Key));
ulong order = phi;
int entryCount = entries.Count;
int entryCount = entries.Length;
GpuPrimeWorkLimiter.Acquire();
for (int i = 0; i < entryCount; i++)
@@ -1677,12 +1677,14 @@ internal static partial class PrimeOrderCalculator
int exponent = entries[i].Value;
for (int iteration = 0; iteration < exponent; iteration++)
{
if ((order % primeFactor) != 0UL)
ulong remainder = order % primeFactor;
if (remainder != 0UL)
{
break;
}
ulong candidate = order / primeFactor;
ulong candidate = order - primeFactor * remainder;
// ulong candidate = order / primeFactor;
// if (candidate.Pow2MontgomeryModWindowedCpu(divisorData, keepMontgomery: false) == 1UL)
if (candidate.Pow2MontgomeryModWindowedConvertGpu(gpu, divisorData) == 1UL)
@@ -1700,7 +1702,7 @@ internal static partial class PrimeOrderCalculator
return order;
}
private static void FactorCompletely(ulong value, Dictionary<ulong, int> counts)
private static void FactorCompletelyCpu(ulong value, Dictionary<ulong, int> counts)
{
FactorCompletely(value, counts, knownComposite: false);
}
@@ -1,8 +1,10 @@
using System.Runtime.InteropServices;
using ILGPU;
using ILGPU.Runtime;
using ILGPU.Runtime.Cuda;
using ILGPU.Util;
using Open.Collections;
using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core;
@@ -12,6 +14,47 @@ internal static partial class PrimeOrderCalculator
private const int GpuSmallPrimeFactorSlots = 64;
private static ulong CalculateByFactorizationGpu(Pow2MontgomeryAccelerator gpu, ulong prime, in MontgomeryDivisorData divisorData)
{
ulong phi = prime - 1UL;
Dictionary<ulong, int> counts = gpu.ToTestOnHost;
counts.Clear();
FactorCompletelyCpu(phi, counts);
if (counts.Count == 0)
{
return phi;
}
int entryCount = counts.Count;
Span<KeyValuePair<ulong, int>> entries = stackalloc KeyValuePair<ulong, int>[entryCount];
int stored = 0;
foreach (var entry in counts)
{
entries[stored++] = entry;
}
entries.Sort(entries, static (a, b) => a.Key.CompareTo(b.Key));
AcceleratorStream stream = gpu.Stream!;
gpu.EnsureCapacity(entryCount);
gpu.ToTestOnDevice.View.CopyFromCPU(stream, entries);
ulong order = phi;
var kernel = gpu.CheckFactorsKernel;
stream.Synchronize();
GpuPrimeWorkLimiter.Acquire();
kernel.Launch(stream, 1, entryCount, phi, gpu.ToTestOnDevice.View, divisorData, gpu.Output.View);
gpu.Output.View.CopyToCPU(stream, ref order, 1);
stream.Synchronize();
GpuPrimeWorkLimiter.Release();
return order;
}
private static bool TryPopulateSmallPrimeFactorsGpu(ulong value, uint limit, Dictionary<ulong, int> counts, out int factorCount, out ulong remaining)
{
var primeBufferArray = ThreadStaticPools.UlongPool.Rent(GpuSmallPrimeFactorSlots);
+5 -5
View File
@@ -99,7 +99,7 @@ public sealed class PrimeTester
var gpu = PrimeTesterGpuContextPool.Rent(1);
var accelerator = gpu.Accelerator;
var stream = gpu.Stream;
var kernel = PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.GetKernel(accelerator);
var kernel = PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.GetSmallPrimeSieveKernel(accelerator);
ulong value = n;
// Span<byte> flag = stackalloc byte[1];
@@ -167,7 +167,7 @@ public sealed class PrimeTester
var gpu = PrimeTesterGpuContextPool.Rent(GpuBatchSize);
var accelerator = gpu.Accelerator;
var stream = gpu.Stream;
var kernel = PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.GetKernel(accelerator);
var kernel = PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.GetSmallPrimeSieveKernel(accelerator);
int totalLength = values.Length;
int batchSize = GpuBatchSize;
@@ -324,11 +324,11 @@ public sealed class PrimeTester
}
[ThreadStatic]
public static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>? Kernel;
public static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, 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<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> GetKernel(Accelerator accelerator)
internal static Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> GetSmallPrimeSieveKernel(Accelerator accelerator)
{
var pool = Kernel ??= [];
var pool = SmallPrimeSieveKernel ??= [];
if (pool.TryGetValue(accelerator, out var cached))
{
return cached;