ConcurrentDictionary removed. Kernel made dependent on the shared GPU context.
This commit is contained in:
1 parent
9537addf38
commit
119690086a
11 files changed
+342
-314
No files matched your search
@@ -349,7 +349,7 @@ public class GpuKernelCompilationTests
|
|||||||
private static void CompileGpuKernelPoolKernels(Accelerator accelerator)
|
private static void CompileGpuKernelPoolKernels(Accelerator accelerator)
|
||||||
{
|
{
|
||||||
_ = accelerator;
|
_ = accelerator;
|
||||||
var lease = GpuKernelPool.GetKernel();
|
var lease = GpuKernelPool.Rent();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_ = lease.OrderKernel;
|
_ = lease.OrderKernel;
|
||||||
@@ -363,7 +363,7 @@ public class GpuKernelCompilationTests
|
|||||||
lease.Dispose();
|
lease.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
var orderLease = GpuKernelPool.GetKernel();
|
var orderLease = GpuKernelPool.Rent();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_ = orderLease.OrderKernel;
|
_ = orderLease.OrderKernel;
|
||||||
|
|||||||
@@ -12,324 +12,360 @@ namespace PerfectNumbers.Core;
|
|||||||
|
|
||||||
public sealed class DivisorCycleCache
|
public sealed class DivisorCycleCache
|
||||||
{
|
{
|
||||||
private const byte ByteZero = 0;
|
private const byte ByteZero = 0;
|
||||||
private const byte ByteOne = 1;
|
private const byte ByteOne = 1;
|
||||||
private const int StackBufferThreshold = 128;
|
private const int StackBufferThreshold = 128;
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<Accelerator, Action<AcceleratorStream, Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>> _gpuKernelCache = new(AcceleratorReferenceComparer.Instance);
|
private readonly Action<AcceleratorStream, Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>> _gpuKernel = LoadKernel(SharedGpuContext.Accelerator);
|
||||||
private volatile ulong[] _snapshot;
|
private volatile ulong[] _snapshot;
|
||||||
private volatile bool _useGpuGeneration = true;
|
private volatile bool _useGpuGeneration = true;
|
||||||
private readonly int _divisorCyclesBatchSize;
|
private readonly int _divisorCyclesBatchSize;
|
||||||
private static int _sharedDivisorCyclesBatchSize = GpuConstants.GpuCycleStepsPerInvocation;
|
private static int _sharedDivisorCyclesBatchSize = GpuConstants.GpuCycleStepsPerInvocation;
|
||||||
|
|
||||||
private const int CycleCacheTrackingLimit = 500_000;
|
private const int CycleCacheTrackingLimit = 500_000;
|
||||||
private static readonly HashSet<ulong> CycleCacheTrackedDivisors = new();
|
private static readonly HashSet<ulong> CycleCacheTrackedDivisors = new();
|
||||||
private static readonly object CycleCacheTrackingLock = new();
|
private static readonly object CycleCacheTrackingLock = new();
|
||||||
private static ulong CycleCacheHitCount;
|
private static ulong CycleCacheHitCount;
|
||||||
private static bool CycleCacheTrackingDisabled;
|
private static bool CycleCacheTrackingDisabled;
|
||||||
|
|
||||||
public static void SetDivisorCyclesBatchSize(int divisorCyclesBatchSize)
|
public static void SetDivisorCyclesBatchSize(int divisorCyclesBatchSize)
|
||||||
{
|
{
|
||||||
_sharedDivisorCyclesBatchSize = Math.Max(1, divisorCyclesBatchSize);
|
_sharedDivisorCyclesBatchSize = Math.Max(1, divisorCyclesBatchSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DivisorCycleCache Shared { get; } = new DivisorCycleCache(_sharedDivisorCyclesBatchSize);
|
public static DivisorCycleCache Shared { get; } = new DivisorCycleCache(_sharedDivisorCyclesBatchSize);
|
||||||
|
|
||||||
public int PreferredBatchSize => _divisorCyclesBatchSize;
|
public int PreferredBatchSize => _divisorCyclesBatchSize;
|
||||||
|
|
||||||
private DivisorCycleCache(int divisorCyclesBatchSize)
|
private DivisorCycleCache(int divisorCyclesBatchSize)
|
||||||
{
|
{
|
||||||
_divisorCyclesBatchSize = Math.Max(1, divisorCyclesBatchSize);
|
_divisorCyclesBatchSize = Math.Max(1, divisorCyclesBatchSize);
|
||||||
_snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
_snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ConfigureGeneratorDevice(bool useGpu)
|
public void ConfigureGeneratorDevice(bool useGpu)
|
||||||
{
|
{
|
||||||
_useGpuGeneration = useGpu;
|
_useGpuGeneration = useGpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RefreshSnapshot()
|
public void RefreshSnapshot()
|
||||||
{
|
{
|
||||||
_snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
_snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong GetCycleLength(ulong divisor, bool skipPrimeOrderHeuristic = false)
|
public ulong GetCycleLength(ulong divisor, bool skipPrimeOrderHeuristic = false)
|
||||||
{
|
{
|
||||||
if (!skipPrimeOrderHeuristic)
|
if (!skipPrimeOrderHeuristic)
|
||||||
{
|
{
|
||||||
Span<ulong> result = stackalloc ulong[1];
|
Span<ulong> result = stackalloc ulong[1];
|
||||||
ReadOnlySpan<ulong> singleDivisor = stackalloc ulong[1] { divisor };
|
ReadOnlySpan<ulong> singleDivisor = stackalloc ulong[1] { divisor };
|
||||||
GetCycleLengths(singleDivisor, result);
|
GetCycleLengths(singleDivisor, result);
|
||||||
return result[0];
|
return result[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// The skipPrimeOrderHeuristic path is only used for divisors produced by the CPU by-divisor scanner,
|
// The skipPrimeOrderHeuristic path is only used for divisors produced by the CPU by-divisor scanner,
|
||||||
// which always generates values greater than one. Keep this guard disabled here to avoid repeating the
|
// which always generates values greater than one. Keep this guard disabled here to avoid repeating the
|
||||||
// validation already performed by the general GetCycleLengths path.
|
// validation already performed by the general GetCycleLengths path.
|
||||||
// if (divisor <= 1UL)
|
// if (divisor <= 1UL)
|
||||||
// {
|
// {
|
||||||
// throw new InvalidDataException("Divisor must be > 1");
|
// throw new InvalidDataException("Divisor must be > 1");
|
||||||
// }
|
// }
|
||||||
|
|
||||||
ulong[] snapshot = _snapshot;
|
ulong[] snapshot = _snapshot;
|
||||||
if (divisor < (ulong)snapshot.Length)
|
if (divisor < (ulong)snapshot.Length)
|
||||||
{
|
{
|
||||||
ulong cached = snapshot[divisor];
|
ulong cached = snapshot[divisor];
|
||||||
if (cached == 0UL)
|
if (cached == 0UL)
|
||||||
{
|
{
|
||||||
throw new InvalidDataException($"Divisor cycle is missing for {divisor}");
|
throw new InvalidDataException($"Divisor cycle is missing for {divisor}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObserveCycleCacheDivisor(divisor);
|
// ObserveCycleCacheDivisor(divisor);
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
|
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
|
||||||
return MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: true);
|
return MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetCycleLengths(ReadOnlySpan<ulong> divisors, Span<ulong> cycles)
|
public void GetCycleLengths(ReadOnlySpan<ulong> divisors, Span<ulong> cycles)
|
||||||
{
|
{
|
||||||
ulong[] snapshot = _snapshot;
|
ulong[] snapshot = _snapshot;
|
||||||
int length = divisors.Length;
|
int length = divisors.Length;
|
||||||
int[]? rentedMissing = null;
|
int[]? rentedMissing = null;
|
||||||
Span<int> missingBuffer = length <= StackBufferThreshold
|
Span<int> missingBuffer = length <= StackBufferThreshold
|
||||||
? stackalloc int[length]
|
? stackalloc int[length]
|
||||||
: new Span<int>(rentedMissing = ThreadStaticPools.IntPool.Rent(length), 0, length);
|
: new Span<int>(rentedMissing = ThreadStaticPools.IntPool.Rent(length), 0, length);
|
||||||
|
|
||||||
int missingCount = 0;
|
int missingCount = 0;
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
ulong divisor = divisors[i];
|
ulong divisor = divisors[i];
|
||||||
if (divisor <= 1UL)
|
if (divisor <= 1UL)
|
||||||
{
|
{
|
||||||
throw new InvalidDataException("Divisor must be > 1");
|
throw new InvalidDataException("Divisor must be > 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (divisor < (ulong)snapshot.Length)
|
if (divisor < (ulong)snapshot.Length)
|
||||||
{
|
{
|
||||||
ulong cached = snapshot[divisor];
|
ulong cached = snapshot[divisor];
|
||||||
if (cached == 0UL)
|
if (cached == 0UL)
|
||||||
{
|
{
|
||||||
throw new InvalidDataException($"Divisor cycle is missing for {divisor}");
|
throw new InvalidDataException($"Divisor cycle is missing for {divisor}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObserveCycleCacheDivisor(divisor);
|
// ObserveCycleCacheDivisor(divisor);
|
||||||
cycles[i] = cached;
|
cycles[i] = cached;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
missingBuffer[missingCount++] = i;
|
missingBuffer[missingCount++] = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (missingCount != 0)
|
if (missingCount != 0)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<int> missingIndices = missingBuffer[..missingCount];
|
ReadOnlySpan<int> missingIndices = missingBuffer[..missingCount];
|
||||||
if (_useGpuGeneration)
|
if (_useGpuGeneration)
|
||||||
{
|
{
|
||||||
ComputeCyclesGpu(divisors, cycles, missingIndices);
|
ComputeCyclesGpu(divisors, cycles, missingIndices);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ComputeCyclesCpu(divisors, cycles, missingIndices);
|
ComputeCyclesCpu(divisors, cycles, missingIndices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rentedMissing is not null)
|
if (rentedMissing is not null)
|
||||||
{
|
{
|
||||||
ThreadStaticPools.IntPool.Return(rentedMissing, clearArray: false);
|
ThreadStaticPools.IntPool.Return(rentedMissing, clearArray: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ObserveCycleCacheDivisor(ulong divisor)
|
private static void ObserveCycleCacheDivisor(ulong divisor)
|
||||||
{
|
{
|
||||||
bool logHit = false;
|
bool logHit = false;
|
||||||
ulong hitNumber = 0UL;
|
ulong hitNumber = 0UL;
|
||||||
bool trackingDisabledNow = false;
|
bool trackingDisabledNow = false;
|
||||||
|
|
||||||
lock (CycleCacheTrackingLock)
|
lock (CycleCacheTrackingLock)
|
||||||
{
|
{
|
||||||
if (CycleCacheTrackingDisabled)
|
if (CycleCacheTrackingDisabled)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CycleCacheTrackedDivisors.Add(divisor))
|
if (!CycleCacheTrackedDivisors.Add(divisor))
|
||||||
{
|
{
|
||||||
CycleCacheHitCount++;
|
CycleCacheHitCount++;
|
||||||
hitNumber = CycleCacheHitCount;
|
hitNumber = CycleCacheHitCount;
|
||||||
logHit = true;
|
logHit = true;
|
||||||
}
|
}
|
||||||
else if (CycleCacheTrackedDivisors.Count >= CycleCacheTrackingLimit)
|
else if (CycleCacheTrackedDivisors.Count >= CycleCacheTrackingLimit)
|
||||||
{
|
{
|
||||||
CycleCacheTrackingDisabled = true;
|
CycleCacheTrackingDisabled = true;
|
||||||
CycleCacheTrackedDivisors.Clear();
|
CycleCacheTrackedDivisors.Clear();
|
||||||
trackingDisabledNow = true;
|
trackingDisabledNow = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logHit)
|
if (logHit)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Cycle cache hit for divisor {divisor} ({hitNumber})");
|
Console.WriteLine($"Cycle cache hit for divisor {divisor} ({hitNumber})");
|
||||||
}
|
}
|
||||||
else if (trackingDisabledNow)
|
else if (trackingDisabledNow)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Cycle cache hit tracking disabled after exceeding the tracking limit.");
|
Console.WriteLine("Cycle cache hit tracking disabled after exceeding the tracking limit.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static void ComputeCyclesCpu(ReadOnlySpan<ulong> divisors, Span<ulong> cycles, ReadOnlySpan<int> indices)
|
private static void ComputeCyclesCpu(ReadOnlySpan<ulong> divisors, Span<ulong> cycles, ReadOnlySpan<int> indices)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < indices.Length; i++)
|
for (int i = 0; i < indices.Length; i++)
|
||||||
{
|
{
|
||||||
int targetIndex = indices[i];
|
int targetIndex = indices[i];
|
||||||
ulong divisor = divisors[targetIndex];
|
ulong divisor = divisors[targetIndex];
|
||||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
|
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
|
||||||
cycles[targetIndex] = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData);
|
cycles[targetIndex] = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ComputeCyclesGpu(ReadOnlySpan<ulong> divisors, Span<ulong> cycles, ReadOnlySpan<int> indices)
|
private void ComputeCyclesGpu(ReadOnlySpan<ulong> divisors, Span<ulong> cycles, ReadOnlySpan<int> indices)
|
||||||
{
|
{
|
||||||
int count = indices.Length;
|
int count = indices.Length;
|
||||||
ulong[]? rentedDivisors = null;
|
ulong[]? rentedDivisors = null;
|
||||||
Span<ulong> workDivisors = count <= StackBufferThreshold
|
Span<ulong> workDivisors = count <= StackBufferThreshold
|
||||||
? stackalloc ulong[count]
|
? stackalloc ulong[count]
|
||||||
: new Span<ulong>(rentedDivisors = ThreadStaticPools.UlongPool.Rent(count), 0, count);
|
: new Span<ulong>(rentedDivisors = ThreadStaticPools.UlongPool.Rent(count), 0, count);
|
||||||
|
|
||||||
ulong[]? rentedResults = null;
|
ulong[]? rentedResults = null;
|
||||||
Span<ulong> workResults = count <= StackBufferThreshold
|
Span<ulong> workResults = count <= StackBufferThreshold
|
||||||
? stackalloc ulong[count]
|
? stackalloc ulong[count]
|
||||||
: new Span<ulong>(rentedResults = ThreadStaticPools.UlongPool.Rent(count), 0, count);
|
: new Span<ulong>(rentedResults = ThreadStaticPools.UlongPool.Rent(count), 0, count);
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
workDivisors[i] = divisors[indices[i]];
|
workDivisors[i] = divisors[indices[i]];
|
||||||
}
|
}
|
||||||
|
|
||||||
ComputeCyclesGpuCore(workDivisors, workResults);
|
ComputeCyclesGpuCore(workDivisors, workResults);
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
cycles[indices[i]] = workResults[i];
|
cycles[indices[i]] = workResults[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rentedDivisors is not null)
|
if (rentedDivisors is not null)
|
||||||
{
|
{
|
||||||
ThreadStaticPools.UlongPool.Return(rentedDivisors, clearArray: false);
|
ThreadStaticPools.UlongPool.Return(rentedDivisors, clearArray: false);
|
||||||
ThreadStaticPools.UlongPool.Return(rentedResults!, clearArray: false);
|
ThreadStaticPools.UlongPool.Return(rentedResults!, clearArray: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ComputeCyclesGpuCore(ReadOnlySpan<ulong> divisors, Span<ulong> destination)
|
private const int DefaultCapacity = 128;
|
||||||
{
|
|
||||||
int length = divisors.Length;
|
[ThreadStatic]
|
||||||
var gpuLease = GpuKernelPool.GetKernel();
|
private static MemoryBuffer1D<ulong, Stride1D.Dense>? divisorBuffer;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static MemoryBuffer1D<ulong, Stride1D.Dense>? powBuffer;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static MemoryBuffer1D<ulong, Stride1D.Dense>? orderBuffer;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static MemoryBuffer1D<ulong, Stride1D.Dense>? resultBuffer;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static MemoryBuffer1D<byte, Stride1D.Dense>? statusBuffer;
|
||||||
|
|
||||||
|
private static void EnsureCapacity(int requiredCapacity)
|
||||||
|
{
|
||||||
|
if (requiredCapacity <= (divisorBuffer?.Length ?? 0))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (divisorBuffer != null)
|
||||||
|
{
|
||||||
|
divisorBuffer.Dispose();
|
||||||
|
powBuffer!.Dispose();
|
||||||
|
orderBuffer!.Dispose();
|
||||||
|
resultBuffer!.Dispose();
|
||||||
|
statusBuffer!.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
divisorBuffer = SharedGpuContext.Accelerator.Allocate1D<ulong>(requiredCapacity);
|
||||||
|
powBuffer = SharedGpuContext.Accelerator.Allocate1D<ulong>(requiredCapacity);
|
||||||
|
orderBuffer = SharedGpuContext.Accelerator.Allocate1D<ulong>(requiredCapacity);
|
||||||
|
resultBuffer = SharedGpuContext.Accelerator.Allocate1D<ulong>(requiredCapacity);
|
||||||
|
statusBuffer = SharedGpuContext.Accelerator.Allocate1D<byte>(requiredCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ComputeCyclesGpuCore(ReadOnlySpan<ulong> divisors, Span<ulong> destination)
|
||||||
|
{
|
||||||
|
int length = divisors.Length;
|
||||||
|
var gpuLease = GpuKernelPool.Rent();
|
||||||
|
|
||||||
Accelerator accelerator = gpuLease.Accelerator;
|
Accelerator accelerator = gpuLease.Accelerator;
|
||||||
var stream = gpuLease.Stream;
|
var stream = gpuLease.Stream;
|
||||||
var kernel = _gpuKernelCache.GetOrAdd(accelerator, LoadKernel);
|
var kernel = _gpuKernel;
|
||||||
|
EnsureCapacity(length);
|
||||||
|
|
||||||
using MemoryBuffer1D<ulong, Stride1D.Dense> divisorBuffer = accelerator.Allocate1D<ulong>(length);
|
ulong[]? rentedPow = null;
|
||||||
using MemoryBuffer1D<ulong, Stride1D.Dense> powBuffer = accelerator.Allocate1D<ulong>(length);
|
Span<ulong> powSpan = length <= StackBufferThreshold
|
||||||
using MemoryBuffer1D<ulong, Stride1D.Dense> orderBuffer = accelerator.Allocate1D<ulong>(length);
|
? stackalloc ulong[length]
|
||||||
using MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = accelerator.Allocate1D<ulong>(length);
|
: new Span<ulong>(rentedPow = ThreadStaticPools.UlongPool.Rent(length), 0, length);
|
||||||
using MemoryBuffer1D<byte, Stride1D.Dense> statusBuffer = accelerator.Allocate1D<byte>(length);
|
|
||||||
|
|
||||||
ulong[]? rentedPow = null;
|
ulong[]? rentedOrder = null;
|
||||||
Span<ulong> powSpan = length <= StackBufferThreshold
|
Span<ulong> orderSpan = length <= StackBufferThreshold
|
||||||
? stackalloc ulong[length]
|
? stackalloc ulong[length]
|
||||||
: new Span<ulong>(rentedPow = ThreadStaticPools.UlongPool.Rent(length), 0, length);
|
: new Span<ulong>(rentedOrder = ThreadStaticPools.UlongPool.Rent(length), 0, length);
|
||||||
|
|
||||||
ulong[]? rentedOrder = null;
|
ulong[]? rentedResult = null;
|
||||||
Span<ulong> orderSpan = length <= StackBufferThreshold
|
Span<ulong> resultSpan = length <= StackBufferThreshold
|
||||||
? stackalloc ulong[length]
|
? stackalloc ulong[length]
|
||||||
: new Span<ulong>(rentedOrder = ThreadStaticPools.UlongPool.Rent(length), 0, length);
|
: new Span<ulong>(rentedResult = ThreadStaticPools.UlongPool.Rent(length), 0, length);
|
||||||
|
|
||||||
ulong[]? rentedResult = null;
|
byte[]? rentedStatus = null;
|
||||||
Span<ulong> resultSpan = length <= StackBufferThreshold
|
Span<byte> statusSpan = length <= StackBufferThreshold
|
||||||
? stackalloc ulong[length]
|
? stackalloc byte[length]
|
||||||
: new Span<ulong>(rentedResult = ThreadStaticPools.UlongPool.Rent(length), 0, length);
|
: new Span<byte>(rentedStatus = ThreadStaticPools.BytePool.Rent(length), 0, length);
|
||||||
|
|
||||||
byte[]? rentedStatus = null;
|
ref ulong divisorRef = ref MemoryMarshal.GetReference(divisors);
|
||||||
Span<byte> statusSpan = length <= StackBufferThreshold
|
divisorBuffer!.View.CopyFromCPU(stream, ref divisorRef, length);
|
||||||
? stackalloc byte[length]
|
|
||||||
: new Span<byte>(rentedStatus = ThreadStaticPools.BytePool.Rent(length), 0, length);
|
|
||||||
|
|
||||||
ref ulong divisorRef = ref MemoryMarshal.GetReference(divisors);
|
for (int i = 0; i < length; i++)
|
||||||
divisorBuffer.View.CopyFromCPU(stream, ref divisorRef, length);
|
{
|
||||||
|
ulong divisor = divisors[i];
|
||||||
|
ulong initialPow = 2UL;
|
||||||
|
if (initialPow >= divisor)
|
||||||
|
{
|
||||||
|
initialPow -= divisor;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
powSpan[i] = initialPow;
|
||||||
{
|
orderSpan[i] = 1UL;
|
||||||
ulong divisor = divisors[i];
|
resultSpan[i] = 0UL;
|
||||||
ulong initialPow = 2UL;
|
statusSpan[i] = ByteZero;
|
||||||
if (initialPow >= divisor)
|
}
|
||||||
{
|
|
||||||
initialPow -= divisor;
|
|
||||||
}
|
|
||||||
|
|
||||||
powSpan[i] = initialPow;
|
powBuffer!.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(powSpan), length);
|
||||||
orderSpan[i] = 1UL;
|
orderBuffer!.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(orderSpan), length);
|
||||||
resultSpan[i] = 0UL;
|
resultBuffer!.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(resultSpan), length);
|
||||||
statusSpan[i] = ByteZero;
|
statusBuffer!.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(statusSpan), length);
|
||||||
}
|
|
||||||
|
|
||||||
powBuffer.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(powSpan), length);
|
int pending;
|
||||||
orderBuffer.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(orderSpan), length);
|
do
|
||||||
resultBuffer.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(resultSpan), length);
|
{
|
||||||
statusBuffer.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(statusSpan), length);
|
|
||||||
|
|
||||||
int pending;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
kernel(stream, length, _divisorCyclesBatchSize, divisorBuffer.View, powBuffer.View, orderBuffer.View, resultBuffer.View, statusBuffer.View);
|
kernel(stream, length, _divisorCyclesBatchSize, divisorBuffer.View, powBuffer.View, orderBuffer.View, resultBuffer.View, statusBuffer.View);
|
||||||
|
|
||||||
statusBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(statusSpan), length);
|
statusBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(statusSpan), length);
|
||||||
stream.Synchronize();
|
stream.Synchronize();
|
||||||
|
|
||||||
pending = 0;
|
pending = 0;
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
if (statusSpan[i] == ByteZero)
|
if (statusSpan[i] == ByteZero)
|
||||||
{
|
{
|
||||||
pending++;
|
pending++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (pending > 0);
|
while (pending > 0);
|
||||||
|
|
||||||
resultBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(resultSpan), length);
|
resultBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(resultSpan), length);
|
||||||
stream.Synchronize();
|
stream.Synchronize();
|
||||||
resultSpan.CopyTo(destination);
|
|
||||||
if (rentedPow is not null)
|
|
||||||
{
|
|
||||||
ThreadStaticPools.UlongPool.Return(rentedPow, clearArray: false);
|
|
||||||
ThreadStaticPools.UlongPool.Return(rentedOrder!, clearArray: false);
|
|
||||||
ThreadStaticPools.UlongPool.Return(rentedResult!, clearArray: false);
|
|
||||||
ThreadStaticPools.BytePool.Return(rentedStatus!, clearArray: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
gpuLease.Dispose();
|
resultSpan.CopyTo(destination);
|
||||||
}
|
if (rentedPow is not null)
|
||||||
|
{
|
||||||
|
ThreadStaticPools.UlongPool.Return(rentedPow, clearArray: false);
|
||||||
|
ThreadStaticPools.UlongPool.Return(rentedOrder!, clearArray: false);
|
||||||
|
ThreadStaticPools.UlongPool.Return(rentedResult!, clearArray: false);
|
||||||
|
ThreadStaticPools.BytePool.Return(rentedStatus!, clearArray: false);
|
||||||
|
}
|
||||||
|
|
||||||
private static Action<AcceleratorStream, Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>> LoadKernel(Accelerator accelerator)
|
gpuLease.Dispose();
|
||||||
{
|
}
|
||||||
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>(DivisorCycleKernels.GpuAdvanceDivisorCyclesKernel);
|
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
|
||||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class AcceleratorReferenceComparer : IEqualityComparer<Accelerator>
|
private static Action<AcceleratorStream, Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>> LoadKernel(Accelerator accelerator)
|
||||||
{
|
{
|
||||||
internal static AcceleratorReferenceComparer Instance { get; } = new();
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>(DivisorCycleKernels.GpuAdvanceDivisorCyclesKernel);
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>>();
|
||||||
|
}
|
||||||
|
|
||||||
public bool Equals(Accelerator? x, Accelerator? y) => ReferenceEquals(x, y);
|
private sealed class AcceleratorReferenceComparer : IEqualityComparer<Accelerator>
|
||||||
|
{
|
||||||
|
internal static AcceleratorReferenceComparer Instance { get; } = new();
|
||||||
|
|
||||||
public int GetHashCode(Accelerator obj) => RuntimeHelpers.GetHashCode(obj);
|
public bool Equals(Accelerator? x, Accelerator? y) => ReferenceEquals(x, y);
|
||||||
}
|
|
||||||
|
public int GetHashCode(Accelerator obj) => RuntimeHelpers.GetHashCode(obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public readonly struct ResiduePrimeViews(
|
|||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> LastSevenPow2 = lastSevenPow2;
|
public readonly ArrayView1D<ulong, Stride1D.Dense> LastSevenPow2 = lastSevenPow2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly struct SmallPrimeFactorTables(
|
public readonly struct SmallPrimeFactorViews(
|
||||||
MemoryBuffer1D<uint, Stride1D.Dense> primes,
|
MemoryBuffer1D<uint, Stride1D.Dense> primes,
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense> squares,
|
MemoryBuffer1D<ulong, Stride1D.Dense> squares,
|
||||||
int count)
|
int count)
|
||||||
@@ -184,18 +184,13 @@ public class GpuKernelPool
|
|||||||
return new ResiduePrimeViews(deviceLastOne.View, deviceLastSeven.View, deviceLastOnePow2.View, deviceLastSevenPow2.View);
|
return new ResiduePrimeViews(deviceLastOne.View, deviceLastSeven.View, deviceLastOnePow2.View, deviceLastSevenPow2.View);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SmallPrimeFactorTables EnsureSmallPrimeFactorTables(KernelContainer kernels, Accelerator accelerator, AcceleratorStream stream)
|
public static SmallPrimeFactorViews EnsureSmallPrimeFactorTables(KernelContainer kernels, Accelerator accelerator, AcceleratorStream stream)
|
||||||
{
|
{
|
||||||
if (kernels.SmallPrimeFactorsPrimes is { } primeBuffer && kernels.SmallPrimeFactorsSquares is { } squareBuffer)
|
if (kernels.SmallPrimeFactorsPrimes is { } primeBuffer && kernels.SmallPrimeFactorsSquares is { } squareBuffer)
|
||||||
{
|
{
|
||||||
return new SmallPrimeFactorTables(primeBuffer, squareBuffer, (int)primeBuffer.Length);
|
return new SmallPrimeFactorViews(primeBuffer, squareBuffer, (int)primeBuffer.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (kernels.SmallPrimeFactorsPrimes is { } existingPrimes && kernels.SmallPrimeFactorsSquares is { } existingSquares)
|
|
||||||
// {
|
|
||||||
// return new SmallPrimeFactorTables(existingPrimes, existingSquares, (int)existingPrimes.Length);
|
|
||||||
// }
|
|
||||||
|
|
||||||
var hostPrimes = PrimesGenerator.SmallPrimes;
|
var hostPrimes = PrimesGenerator.SmallPrimes;
|
||||||
var hostSquares = PrimesGenerator.SmallPrimesPow2;
|
var hostSquares = PrimesGenerator.SmallPrimesPow2;
|
||||||
|
|
||||||
@@ -207,17 +202,13 @@ public class GpuKernelPool
|
|||||||
kernels.SmallPrimeFactorsPrimes = devicePrimes;
|
kernels.SmallPrimeFactorsPrimes = devicePrimes;
|
||||||
kernels.SmallPrimeFactorsSquares = deviceSquares;
|
kernels.SmallPrimeFactorsSquares = deviceSquares;
|
||||||
|
|
||||||
return new SmallPrimeFactorTables(devicePrimes, deviceSquares, hostPrimes.Length);
|
return new SmallPrimeFactorViews(devicePrimes, deviceSquares, hostPrimes.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static GpuKernelLease Rent()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static GpuKernelLease GetKernel()
|
|
||||||
{
|
{
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
var gpu = Rent();
|
var gpu = GpuContextPool.Rent();
|
||||||
var stream = gpu.Accelerator.CreateStream();
|
var stream = gpu.Accelerator.CreateStream();
|
||||||
var kernels = GetKernels(gpu.Accelerator, stream);
|
var kernels = GetKernels(gpu.Accelerator, stream);
|
||||||
return GpuKernelLease.Rent(gpu, stream, kernels);
|
return GpuKernelLease.Rent(gpu, stream, kernels);
|
||||||
@@ -229,7 +220,7 @@ public class GpuKernelPool
|
|||||||
/// <param name="action">Action to run with (Accelerator, Stream).</param>
|
/// <param name="action">Action to run with (Accelerator, Stream).</param>
|
||||||
public static void Run(Action<Accelerator, AcceleratorStream> action)
|
public static void Run(Action<Accelerator, AcceleratorStream> action)
|
||||||
{
|
{
|
||||||
var lease = GetKernel();
|
var lease = Rent();
|
||||||
var accelerator = lease.Accelerator;
|
var accelerator = lease.Accelerator;
|
||||||
var stream = lease.Stream;
|
var stream = lease.Stream;
|
||||||
action(accelerator, stream);
|
action(accelerator, stream);
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
ulong processedCount;
|
ulong processedCount;
|
||||||
ulong lastProcessed;
|
ulong lastProcessed;
|
||||||
|
|
||||||
var gpuLease = GpuKernelPool.GetKernel();
|
var gpuLease = GpuKernelPool.Rent();
|
||||||
var accelerator = gpuLease.Accelerator;
|
var accelerator = gpuLease.Accelerator;
|
||||||
var stream = gpuLease.Stream;
|
var stream = gpuLease.Stream;
|
||||||
|
|
||||||
@@ -501,7 +501,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner)
|
internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner)
|
||||||
{
|
{
|
||||||
_owner = owner;
|
_owner = owner;
|
||||||
_lease = GpuKernelPool.GetKernel();
|
_lease = GpuKernelPool.Rent();
|
||||||
_accelerator = _lease.Accelerator;
|
_accelerator = _lease.Accelerator;
|
||||||
_stream = _lease.Stream;
|
_stream = _lease.Stream;
|
||||||
_capacity = Math.Max(1, owner._gpuBatchSize);
|
_capacity = Math.Max(1, owner._gpuBatchSize);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
|
|||||||
{
|
{
|
||||||
throw new NotImplementedException($"GPU incremental scanning requires the device cycle heuristics implementation (kernel: {_kernelType}, GPU order: {_useGpuOrder}).");
|
throw new NotImplementedException($"GPU incremental scanning requires the device cycle heuristics implementation (kernel: {_kernelType}, GPU order: {_useGpuOrder}).");
|
||||||
|
|
||||||
var gpuLease = GpuKernelPool.GetKernel();
|
var gpuLease = GpuKernelPool.Rent();
|
||||||
var accelerator = gpuLease.Accelerator;
|
var accelerator = gpuLease.Accelerator;
|
||||||
var stream = gpuLease.Stream;
|
var stream = gpuLease.Stream;
|
||||||
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy
|
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
|
|||||||
|
|
||||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||||
{
|
{
|
||||||
var gpuLease = GpuKernelPool.GetKernel();
|
var gpuLease = GpuKernelPool.Rent();
|
||||||
var accelerator = gpuLease.Accelerator;
|
var accelerator = gpuLease.Accelerator;
|
||||||
var stream = gpuLease.Stream;
|
var stream = gpuLease.Stream;
|
||||||
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR
|
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var lease = GpuKernelPool.GetKernel();
|
var lease = GpuKernelPool.Rent();
|
||||||
Accelerator accelerator = lease.Accelerator;
|
Accelerator accelerator = lease.Accelerator;
|
||||||
AcceleratorStream stream = lease.Stream;
|
AcceleratorStream stream = lease.Stream;
|
||||||
|
|
||||||
@@ -365,7 +365,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
{
|
{
|
||||||
order = 0UL;
|
order = 0UL;
|
||||||
|
|
||||||
var lease = GpuKernelPool.GetKernel();
|
var lease = GpuKernelPool.Rent();
|
||||||
Accelerator accelerator = lease.Accelerator;
|
Accelerator accelerator = lease.Accelerator;
|
||||||
AcceleratorStream stream = lease.Stream;
|
AcceleratorStream stream = lease.Stream;
|
||||||
|
|
||||||
@@ -526,7 +526,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
|
|
||||||
private static bool TryComputeOnGpu(ReadOnlySpan<ulong> exponents, ulong prime, in MontgomeryDivisorData divisorData, Span<ulong> results)
|
private static bool TryComputeOnGpu(ReadOnlySpan<ulong> exponents, ulong prime, in MontgomeryDivisorData divisorData, Span<ulong> results)
|
||||||
{
|
{
|
||||||
var lease = GpuKernelPool.GetKernel();
|
var lease = GpuKernelPool.Rent();
|
||||||
Accelerator accelerator = lease.Accelerator;
|
Accelerator accelerator = lease.Accelerator;
|
||||||
AcceleratorStream stream = lease.Stream;
|
AcceleratorStream stream = lease.Stream;
|
||||||
var kernel = GetPow2ModKernel(accelerator);
|
var kernel = GetPow2ModKernel(accelerator);
|
||||||
@@ -581,7 +581,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
|
|
||||||
GpuUInt128[]? rentedExponents = null;
|
GpuUInt128[]? rentedExponents = null;
|
||||||
GpuUInt128[]? rentedResults = null;
|
GpuUInt128[]? rentedResults = null;
|
||||||
var lease = GpuKernelPool.GetKernel();
|
var lease = GpuKernelPool.Rent();
|
||||||
ArrayPool<GpuUInt128> gpuUInt128Pool = ThreadStaticPools.GpuUInt128Pool;
|
ArrayPool<GpuUInt128> gpuUInt128Pool = ThreadStaticPools.GpuUInt128Pool;
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -804,7 +804,7 @@ public class MersenneDivisorCycles
|
|||||||
|
|
||||||
divisors = pool.Rent(count);
|
divisors = pool.Rent(count);
|
||||||
outCycles = pool.Rent(count);
|
outCycles = pool.Rent(count);
|
||||||
var lease = GpuKernelPool.GetKernel();
|
var lease = GpuKernelPool.Rent();
|
||||||
var accelerator = lease.Accelerator;
|
var accelerator = lease.Accelerator;
|
||||||
var stream = lease.Stream;
|
var stream = lease.Stream;
|
||||||
var loaded = accelerator.LoadAutoGroupedStreamKernel<
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ public sealed class MersenneNumberTester(
|
|||||||
|
|
||||||
UInt128[] qs = qsBuffer;
|
UInt128[] qs = qsBuffer;
|
||||||
|
|
||||||
var gpuLease = GpuKernelPool.GetKernel();
|
var gpuLease = GpuKernelPool.Rent();
|
||||||
var accelerator = gpuLease.Accelerator;
|
var accelerator = gpuLease.Accelerator;
|
||||||
var stream = gpuLease.Stream;
|
var stream = gpuLease.Stream;
|
||||||
var orderKernel = gpuLease.OrderKernel!;
|
var orderKernel = gpuLease.OrderKernel!;
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ internal static partial class PrimeOrderCalculator
|
|||||||
Span<int> exponentBuffer = exponentBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots);
|
Span<int> exponentBuffer = exponentBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots);
|
||||||
remaining = value;
|
remaining = value;
|
||||||
|
|
||||||
var lease = GpuKernelPool.GetKernel();
|
var lease = GpuKernelPool.Rent();
|
||||||
Accelerator accelerator = lease.Accelerator;
|
Accelerator accelerator = lease.Accelerator;
|
||||||
var stream = lease.Stream;
|
var stream = lease.Stream;
|
||||||
KernelContainer kernels = lease.Kernels;
|
KernelContainer kernels = lease.Kernels;
|
||||||
|
|
||||||
ScratchBuffer scratch = GpuScratchBufferPool.Rent(accelerator, GpuSmallPrimeFactorSlots, 0);
|
ScratchBuffer scratch = GpuScratchBufferPool.Rent(accelerator, GpuSmallPrimeFactorSlots, 0);
|
||||||
|
|
||||||
SmallPrimeFactorTables tables = GpuKernelPool.EnsureSmallPrimeFactorTables(kernels, accelerator, stream);
|
SmallPrimeFactorViews tables = GpuKernelPool.EnsureSmallPrimeFactorTables(kernels, accelerator, stream);
|
||||||
|
|
||||||
if (kernels.SmallPrimeFactorsPrimes is null) throw new NullReferenceException($"{nameof(kernels.SmallPrimeFactorsPrimes)} is null");
|
if (kernels.SmallPrimeFactorsPrimes is null) throw new NullReferenceException($"{nameof(kernels.SmallPrimeFactorsPrimes)} is null");
|
||||||
if (kernels.SmallPrimeFactorsSquares is null) throw new NullReferenceException($"{nameof(kernels.SmallPrimeFactorsSquares)} is null");
|
if (kernels.SmallPrimeFactorsSquares is null) throw new NullReferenceException($"{nameof(kernels.SmallPrimeFactorsSquares)} is null");
|
||||||
@@ -100,7 +100,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
factorSpan[i] = factors[i].Value;
|
factorSpan[i] = factors[i].Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
GpuKernelLease lease = GpuKernelPool.GetKernel();
|
GpuKernelLease lease = GpuKernelPool.Rent();
|
||||||
Accelerator accelerator = lease.Accelerator;
|
Accelerator accelerator = lease.Accelerator;
|
||||||
|
|
||||||
var stream = lease.Stream;
|
var stream = lease.Stream;
|
||||||
|
|||||||
@@ -440,7 +440,7 @@ public static partial class ULongExtensions
|
|||||||
|
|
||||||
private static class Pow2MontgomeryGpuExecutor
|
private static class Pow2MontgomeryGpuExecutor
|
||||||
{
|
{
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Pow2MontgomeryKernelGroup> KernelCache = new();
|
private static readonly Pow2MontgomeryKernelGroup Kernel = Pow2MontgomeryKernelGroup.Create(SharedGpuContext.Accelerator);
|
||||||
|
|
||||||
public static ulong ExecuteKeep(ulong exponent, in MontgomeryDivisorData divisor)
|
public static ulong ExecuteKeep(ulong exponent, in MontgomeryDivisorData divisor)
|
||||||
{
|
{
|
||||||
@@ -452,24 +452,25 @@ public static partial class ULongExtensions
|
|||||||
return Execute(exponent, divisor, useKeepKernel: false);
|
return Execute(exponent, divisor, useKeepKernel: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static MemoryBuffer1D<ulong, Stride1D.Dense>? _exponentBuffer;
|
||||||
|
[ThreadStatic]
|
||||||
|
private static MemoryBuffer1D<ulong, Stride1D.Dense>? _resultBuffer;
|
||||||
|
|
||||||
private static ulong Execute(ulong exponent, in MontgomeryDivisorData divisor, bool useKeepKernel)
|
private static ulong Execute(ulong exponent, in MontgomeryDivisorData divisor, bool useKeepKernel)
|
||||||
{
|
{
|
||||||
ulong result = 0UL;
|
ulong result = 0UL;
|
||||||
|
|
||||||
GpuKernelLease lease = GpuKernelPool.GetKernel();
|
GpuKernelLease lease = GpuKernelPool.Rent();
|
||||||
|
|
||||||
Accelerator accelerator = lease.Accelerator;
|
Accelerator accelerator = lease.Accelerator;
|
||||||
AcceleratorStream stream = lease.Stream;
|
AcceleratorStream stream = lease.Stream;
|
||||||
// Keep this commented out. It should never happen in production code.
|
var kernelGroup = Kernel;
|
||||||
// if (accelerator.AcceleratorType == AcceleratorType.CPU)
|
|
||||||
// {
|
|
||||||
// return 0UL;
|
|
||||||
// }
|
|
||||||
|
|
||||||
var kernelGroup = KernelCache.GetOrAdd(accelerator, static accel => Pow2MontgomeryKernelGroup.Create(accel));
|
EnsureCapacity();
|
||||||
|
|
||||||
var exponentBuffer = accelerator.Allocate1D<ulong>(1);
|
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = _exponentBuffer!;
|
||||||
var resultBuffer = accelerator.Allocate1D<ulong>(1);
|
MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = _resultBuffer!;
|
||||||
|
|
||||||
exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1);
|
exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1);
|
||||||
// We don't need to worry about any left-overs here.
|
// We don't need to worry about any left-overs here.
|
||||||
@@ -483,19 +484,19 @@ public static partial class ULongExtensions
|
|||||||
resultBuffer.Dispose();
|
resultBuffer.Dispose();
|
||||||
exponentBuffer.Dispose();
|
exponentBuffer.Dispose();
|
||||||
lease.Dispose();
|
lease.Dispose();
|
||||||
// Keep this commented. We don't want to catch any exceptions. All should crash the scanner.
|
|
||||||
// catch (AcceleratorException)
|
|
||||||
// {
|
|
||||||
// return 0UL;
|
|
||||||
// }
|
|
||||||
// catch (NotSupportedException)
|
|
||||||
// {
|
|
||||||
// return 0UL;
|
|
||||||
// }
|
|
||||||
// Intentionally avoid exception handling here; any accelerator failure should crash the scanner.
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EnsureCapacity()
|
||||||
|
{
|
||||||
|
if (_exponentBuffer == null)
|
||||||
|
{
|
||||||
|
Accelerator accelerator = SharedGpuContext.Accelerator;
|
||||||
|
_exponentBuffer = accelerator.Allocate1D<ulong>(1);
|
||||||
|
_resultBuffer = accelerator.Allocate1D<ulong>(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class Pow2MontgomeryKernelGroup
|
private sealed class Pow2MontgomeryKernelGroup
|
||||||
{
|
{
|
||||||
internal Pow2MontgomeryKernelGroup(
|
internal Pow2MontgomeryKernelGroup(
|
||||||
|
|||||||
Reference in new issue
Block a user