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
+1412 -1292

No files matched your search

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