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)]