Avoid using declarations for GPU resources

This commit is contained in:
Marek Lemańczyk committed 2025-09-10 00:19:48 +02:00
1 parent 2be49020a9
commit 117ea94b50
9 files changed
+273 -212

No files matched your search

+5 -2
View File
@@ -542,9 +542,12 @@ public class GpuKernelPool
/// <param name="action">Action to run with (Accelerator, Stream).</param>
public static void Run(Action<Accelerator, AcceleratorStream> 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();
}
}
@@ -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<ulong>(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<ulong>.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<ulong> 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<ulong>(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<ulong>.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<ulong> 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<ulong>.Shared.Return(orderArray);
}
}
kStart += (UInt128)currentSize;
}
}
finally
{
ArrayPool<ulong>.Shared.Return(orderArray);
}
orderBuffer.Dispose();
gpuLease.Dispose();
}
}
@@ -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<GpuUInt128>(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<GpuUInt128>(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<GpuUInt128>(transformLength);
var stateBuffer = accelerator.Allocate1D<GpuUInt128>(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<byte>(1);
var resultBuffer = accelerator.Allocate1D<byte>(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<int, (GpuUInt128 Modulus, GpuUInt128 PrimitiveRoot)> NttParameterCache = new()
@@ -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<int>(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<int>(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();
}
}
@@ -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<ulong>(batchSize);
ulong[] orderArray = ArrayPool<ulong>.Shared.Rent(batchSize);
UInt128 remaining;
int currentSize;
int i;
UInt128 batchSize128 = (UInt128)batchSize,
q = UInt128.Zero;
Span<ulong> orders = orderArray.AsSpan(0, batchSize);
var orderBuffer = accelerator.Allocate1D<ulong>(batchSize);
ulong[] orderArray = ArrayPool<ulong>.Shared.Rent(batchSize);
UInt128 remaining;
int currentSize;
int i;
UInt128 batchSize128 = (UInt128)batchSize,
q = UInt128.Zero;
Span<ulong> 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<ulong>.Shared.Return(orderArray);
}
}
kStart += batchSize128;
}
}
finally
{
ArrayPool<ulong>.Shared.Return(orderArray);
}
orderBuffer.Dispose();
gpuLease.Dispose();
}
}
+36 -15
View File
@@ -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<GpuUInt128>.Shared;
var array = pool.Rent(n);
values.CopyTo(array);
using var buffer = accelerator.Allocate1D<GpuUInt128>(n);
var buffer = accelerator.Allocate1D<GpuUInt128>(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<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
@@ -863,15 +866,15 @@ public static class NttGpuMath
private static void ForwardGpuReference(Span<GpuUInt128> 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<GpuUInt128>.Shared;
var inputArray = pool.Rent(n);
var outputArray = pool.Rent(n);
values.CopyTo(inputArray);
using var inputBuffer = accelerator.Allocate1D<GpuUInt128>(n);
using var outputBuffer = accelerator.Allocate1D<GpuUInt128>(n);
var inputBuffer = accelerator.Allocate1D<GpuUInt128>(n);
var outputBuffer = accelerator.Allocate1D<GpuUInt128>(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<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
@@ -954,15 +961,15 @@ public static class NttGpuMath
private static void InverseGpuReference(Span<GpuUInt128> 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<GpuUInt128>.Shared;
var inputArray = pool.Rent(n);
var outputArray = pool.Rent(n);
values.CopyTo(inputArray);
using var inputBuffer = accelerator.Allocate1D<GpuUInt128>(n);
using var outputBuffer = accelerator.Allocate1D<GpuUInt128>(n);
var inputBuffer = accelerator.Allocate1D<GpuUInt128>(n);
var outputBuffer = accelerator.Allocate1D<GpuUInt128>(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<GpuUInt128> 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<GpuUInt128>(n);
var buffer = accelerator.Allocate1D<GpuUInt128>(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<GpuUInt128> 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<GpuUInt128>(n);
var buffer = accelerator.Allocate1D<GpuUInt128>(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<GpuUInt128> data, int length, GpuUInt128 modulus, SquareCacheEntry cache)
@@ -1221,7 +1238,7 @@ public static class NttGpuMath
public static void PointwiseMultiply(Span<GpuUInt128> left, Span<GpuUInt128> right, GpuUInt128 modulus)
{
using var gpu = GpuContextPool.Rent();
var gpu = GpuContextPool.Rent();
var accelerator = gpu.Accelerator;
var kernel = GetMulKernel(accelerator);
var pool = ArrayPool<GpuUInt128>.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<GpuUInt128>(left.Length);
using var rightBuffer = accelerator.Allocate1D<GpuUInt128>(right.Length);
var leftBuffer = accelerator.Allocate1D<GpuUInt128>(left.Length);
var rightBuffer = accelerator.Allocate1D<GpuUInt128>(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)