diff --git a/PerfectNumbers.Core/Gpu/GpuKernelPool.cs b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs
index a0fa444..66f5465 100644
--- a/PerfectNumbers.Core/Gpu/GpuKernelPool.cs
+++ b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs
@@ -542,9 +542,12 @@ public class GpuKernelPool
/// Action to run with (Accelerator, Stream).
public static void Run(Action action)
{
- using var lease = GetKernel(useGpuOrder: true);
+ var lease = GetKernel(useGpuOrder: true);
var accelerator = lease.Accelerator;
- using var stream = accelerator.CreateStream();
+ var stream = accelerator.CreateStream();
action(accelerator, stream);
+ stream.Dispose();
+ lease.Dispose();
+
}
}
diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs
index 4b876fb..743b0e5 100644
--- a/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs
+++ b/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs
@@ -9,69 +9,73 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
private readonly GpuKernelType _kernelType = kernelType;
private readonly bool _useGpuOrder = useGpuOrder;
- public void Scan(ulong exponent, UInt128 twoP, bool lastIsSeven, UInt128 maxK, ref bool isPrime)
- {
- using var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
- var accelerator = gpuLease.Accelerator;
- int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy
- UInt128 kStart = 1UL;
- ulong divMul = (ulong)((((UInt128)1 << 64) - UInt128.One) / exponent) + 1UL;
- byte last = lastIsSeven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
+ public void Scan(ulong exponent, UInt128 twoP, bool lastIsSeven, UInt128 maxK, ref bool isPrime)
+ {
+ var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
+ var accelerator = gpuLease.Accelerator;
+ int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy
+ UInt128 kStart = 1UL;
+ ulong divMul = (ulong)((((UInt128)1 << 64) - UInt128.One) / exponent) + 1UL;
+ byte last = lastIsSeven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
var kernel = (_kernelType == GpuKernelType.Pow2Mod)
? gpuLease.Pow2ModKernel
: gpuLease.IncrementalKernel;
- using var orderBuffer = accelerator.Allocate1D(batchSize);
- // Avoid giant stack allocations that can trigger StackOverflow when batchSize is large.
- // Rent a reusable array from the shared pool instead.
- ulong[] orderArray = ArrayPool.Shared.Rent(batchSize);
- UInt128 remaining;
- int currentSize;
- int i;
- UInt128 q = UInt128.Zero;
- try
- {
- while (kStart <= maxK && Volatile.Read(ref isPrime))
- {
- remaining = maxK - kStart + UInt128.One;
- currentSize = remaining > (UInt128)batchSize ? batchSize : (int)remaining;
- Span orders = orderArray.AsSpan(0, currentSize);
- // Precompute residue automaton bases for this batch
- UInt128 q0 = twoP * kStart + UInt128.One;
- ulong q0m10 = q0.Mod10();
- ulong q0m8 = q0.Mod8();
- ulong q0m3 = q0.Mod3();
- ulong q0m5 = q0.Mod5();
- ulong step10 = ((exponent % 10UL) << 1) % 10UL;
- ulong step8 = ((exponent & 7UL) << 1) & 7UL;
- ulong step3 = ((exponent % 3UL) << 1) % 3UL;
- ulong step5 = ((exponent % 5UL) << 1) % 5UL;
+ var orderBuffer = accelerator.Allocate1D(batchSize);
+ // Avoid giant stack allocations that can trigger StackOverflow when batchSize is large.
+ // Rent a reusable array from the shared pool instead.
+ ulong[] orderArray = ArrayPool.Shared.Rent(batchSize);
+ UInt128 remaining;
+ int currentSize;
+ int i;
+ UInt128 q = UInt128.Zero;
+ try
+ {
+ while (kStart <= maxK && Volatile.Read(ref isPrime))
+ {
+ remaining = maxK - kStart + UInt128.One;
+ currentSize = remaining > (UInt128)batchSize ? batchSize : (int)remaining;
+ Span orders = orderArray.AsSpan(0, currentSize);
+ // Precompute residue automaton bases for this batch
+ UInt128 q0 = twoP * kStart + UInt128.One;
+ ulong q0m10 = q0.Mod10();
+ ulong q0m8 = q0.Mod8();
+ ulong q0m3 = q0.Mod3();
+ ulong q0m5 = q0.Mod5();
+ ulong step10 = ((exponent % 10UL) << 1) % 10UL;
+ ulong step8 = ((exponent & 7UL) << 1) & 7UL;
+ ulong step3 = ((exponent % 3UL) << 1) % 3UL;
+ ulong step5 = ((exponent % 5UL) << 1) % 5UL;
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
kernel(currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, divMul,
q0m10, q0m8, q0m3, q0m5, orderBuffer.View, smallCyclesView);
- accelerator.Synchronize();
- orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize);
- q = twoP * kStart + UInt128.One;
- for (i = 0; i < currentSize && Volatile.Read(ref isPrime); i++, q += twoP)
- {
- if (orders[i] != 0UL)
- {
- if (q.IsPrimeCandidate())
- {
- Volatile.Write(ref isPrime, false);
- }
- }
- }
+ accelerator.Synchronize();
+ orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize);
+ q = twoP * kStart + UInt128.One;
+ for (i = 0; i < currentSize && Volatile.Read(ref isPrime); i++, q += twoP)
+ {
+ if (orders[i] != 0UL)
+ {
+ if (q.IsPrimeCandidate())
+ {
+ Volatile.Write(ref isPrime, false);
+ }
+ }
+ }
- kStart += (UInt128)currentSize;
- }
- }
- finally
- {
- ArrayPool.Shared.Return(orderArray);
- }
- }
+ kStart += (UInt128)currentSize;
+ }
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(orderArray);
+ }
+
+ orderBuffer.Dispose();
+ gpuLease.Dispose();
+
+ }
}
diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberLucasLehmerGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberLucasLehmerGpuTester.cs
index 4fb6487..96719bc 100644
--- a/PerfectNumbers.Core/Gpu/MersenneNumberLucasLehmerGpuTester.cs
+++ b/PerfectNumbers.Core/Gpu/MersenneNumberLucasLehmerGpuTester.cs
@@ -55,30 +55,39 @@ public class MersenneNumberLucasLehmerGpuTester
return false;
}
- using var limiter = GpuPrimeWorkLimiter.Acquire();
+ var limiter = GpuPrimeWorkLimiter.Acquire();
+ bool result;
if (exponent >= 128UL)
{
if (!TryGetNttParameters(exponent, out var nttMod, out var primitiveRoot))
{
+ limiter.Dispose();
throw new NotSupportedException("NTT parameters for the given exponent are not available.");
}
- return IsMersennePrimeNtt(exponent, nttMod, primitiveRoot, runOnGpu);
+ result = IsMersennePrimeNtt(exponent, nttMod, primitiveRoot, runOnGpu);
+ }
+ else
+ {
+ var gpu = GpuContextPool.RentPreferred(preferCpu: !runOnGpu);
+ var accelerator = gpu.Accelerator;
+ var kernel = GetKernel(accelerator);
+ var modulus = new GpuUInt128(((UInt128)1 << (int)exponent) - 1UL);
+ var buffer = accelerator.Allocate1D(1);
+ kernel(1, exponent, modulus, buffer.View);
+ accelerator.Synchronize();
+ result = buffer.GetAsArray1D()[0].IsZero;
+ buffer.Dispose();
+ gpu.Dispose();
}
- using var gpu = GpuContextPool.RentPreferred(preferCpu: !runOnGpu);
- var accelerator = gpu.Accelerator;
- var kernel = GetKernel(accelerator);
- var modulus = new GpuUInt128(((UInt128)1 << (int)exponent) - 1UL);
- using var buffer = accelerator.Allocate1D(1);
- kernel(1, exponent, modulus, buffer.View);
- accelerator.Synchronize();
- return buffer.GetAsArray1D()[0].IsZero;
+ limiter.Dispose();
+ return result;
}
private bool IsMersennePrimeNtt(ulong exponent, GpuUInt128 nttMod, GpuUInt128 primitiveRoot, bool runOnGpu)
{
- using var gpu = GpuContextPool.RentPreferred(preferCpu: !runOnGpu);
+ var gpu = GpuContextPool.RentPreferred(preferCpu: !runOnGpu);
var accelerator = gpu.Accelerator;
var addKernel = GetAddSmallKernel(accelerator);
var subKernel = GetSubSmallKernel(accelerator);
@@ -92,7 +101,7 @@ public class MersenneNumberLucasLehmerGpuTester
{
transformLength <<= 1;
}
- using var stateBuffer = accelerator.Allocate1D(transformLength);
+ var stateBuffer = accelerator.Allocate1D(transformLength);
stateBuffer.MemSetToZero();
addKernel(1, 4UL, stateBuffer.View);
@@ -121,12 +130,16 @@ public class MersenneNumberLucasLehmerGpuTester
reduceKernel(1, exponent, stateBuffer.View);
- using var resultBuffer = accelerator.Allocate1D(1);
+ var resultBuffer = accelerator.Allocate1D(1);
zeroKernel(1, stateBuffer.View, resultBuffer.View);
accelerator.Synchronize();
byte[] result = new byte[1];
resultBuffer.View.CopyToCPU(result);
- return result[0] != 0;
+ bool isPrime = result[0] != 0;
+ resultBuffer.Dispose();
+ stateBuffer.Dispose();
+ gpu.Dispose();
+ return isPrime;
}
private static readonly ConcurrentDictionary NttParameterCache = new()
diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs
index 39a6aad..fa11100 100644
--- a/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs
+++ b/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs
@@ -7,14 +7,14 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
private readonly GpuKernelType _kernelType = kernelType;
private readonly bool _useGpuOrder = useGpuOrder;
- public void Scan(ulong exponent, UInt128 twoP, bool lastIsSeven, UInt128 maxK, ref bool isPrime)
- {
- using var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
- var accelerator = gpuLease.Accelerator;
- int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR
- UInt128 kStart = UInt128.One;
- ulong divMul = (ulong)((((UInt128)1 << 64) - UInt128.One) / exponent) + 1UL;
- byte last = lastIsSeven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
+ public void Scan(ulong exponent, UInt128 twoP, bool lastIsSeven, UInt128 maxK, ref bool isPrime)
+ {
+ var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
+ var accelerator = gpuLease.Accelerator;
+ int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR
+ UInt128 kStart = UInt128.One;
+ ulong divMul = (ulong)((((UInt128)1 << 64) - UInt128.One) / exponent) + 1UL;
+ byte last = lastIsSeven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
var kernel = _kernelType switch
{
@@ -22,42 +22,46 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
_ => gpuLease.IncrementalOrderKernel,
};
- using var foundBuffer = accelerator.Allocate1D(1);
- UInt128 remaining;
- int currentSize;
- int found = 0;
- while (kStart <= maxK && Volatile.Read(ref isPrime))
- {
- remaining = maxK - kStart + 1UL;
- currentSize = remaining > (UInt128)batchSize ? batchSize : (int)remaining;
- foundBuffer.MemSetToZero();
- // Precompute residue automaton bases for this batch
- UInt128 q0 = twoP * kStart + 1UL;
- ulong q0m10 = q0.Mod10();
- ulong q0m8 = q0.Mod8();
- ulong q0m3 = q0.Mod3();
- ulong q0m5 = q0.Mod5();
- ulong step10 = ((exponent % 10UL) << 1) % 10UL;
- ulong step8 = ((exponent & 7UL) << 1) & 7UL;
- ulong step3 = ((exponent % 3UL) << 1) % 3UL;
- ulong step5 = ((exponent % 5UL) << 1) % 5UL;
- var ra = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
+ var foundBuffer = accelerator.Allocate1D(1);
+ UInt128 remaining;
+ int currentSize;
+ int found = 0;
+ while (kStart <= maxK && Volatile.Read(ref isPrime))
+ {
+ remaining = maxK - kStart + 1UL;
+ currentSize = remaining > (UInt128)batchSize ? batchSize : (int)remaining;
+ foundBuffer.MemSetToZero();
+ // Precompute residue automaton bases for this batch
+ UInt128 q0 = twoP * kStart + 1UL;
+ ulong q0m10 = q0.Mod10();
+ ulong q0m8 = q0.Mod8();
+ ulong q0m3 = q0.Mod3();
+ ulong q0m5 = q0.Mod5();
+ ulong step10 = ((exponent % 10UL) << 1) % 10UL;
+ ulong step8 = ((exponent & 7UL) << 1) & 7UL;
+ ulong step3 = ((exponent % 3UL) << 1) % 3UL;
+ ulong step5 = ((exponent % 5UL) << 1) % 5UL;
+ var ra = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
- // Ensure device has small cycles table for early rejections in order kernels
+ // Ensure device has small cycles table for early rejections in order kernels
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
kernel(currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, divMul,
ra, foundBuffer.View, smallCyclesView);
- accelerator.Synchronize();
- foundBuffer.View.CopyToCPU(ref found, 1);
- if (found != 0)
- {
- Volatile.Write(ref isPrime, false);
- break;
- }
+ accelerator.Synchronize();
+ foundBuffer.View.CopyToCPU(ref found, 1);
+ if (found != 0)
+ {
+ Volatile.Write(ref isPrime, false);
+ break;
+ }
- kStart += (UInt128)currentSize;
- }
- }
+ kStart += (UInt128)currentSize;
+ }
+
+ foundBuffer.Dispose();
+ gpuLease.Dispose();
+
+ }
}
diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs
index 5679911..bc532dd 100644
--- a/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs
+++ b/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs
@@ -14,54 +14,54 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
// GPU residue variant: check 2^p % q == 1 for q = 2*p*k + 1.
public void Scan(ulong exponent, UInt128 twoP, bool lastIsSeven, UInt128 maxK, ref bool isPrime)
{
- using var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
+ var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
var accelerator = gpuLease.Accelerator;
// Ensure device has small cycles table for in-kernel lookup
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
int batchSize = GpuConstants.ScanBatchSize;
- UInt128 kStart = 1UL;
- byte last = lastIsSeven ? (byte)1 : (byte)0;
- var kernel = gpuLease.Pow2ModKernel;
+ UInt128 kStart = 1UL;
+ byte last = lastIsSeven ? (byte)1 : (byte)0;
+ var kernel = gpuLease.Pow2ModKernel;
- using var orderBuffer = accelerator.Allocate1D(batchSize);
- ulong[] orderArray = ArrayPool.Shared.Rent(batchSize);
- UInt128 remaining;
- int currentSize;
- int i;
- UInt128 batchSize128 = (UInt128)batchSize,
- q = UInt128.Zero;
- Span orders = orderArray.AsSpan(0, batchSize);
+ var orderBuffer = accelerator.Allocate1D(batchSize);
+ ulong[] orderArray = ArrayPool.Shared.Rent(batchSize);
+ UInt128 remaining;
+ int currentSize;
+ int i;
+ UInt128 batchSize128 = (UInt128)batchSize,
+ q = UInt128.Zero;
+ Span orders = orderArray.AsSpan(0, batchSize);
try
{
while (kStart < maxK && Volatile.Read(ref isPrime))
{
- remaining = maxK - kStart;
- if (remaining > batchSize128)
- {
- currentSize = batchSize;
- }
- else
- {
- currentSize = (int)remaining;
- orders = orderArray.AsSpan(0, currentSize);
- }
+ remaining = maxK - kStart;
+ if (remaining > batchSize128)
+ {
+ currentSize = batchSize;
+ }
+ else
+ {
+ currentSize = (int)remaining;
+ orders = orderArray.AsSpan(0, currentSize);
+ }
- q = twoP * kStart + UInt128.One;
- q.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
- ulong step10 = ((exponent % 10UL) << 1) % 10UL;
- ulong step8 = ((exponent & 7UL) << 1) & 7UL;
- ulong step3 = ((exponent % 3UL) << 1) % 3UL;
- ulong step5 = ((exponent % 5UL) << 1) % 5UL;
+ q = twoP * kStart + UInt128.One;
+ q.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
+ ulong step10 = ((exponent % 10UL) << 1) % 10UL;
+ ulong step8 = ((exponent & 7UL) << 1) & 7UL;
+ ulong step3 = ((exponent % 3UL) << 1) % 3UL;
+ ulong step5 = ((exponent % 5UL) << 1) % 5UL;
kernel(currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, 0UL,
q0m10, q0m8, q0m3, q0m5, orderBuffer.View, smallCyclesView);
- accelerator.Synchronize();
- orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize);
- if (!Volatile.Read(ref isPrime))
- {
- return;
- }
+ accelerator.Synchronize();
+ orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize);
+ if (!Volatile.Read(ref isPrime))
+ {
+ break;
+ }
for (i = 0; i < currentSize; i++, q += twoP)
{
@@ -72,12 +72,16 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
}
}
- kStart += batchSize128;
- }
- }
- finally
- {
- ArrayPool.Shared.Return(orderArray);
- }
- }
+ kStart += batchSize128;
+ }
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(orderArray);
+ }
+
+ orderBuffer.Dispose();
+ gpuLease.Dispose();
+
+ }
}
diff --git a/PerfectNumbers.Core/Gpu/NttGpuMath.cs b/PerfectNumbers.Core/Gpu/NttGpuMath.cs
index 4e206cf..5466748 100644
--- a/PerfectNumbers.Core/Gpu/NttGpuMath.cs
+++ b/PerfectNumbers.Core/Gpu/NttGpuMath.cs
@@ -804,19 +804,22 @@ public static class NttGpuMath
{
int n = values.Length;
int bits = (int)Math.Log2(n);
- using var gpu = GpuContextPool.Rent();
+ var gpu = GpuContextPool.Rent();
var accelerator = gpu.Accelerator;
var kernel = GetBitReverseKernel(accelerator);
var pool = ArrayPool.Shared;
var array = pool.Rent(n);
values.CopyTo(array);
- using var buffer = accelerator.Allocate1D(n);
+ var buffer = accelerator.Allocate1D(n);
buffer.View.CopyFromCPU(ref array[0], n);
kernel(n, buffer.View, bits);
accelerator.Synchronize();
buffer.View.CopyToCPU(ref array[0], n);
array.AsSpan(0, n).CopyTo(values);
pool.Return(array, clearArray: true);
+ buffer.Dispose();
+ gpu.Dispose();
+
}
public static void Forward(Span values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
@@ -863,15 +866,15 @@ public static class NttGpuMath
private static void ForwardGpuReference(Span values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
{
int n = (int)values.Length;
- using var gpu = GpuContextPool.Rent();
+ var gpu = GpuContextPool.Rent();
var accelerator = gpu.Accelerator;
var kernel = GetForwardKernel(accelerator);
var pool = ArrayPool.Shared;
var inputArray = pool.Rent(n);
var outputArray = pool.Rent(n);
values.CopyTo(inputArray);
- using var inputBuffer = accelerator.Allocate1D(n);
- using var outputBuffer = accelerator.Allocate1D(n);
+ var inputBuffer = accelerator.Allocate1D(n);
+ var outputBuffer = accelerator.Allocate1D(n);
inputBuffer.View.CopyFromCPU(ref inputArray[0], n);
UInt128 modValue = modulus;
UInt128 expBase = (modValue - 1UL) / (ulong)n;
@@ -883,6 +886,10 @@ public static class NttGpuMath
outputArray.AsSpan(0, n).CopyTo(values);
pool.Return(inputArray, clearArray: true);
pool.Return(outputArray, clearArray: true);
+ outputBuffer.Dispose();
+ inputBuffer.Dispose();
+ gpu.Dispose();
+
}
public static void Inverse(Span values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
@@ -954,15 +961,15 @@ public static class NttGpuMath
private static void InverseGpuReference(Span values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
{
int n = values.Length;
- using var gpu = GpuContextPool.Rent();
+ var gpu = GpuContextPool.Rent();
var accelerator = gpu.Accelerator;
var kernel = GetInverseKernel(accelerator);
var pool = ArrayPool.Shared;
var inputArray = pool.Rent(n);
var outputArray = pool.Rent(n);
values.CopyTo(inputArray);
- using var inputBuffer = accelerator.Allocate1D(n);
- using var outputBuffer = accelerator.Allocate1D(n);
+ var inputBuffer = accelerator.Allocate1D(n);
+ var outputBuffer = accelerator.Allocate1D(n);
inputBuffer.View.CopyFromCPU(ref inputArray[0], n);
UInt128 modValue = modulus;
UInt128 expBase = (modValue - 1UL) / (ulong)n;
@@ -993,30 +1000,40 @@ public static class NttGpuMath
outputArray.AsSpan(0, n).CopyTo(values);
pool.Return(inputArray, clearArray: true);
pool.Return(outputArray, clearArray: true);
+ outputBuffer.Dispose();
+ inputBuffer.Dispose();
+ gpu.Dispose();
+
}
private static void ForwardGpuStaged(Span values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
{
int n = values.Length;
- using var gpu = GpuContextPool.Rent();
+ var gpu = GpuContextPool.Rent();
var accelerator = gpu.Accelerator;
- using var buffer = accelerator.Allocate1D(n);
+ var buffer = accelerator.Allocate1D(n);
buffer.View.CopyFromCPU(ref values[0], n);
var cache = GetSquareCache(accelerator, n, modulus, primitiveRoot);
ForwardDevice(accelerator, buffer.View, n, modulus, cache);
buffer.View.CopyToCPU(ref values[0], n);
+ buffer.Dispose();
+ gpu.Dispose();
+
}
private static void InverseGpuStaged(Span values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
{
int n = values.Length;
- using var gpu = GpuContextPool.Rent();
+ var gpu = GpuContextPool.Rent();
var accelerator = gpu.Accelerator;
- using var buffer = accelerator.Allocate1D(n);
+ var buffer = accelerator.Allocate1D(n);
buffer.View.CopyFromCPU(ref values[0], n);
var cache = GetSquareCache(accelerator, n, modulus, primitiveRoot);
InverseDevice(accelerator, buffer.View, n, modulus, cache);
buffer.View.CopyToCPU(ref values[0], n);
+ buffer.Dispose();
+ gpu.Dispose();
+
}
private static void ForwardDevice(Accelerator accelerator, ArrayView data, int length, GpuUInt128 modulus, SquareCacheEntry cache)
@@ -1221,7 +1238,7 @@ public static class NttGpuMath
public static void PointwiseMultiply(Span left, Span right, GpuUInt128 modulus)
{
- using var gpu = GpuContextPool.Rent();
+ var gpu = GpuContextPool.Rent();
var accelerator = gpu.Accelerator;
var kernel = GetMulKernel(accelerator);
var pool = ArrayPool.Shared;
@@ -1229,8 +1246,8 @@ public static class NttGpuMath
var rightArray = pool.Rent(right.Length);
left.CopyTo(leftArray);
right.CopyTo(rightArray);
- using var leftBuffer = accelerator.Allocate1D(left.Length);
- using var rightBuffer = accelerator.Allocate1D(right.Length);
+ var leftBuffer = accelerator.Allocate1D(left.Length);
+ var rightBuffer = accelerator.Allocate1D(right.Length);
leftBuffer.View.CopyFromCPU(ref leftArray[0], left.Length);
rightBuffer.View.CopyFromCPU(ref rightArray[0], right.Length);
kernel(left.Length, leftBuffer.View, rightBuffer.View, modulus);
@@ -1241,6 +1258,10 @@ public static class NttGpuMath
rightArray.AsSpan(0, right.Length).CopyTo(right);
pool.Return(leftArray, clearArray: true);
pool.Return(rightArray, clearArray: true);
+ rightBuffer.Dispose();
+ leftBuffer.Dispose();
+ gpu.Dispose();
+
}
private static ulong[] GenerateSmallPrimes(int limit)
diff --git a/PerfectNumbers.Core/MersenneDivisorCycles.cs b/PerfectNumbers.Core/MersenneDivisorCycles.cs
index a97d1ef..3433afd 100644
--- a/PerfectNumbers.Core/MersenneDivisorCycles.cs
+++ b/PerfectNumbers.Core/MersenneDivisorCycles.cs
@@ -223,15 +223,15 @@ public class MersenneDivisorCycles
int count = (int)Math.Min(batchSizeUL, maxDivisor), i, idx;
ulong[] divisors, outCycles, validDivisors;
- divisors = pool.Rent(count);
- outCycles = pool.Rent(count);
- using var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
- var accelerator = lease.Accelerator;
- using var stream = accelerator.CreateStream();
- var kernel = accelerator.LoadAutoGroupedStreamKernel<
- Index1D,
- ArrayView1D,
- ArrayView1D>(GpuDivisorCycleKernel);
+ divisors = pool.Rent(count);
+ outCycles = pool.Rent(count);
+ var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
+ var accelerator = lease.Accelerator;
+ var stream = accelerator.CreateStream();
+ var kernel = accelerator.LoadAutoGroupedStreamKernel<
+ Index1D,
+ ArrayView1D,
+ ArrayView1D>(GpuDivisorCycleKernel);
using Stream outputStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, BufferSize10M, useAsync: true);
@@ -309,12 +309,16 @@ public class MersenneDivisorCycles
start = end + 1UL;
}
}
- finally
- {
- pool.Return(divisors, clearArray: false);
- pool.Return(outCycles, clearArray: false);
- }
- }
+ finally
+ {
+ pool.Return(divisors, clearArray: false);
+ pool.Return(outCycles, clearArray: false);
+ }
+
+ stream.Dispose();
+ lease.Dispose();
+
+ }
const byte ByteZero = 0;
const byte ByteOne = 1;
diff --git a/PerfectNumbers.Core/MersenneNumberTester.cs b/PerfectNumbers.Core/MersenneNumberTester.cs
index af4d838..e47c9b4 100644
--- a/PerfectNumbers.Core/MersenneNumberTester.cs
+++ b/PerfectNumbers.Core/MersenneNumberTester.cs
@@ -141,20 +141,20 @@ public sealed class MersenneNumberTester(
UInt128[] qs = qsBuffer;
- using var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
+ var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
var accelerator = gpuLease.Accelerator;
var orderKernel = gpuLease.OrderKernel!;
- // Guard long-running kernels by chunking the warm-up across batches.
- // Reuse the same ScanBatchSize knob used for scanning.
- int batchSize = GpuConstants.ScanBatchSize;
- ulong divMul = (ulong)((((UInt128)1 << 64) - 1UL) / exponent) + 1UL;
- int offset = 0;
- while (offset < idx)
- {
+ // Guard long-running kernels by chunking the warm-up across batches.
+ // Reuse the same ScanBatchSize knob used for scanning.
+ int batchSize = GpuConstants.ScanBatchSize;
+ ulong divMul = (ulong)((((UInt128)1 << 64) - 1UL) / exponent) + 1UL;
+ int offset = 0;
+ while (offset < idx)
+ {
int count = Math.Min(batchSize, idx - offset);
- using var qBuffer = accelerator.Allocate1D(count);
- using var orderBuffer = accelerator.Allocate1D(count);
+ var qBuffer = accelerator.Allocate1D(count);
+ var orderBuffer = accelerator.Allocate1D(count);
// Convert to device-friendly GpuUInt128 on the fly
var tmp = System.Buffers.ArrayPool.Shared.Rent(count);
for (int i = 0; i < count; i++)
@@ -167,25 +167,28 @@ public sealed class MersenneNumberTester(
orderKernel(count, exponent, divMul, qBuffer.View, orderBuffer.View);
accelerator.Synchronize();
- ulong[] orders = orderBuffer.GetAsArray1D();
- for (int i = 0; i < count; i++)
- {
- ulong order = orders[i];
- if (order == 0UL)
- {
- order = qs[offset + i].CalculateOrder();
- }
+ ulong[] orders = orderBuffer.GetAsArray1D();
+ for (int i = 0; i < count; i++)
+ {
+ ulong order = orders[i];
+ if (order == 0UL)
+ {
+ order = qs[offset + i].CalculateOrder();
+ }
- if (useOrderCache)
- {
- OrderCache[qs[offset + i]] = order;
- }
- }
+ if (useOrderCache)
+ {
+ OrderCache[qs[offset + i]] = order;
+ }
+ }
- offset += count;
- }
+ offset += count;
+ orderBuffer.Dispose();
+ qBuffer.Dispose();
+ }
- ArrayPool.Shared.Return(qs);
+ gpuLease.Dispose();
+ ArrayPool.Shared.Return(qs);
}
public bool IsMersennePrime(ulong exponent)
diff --git a/PerfectNumbers.Core/PrimeTester.cs b/PerfectNumbers.Core/PrimeTester.cs
index 6704d21..3ab68c9 100644
--- a/PerfectNumbers.Core/PrimeTester.cs
+++ b/PerfectNumbers.Core/PrimeTester.cs
@@ -81,8 +81,8 @@ public sealed class PrimeTester(bool useInternal = false)
public static void IsPrimeBatchGpu(ReadOnlySpan values, Span results)
{
// Limit concurrency and declare variables outside loops for performance and reuse
- using var limiter = GpuPrimeWorkLimiter.Acquire();
- using var gpu = GpuContextPool.Rent();
+ var limiter = GpuPrimeWorkLimiter.Acquire();
+ var gpu = GpuContextPool.Rent();
var accelerator = gpu.Accelerator;
// Get per-accelerator cached kernel and device primes buffer.
@@ -123,6 +123,8 @@ public sealed class PrimeTester(bool useInternal = false)
ArrayPool.Shared.Return(temp);
state.ReturnScratch(scratch);
+ gpu.Dispose();
+ limiter.Dispose();
}
// Per-accelerator GPU state for prime sieve (kernel + uploaded primes).
@@ -283,13 +285,13 @@ public sealed class PrimeTester(bool useInternal = false)
internal static void SharesFactorWithMaxExponentBatch(ReadOnlySpan values, Span results)
{
- using var gpu = GpuContextPool.Rent();
+ var gpu = GpuContextPool.Rent();
var accelerator = gpu.Accelerator;
var kernel = accelerator.LoadAutoGroupedStreamKernel, ArrayView>(SharesFactorKernel);
int length = values.Length;
- using var inputBuffer = accelerator.Allocate1D(length);
- using var resultBuffer = accelerator.Allocate1D(length);
+ var inputBuffer = accelerator.Allocate1D(length);
+ var resultBuffer = accelerator.Allocate1D(length);
ulong[] temp = ArrayPool.Shared.Rent(length);
values.CopyTo(temp);
@@ -298,6 +300,9 @@ public sealed class PrimeTester(bool useInternal = false)
accelerator.Synchronize();
resultBuffer.View.CopyToCPU(ref results[0], length);
ArrayPool.Shared.Return(temp);
+ resultBuffer.Dispose();
+ inputBuffer.Dispose();
+ gpu.Dispose();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]