Remove temporary by-divisor review documents
This commit is contained in:
1 parent
bd0727131f
commit
6dd5cea79f
27 files changed
+498
-600
No files matched your search
@@ -14,8 +14,6 @@ public static class GpuContextPool
|
||||
|
||||
internal sealed class PooledContext
|
||||
{
|
||||
private bool disposed;
|
||||
|
||||
public Context Context { get; }
|
||||
public Accelerator Accelerator { get; }
|
||||
public bool IsCpu { get; }
|
||||
@@ -34,10 +32,6 @@ public static class GpuContextPool
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure all cached GPU buffers for this accelerator are released.
|
||||
NttGpuMath.ClearCaches(Accelerator);
|
||||
@@ -48,7 +42,6 @@ public static class GpuContextPool
|
||||
|
||||
Accelerator.Dispose();
|
||||
Context.Dispose();
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +124,6 @@ public static class GpuContextPool
|
||||
public struct GpuContextLease
|
||||
{
|
||||
private readonly PooledContext _ctx;
|
||||
private bool disposed;
|
||||
|
||||
internal GpuContextLease(PooledContext ctx, object kernelInitLock)
|
||||
{
|
||||
@@ -150,13 +142,8 @@ public static class GpuContextPool
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Return(_ctx);
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using ILGPU;
|
||||
using ILGPU.Runtime;
|
||||
@@ -9,30 +10,28 @@ public sealed class GpuKernelLease
|
||||
{
|
||||
private static readonly ConcurrentQueue<GpuKernelLease> Pool = new();
|
||||
|
||||
private IDisposable? _limiter;
|
||||
private Action? _releaseLimiter;
|
||||
private GpuContextLease? _gpu;
|
||||
private KernelContainer? _kernels;
|
||||
private AcceleratorStream? _stream;
|
||||
private object? _executionLock;
|
||||
private bool _disposed;
|
||||
|
||||
private GpuKernelLease()
|
||||
{
|
||||
}
|
||||
|
||||
internal static GpuKernelLease Rent(IDisposable limiter, GpuContextLease gpu, KernelContainer kernels)
|
||||
internal static GpuKernelLease Rent(GpuPrimeWorkLimiter.Lease limiter, GpuContextLease gpu, KernelContainer kernels)
|
||||
{
|
||||
if (!Pool.TryDequeue(out var lease))
|
||||
{
|
||||
lease = new GpuKernelLease();
|
||||
}
|
||||
|
||||
lease._limiter = limiter;
|
||||
lease._releaseLimiter = limiter.Dispose;
|
||||
lease._gpu = gpu;
|
||||
lease._kernels = kernels;
|
||||
lease._stream = null;
|
||||
lease._executionLock = gpu.ExecutionLock;
|
||||
lease._disposed = false;
|
||||
return lease;
|
||||
}
|
||||
|
||||
@@ -158,35 +157,21 @@ public sealed class GpuKernelLease
|
||||
}
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_stream?.Dispose();
|
||||
_stream = null;
|
||||
|
||||
_gpu?.Dispose();
|
||||
_gpu = null;
|
||||
|
||||
_executionLock = null;
|
||||
|
||||
_limiter?.Dispose();
|
||||
_limiter = null;
|
||||
}
|
||||
|
||||
_kernels = null;
|
||||
_disposed = true;
|
||||
Pool.Enqueue(this);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: false);
|
||||
_stream?.Dispose();
|
||||
_stream = null;
|
||||
|
||||
_gpu?.Dispose();
|
||||
_gpu = null;
|
||||
|
||||
_executionLock = null;
|
||||
|
||||
_releaseLimiter?.Invoke();
|
||||
_releaseLimiter = null;
|
||||
|
||||
_kernels = null;
|
||||
Pool.Enqueue(this);
|
||||
}
|
||||
|
||||
public readonly struct ExecutionScope
|
||||
|
||||
@@ -6,13 +6,13 @@ public static class GpuPrimeWorkLimiter
|
||||
private static readonly object _lock = new();
|
||||
private static int _currentLimit = 1;
|
||||
|
||||
public static IDisposable Acquire()
|
||||
public static Lease Acquire()
|
||||
{
|
||||
// TODO: Inline the pooled limiter guard from the GpuLimiterThroughputBenchmarks so prime-sieve
|
||||
// GPU jobs stop allocating Releaser instances on every acquisition.
|
||||
// GPU jobs stop allocating Lease instances on every acquisition.
|
||||
var sem = _semaphore;
|
||||
sem.Wait();
|
||||
return new Releaser(sem);
|
||||
return new Lease(sem);
|
||||
}
|
||||
|
||||
public static void SetLimit(int value)
|
||||
@@ -36,25 +36,18 @@ public static class GpuPrimeWorkLimiter
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class Releaser : IDisposable
|
||||
public sealed class Lease
|
||||
{
|
||||
private readonly SemaphoreSlim _sem;
|
||||
private bool _disposed;
|
||||
|
||||
public Releaser(SemaphoreSlim sem)
|
||||
internal Lease(SemaphoreSlim sem)
|
||||
{
|
||||
_sem = sem;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_sem.Release();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ public static class GpuWorkLimiter
|
||||
private static readonly object _lock = new();
|
||||
private static int _currentLimit = 1;
|
||||
|
||||
public static IDisposable Acquire()
|
||||
public static Lease Acquire()
|
||||
{
|
||||
// TODO: Replace the per-call Releaser allocation with the pooled struct-based guard from the
|
||||
// TODO: Replace the per-call Lease allocation with the pooled struct-based guard from the
|
||||
// GpuLimiterThroughputBenchmarks so limiter acquisition does not allocate when we enter the
|
||||
// GPU scanning hot path.
|
||||
var sem = _semaphore;
|
||||
sem.Wait();
|
||||
return new Releaser(sem);
|
||||
return new Lease(sem);
|
||||
}
|
||||
|
||||
public static void SetLimit(int value)
|
||||
@@ -32,7 +32,7 @@ public static class GpuWorkLimiter
|
||||
|
||||
// Swap to a new semaphore for the new limit. We intentionally
|
||||
// do not dispose the old semaphore to avoid releasing handles
|
||||
// that may still be in use by existing Releaser instances.
|
||||
// that may still be in use by existing Lease instances.
|
||||
// TODO: Consolidate with GpuPrimeWorkLimiter so both limiters share a pooled SemaphoreSlim and avoid rebuilding the
|
||||
// limiter state whenever limits are adjusted from the CLI.
|
||||
_semaphore = new SemaphoreSlim(value, value);
|
||||
@@ -40,25 +40,18 @@ public static class GpuWorkLimiter
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class Releaser : IDisposable
|
||||
public sealed class Lease
|
||||
{
|
||||
private readonly SemaphoreSlim _sem;
|
||||
private bool _disposed;
|
||||
|
||||
public Releaser(SemaphoreSlim sem)
|
||||
internal Lease(SemaphoreSlim sem)
|
||||
{
|
||||
_sem = sem;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_sem.Release();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +220,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
ulong stepValue = (ulong)twoP128;
|
||||
byte step10 = (byte)(stepValue % 10UL);
|
||||
byte step8 = (byte)(stepValue & 7UL);
|
||||
byte step5 = (byte)(stepValue % 5UL);
|
||||
byte step3 = (byte)(stepValue % 3UL);
|
||||
byte step7 = (byte)(stepValue % 7UL);
|
||||
byte step11 = (byte)(stepValue % 11UL);
|
||||
@@ -229,7 +228,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
ulong currentDivisorValue = (ulong)currentDivisor128;
|
||||
byte remainder10 = (byte)(currentDivisorValue % 10UL);
|
||||
byte remainder8 = (byte)(currentDivisorValue & 7UL);
|
||||
byte remainder5 = (byte)(currentDivisorValue % 5UL);
|
||||
byte remainder3 = (byte)(currentDivisorValue % 3UL);
|
||||
byte remainder7 = (byte)(currentDivisorValue % 7UL);
|
||||
byte remainder11 = (byte)(currentDivisorValue % 11UL);
|
||||
@@ -275,14 +273,13 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
|
||||
byte localRemainder10 = remainder10;
|
||||
byte localRemainder8 = remainder8;
|
||||
byte localRemainder5 = remainder5;
|
||||
byte localRemainder3 = remainder3;
|
||||
byte localRemainder7 = remainder7;
|
||||
byte localRemainder11 = remainder11;
|
||||
|
||||
for (int i = 0; i < chunkCount; i++)
|
||||
{
|
||||
bool passesSmallModuli = localRemainder3 != 0 && localRemainder5 != 0 && localRemainder7 != 0 && localRemainder11 != 0;
|
||||
bool passesSmallModuli = localRemainder3 != 0 && localRemainder7 != 0 && localRemainder11 != 0;
|
||||
if (passesSmallModuli && (localRemainder8 == 1 || localRemainder8 == 7) && ((decimalMask >> localRemainder10) & 1) != 0)
|
||||
{
|
||||
ulong divisorValue = (ulong)nextDivisor128;
|
||||
@@ -290,21 +287,16 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
ulong divisorCycle = ResolveDivisorCycle(divisorValue, prime, in montgomeryData);
|
||||
if (divisorCycle == prime)
|
||||
{
|
||||
int targetIndex = admissibleCount;
|
||||
divisorSpan[targetIndex] = divisorValue;
|
||||
exponentSpan[targetIndex] = prime;
|
||||
divisorDataSpan[targetIndex] = new GpuDivisorPartialData(divisorValue);
|
||||
offsetSpan[targetIndex] = targetIndex;
|
||||
countSpan[targetIndex] = 1;
|
||||
cycleSpan[targetIndex] = divisorCycle;
|
||||
admissibleCount++;
|
||||
processedCount += (ulong)(i + 1);
|
||||
lastProcessed = divisorValue;
|
||||
coveredRange = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
nextDivisor128 += twoP128;
|
||||
localRemainder10 = AddMod10(localRemainder10, step10);
|
||||
localRemainder8 = AddMod8(localRemainder8, step8);
|
||||
localRemainder5 = AddMod5(localRemainder5, step5);
|
||||
localRemainder3 = AddMod3(localRemainder3, step3);
|
||||
localRemainder7 = AddMod7(localRemainder7, step7);
|
||||
localRemainder11 = AddMod11(localRemainder11, step11);
|
||||
@@ -312,7 +304,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
|
||||
remainder10 = localRemainder10;
|
||||
remainder8 = localRemainder8;
|
||||
remainder5 = localRemainder5;
|
||||
remainder3 = localRemainder3;
|
||||
remainder7 = localRemainder7;
|
||||
remainder11 = localRemainder11;
|
||||
@@ -380,19 +371,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
return (byte)sum;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static byte AddMod5(byte value, byte delta)
|
||||
{
|
||||
const int Modulus = 5;
|
||||
int sum = value + delta;
|
||||
|
||||
if (sum >= Modulus)
|
||||
{
|
||||
sum -= Modulus;
|
||||
}
|
||||
|
||||
return (byte)sum;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static byte AddMod7(byte value, byte delta)
|
||||
@@ -490,7 +468,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
private MemoryBuffer1D<byte, Stride1D.Dense> _hitBuffer = null!;
|
||||
private ulong[] _hostBuffer = null!;
|
||||
private int _capacity;
|
||||
private bool _disposed;
|
||||
|
||||
internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner)
|
||||
{
|
||||
@@ -502,7 +479,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
_disposed = false;
|
||||
}
|
||||
|
||||
private void EnsureExecutionResourcesLocked(int requiredCapacity)
|
||||
@@ -549,11 +525,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
|
||||
public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan<ulong> primes, Span<byte> hits)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(DivisorScanSession));
|
||||
}
|
||||
|
||||
int length = primes.Length;
|
||||
// EvenPerfectBitScanner always supplies at least one exponent per divisor check, so the guard stays commented out.
|
||||
// if (length == 0)
|
||||
@@ -622,12 +593,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
int sentinel = int.MaxValue;
|
||||
firstHitView.CopyFromCPU(ref sentinel, 1);
|
||||
|
||||
var kernel = _kernel;
|
||||
if (kernel is null)
|
||||
{
|
||||
Monitor.Exit(_lease.ExecutionLock);
|
||||
throw new InvalidOperationException("GPU divisor kernel was not initialized.");
|
||||
}
|
||||
var kernel = _kernel!;
|
||||
|
||||
kernel(1, divisorView, offsetView, countView, exponentView, cycleView, hitView, firstHitView);
|
||||
|
||||
@@ -640,12 +606,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
_owner._sessionPool.Add(this);
|
||||
}
|
||||
}
|
||||
@@ -695,7 +655,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
|
||||
private void ReturnBatchResources(BatchResources resources) => _resourcePool.Add(resources);
|
||||
|
||||
private sealed class BatchResources : IDisposable
|
||||
private sealed class BatchResources
|
||||
{
|
||||
private Accelerator? _accelerator;
|
||||
private MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense>? _divisorDataBuffer;
|
||||
|
||||
@@ -47,88 +47,83 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
|
||||
int currentSize;
|
||||
int i;
|
||||
UInt128 q = UInt128.Zero;
|
||||
try
|
||||
while (kStart <= maxK && Volatile.Read(ref isPrime))
|
||||
{
|
||||
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;
|
||||
if (_kernelType == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
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;
|
||||
if (_kernelType == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
q0.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
|
||||
var kernelArgs = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
|
||||
pow2Kernel(
|
||||
stream,
|
||||
currentSize,
|
||||
exponent,
|
||||
twoPGpu,
|
||||
(GpuUInt128)kStart,
|
||||
last,
|
||||
divMul,
|
||||
kernelArgs,
|
||||
orderBuffer.View,
|
||||
smallCyclesView,
|
||||
primeViews.LastOne,
|
||||
primeViews.LastSeven,
|
||||
primeViews.LastOnePow2,
|
||||
primeViews.LastSevenPow2);
|
||||
}
|
||||
else
|
||||
{
|
||||
q0.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
|
||||
incKernel(
|
||||
stream,
|
||||
currentSize,
|
||||
exponent,
|
||||
twoPGpu,
|
||||
(GpuUInt128)kStart,
|
||||
last,
|
||||
divMul,
|
||||
q0m10,
|
||||
q0m8,
|
||||
q0m3,
|
||||
q0m5,
|
||||
orderBuffer.View,
|
||||
smallCyclesView);
|
||||
}
|
||||
|
||||
stream.Synchronize();
|
||||
orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize);
|
||||
if (_kernelType == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
for (i = 0; i < currentSize; i++)
|
||||
{
|
||||
if (orders[i] != 0UL)
|
||||
{
|
||||
Volatile.Write(ref isPrime, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
q = twoP * kStart + UInt128.One;
|
||||
for (i = 0; i < currentSize && Volatile.Read(ref isPrime); i++, q += twoP)
|
||||
{
|
||||
if (orders[i] != 0UL && q.IsPrimeCandidate())
|
||||
{
|
||||
Volatile.Write(ref isPrime, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kStart += (UInt128)currentSize;
|
||||
q0.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
|
||||
var kernelArgs = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
|
||||
pow2Kernel(
|
||||
stream,
|
||||
currentSize,
|
||||
exponent,
|
||||
twoPGpu,
|
||||
(GpuUInt128)kStart,
|
||||
last,
|
||||
divMul,
|
||||
kernelArgs,
|
||||
orderBuffer.View,
|
||||
smallCyclesView,
|
||||
primeViews.LastOne,
|
||||
primeViews.LastSeven,
|
||||
primeViews.LastOnePow2,
|
||||
primeViews.LastSevenPow2);
|
||||
}
|
||||
else
|
||||
{
|
||||
q0.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
|
||||
incKernel(
|
||||
stream,
|
||||
currentSize,
|
||||
exponent,
|
||||
twoPGpu,
|
||||
(GpuUInt128)kStart,
|
||||
last,
|
||||
divMul,
|
||||
q0m10,
|
||||
q0m8,
|
||||
q0m3,
|
||||
q0m5,
|
||||
orderBuffer.View,
|
||||
smallCyclesView);
|
||||
}
|
||||
|
||||
stream.Synchronize();
|
||||
orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize);
|
||||
if (_kernelType == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
for (i = 0; i < currentSize; i++)
|
||||
{
|
||||
if (orders[i] != 0UL)
|
||||
{
|
||||
Volatile.Write(ref isPrime, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
q = twoP * kStart + UInt128.One;
|
||||
for (i = 0; i < currentSize && Volatile.Read(ref isPrime); i++, q += twoP)
|
||||
{
|
||||
if (orders[i] != 0UL && q.IsPrimeCandidate())
|
||||
{
|
||||
Volatile.Write(ref isPrime, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kStart += (UInt128)currentSize;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<ulong>.Shared.Return(orderArray);
|
||||
orderBuffer.Dispose();
|
||||
execution.Dispose();
|
||||
gpuLease.Dispose();
|
||||
}
|
||||
|
||||
ArrayPool<ulong>.Shared.Return(orderArray);
|
||||
orderBuffer.Dispose();
|
||||
execution.Dispose();
|
||||
gpuLease.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -152,18 +152,12 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
}
|
||||
|
||||
GpuUInt128[] buffer = ArrayPool<GpuUInt128>.Shared.Rent(count);
|
||||
try
|
||||
ComputeResidues(exponents, buffer.AsSpan(0, count));
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ComputeResidues(exponents, buffer.AsSpan(0, count));
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
results[i] = buffer[i].IsZero;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<GpuUInt128>.Shared.Return(buffer);
|
||||
results[i] = buffer[i].IsZero;
|
||||
}
|
||||
ArrayPool<GpuUInt128>.Shared.Return(buffer);
|
||||
}
|
||||
|
||||
private bool IsMersennePrimeNtt(ulong exponent, GpuUInt128 nttMod, GpuUInt128 primitiveRoot, bool runOnGpu)
|
||||
|
||||
@@ -4,19 +4,19 @@ namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuOrder)
|
||||
{
|
||||
private readonly GpuKernelType _kernelType = kernelType;
|
||||
private readonly bool _useGpuOrder = useGpuOrder;
|
||||
private readonly GpuKernelType _kernelType = kernelType;
|
||||
private readonly bool _useGpuOrder = useGpuOrder;
|
||||
|
||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||
{
|
||||
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
|
||||
var execution = gpuLease.EnterExecutionScope();
|
||||
var accelerator = gpuLease.Accelerator;
|
||||
var stream = gpuLease.Stream;
|
||||
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 = lastDigit == LastDigit.Seven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
|
||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||
{
|
||||
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
|
||||
var execution = gpuLease.EnterExecutionScope();
|
||||
var accelerator = gpuLease.Accelerator;
|
||||
var stream = gpuLease.Stream;
|
||||
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 = lastDigit == LastDigit.Seven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
|
||||
|
||||
var kernel = _kernelType switch
|
||||
{
|
||||
@@ -26,48 +26,42 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
|
||||
|
||||
var foundBuffer = accelerator.Allocate1D<int>(1); // TODO: Replace this allocation with the pooled device buffer from GpuOrderKernelBenchmarks so repeated scans reuse the pinned staging memory instead of allocating per run.
|
||||
exponent.Mod10_8_5_3Steps(out ulong step10, out ulong step8, out ulong step5, out ulong step3);
|
||||
try
|
||||
|
||||
UInt128 remaining;
|
||||
int currentSize;
|
||||
int found = 0;
|
||||
while (kStart <= maxK && Volatile.Read(ref isPrime))
|
||||
{
|
||||
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;
|
||||
// This clear is required because the result may not always be set. Remove it after the kernels are modified to always set the result.
|
||||
foundBuffer.MemSetToZero();
|
||||
// Precompute residue automaton bases for this batch
|
||||
UInt128 q0 = twoP * kStart + 1UL;
|
||||
// TODO: Replace these modulo recomputations with the divisor-cycle stepping deltas captured in
|
||||
// MersenneDivisorCycleLengthGpuBenchmarks so each batch advances via cached residues from the
|
||||
// single snapshot block instead of recalculating mod 10/8/5/3 in every iteration.
|
||||
q0.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
|
||||
var kernelArgs = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
|
||||
remaining = maxK - kStart + 1UL;
|
||||
currentSize = remaining > (UInt128)batchSize ? batchSize : (int)remaining;
|
||||
// This clear is required because the result may not always be set. Remove it after the kernels are modified to always set the result.
|
||||
foundBuffer.MemSetToZero();
|
||||
// Precompute residue automaton bases for this batch
|
||||
UInt128 q0 = twoP * kStart + 1UL;
|
||||
// TODO: Replace these modulo recomputations with the divisor-cycle stepping deltas captured in
|
||||
// MersenneDivisorCycleLengthGpuBenchmarks so each batch advances via cached residues from the
|
||||
// single snapshot block instead of recalculating mod 10/8/5/3 in every iteration.
|
||||
q0.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
|
||||
var kernelArgs = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
|
||||
|
||||
// Ensure device has small cycles table for early rejections in order kernels
|
||||
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
|
||||
kernel(stream, currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, divMul,
|
||||
kernelArgs, foundBuffer.View, smallCyclesView);
|
||||
|
||||
// Ensure device has small cycles table for early rejections in order kernels
|
||||
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
|
||||
kernel(stream, currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, divMul,
|
||||
kernelArgs, foundBuffer.View, smallCyclesView);
|
||||
stream.Synchronize();
|
||||
foundBuffer.View.CopyToCPU(ref found, 1);
|
||||
if (found != 0)
|
||||
{
|
||||
Volatile.Write(ref isPrime, false);
|
||||
break;
|
||||
}
|
||||
|
||||
stream.Synchronize();
|
||||
foundBuffer.View.CopyToCPU(ref found, 1);
|
||||
if (found != 0)
|
||||
{
|
||||
Volatile.Write(ref isPrime, false);
|
||||
break;
|
||||
}
|
||||
kStart += (UInt128)currentSize;
|
||||
}
|
||||
|
||||
kStart += (UInt128)currentSize;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
foundBuffer.Dispose();
|
||||
execution.Dispose();
|
||||
gpuLease.Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
foundBuffer.Dispose();
|
||||
execution.Dispose();
|
||||
gpuLease.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
private void ReturnResources(Accelerator accelerator, ResidueResources resources) =>
|
||||
_resourcePools.GetOrAdd(accelerator, static _ => new ConcurrentBag<ResidueResources>()).Add(resources);
|
||||
|
||||
private sealed class ResidueResources : IDisposable
|
||||
private sealed class ResidueResources
|
||||
{
|
||||
internal ResidueResources(Accelerator accelerator, int capacity)
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@ public static class NttGpuMath
|
||||
|
||||
private static readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, int>> BitReverseKernelCache = new(); // TODO: Precompute bit-reversal kernels during boot so we can remove the concurrent lookup overhead flagged in the benchmarks.
|
||||
|
||||
private sealed class SquareCacheEntry : IDisposable
|
||||
private sealed class SquareCacheEntry
|
||||
{
|
||||
public MemoryBuffer1D<GpuUInt128, Stride1D.Dense> Buffer { get; }
|
||||
public GpuUInt128 Root { get; }
|
||||
|
||||
Reference in new issue
Block a user