Factoring switched to PrimeTester.IsPrime for the best performance. PrimeOrderCalculator.PartialFactor switched to GPU factorization. Every so often it will send work to GPU instead of CPU. BatchedResources renamed to MersenneNumberDivisorByDivisorAccelerator for consistency and moved to its own file. It now uses array instead of dictionary for lookup free performance.

This commit is contained in:
mlemanczyk committed 2025-11-23 00:07:03 +01:00
1 parent 7b09c1fd3b
commit 6d1ff1372e
7 files changed
+202 -240

No files matched your search

@@ -0,0 +1,119 @@
using System.Buffers;
using ILGPU;
using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu;
public sealed class MersenneNumberDivisorByDivisorAccelerator
{
internal MersenneNumberDivisorByDivisorAccelerator(Accelerator accelerator, int capacity)
{
ArrayPool<ulong> ulongPool = ThreadStaticPools.UlongPool;
ArrayPool<int> intPool = ThreadStaticPools.IntPool;
ArrayPool<GpuDivisorPartialData> partialPool = ThreadStaticPools.GpuDivisorPartialDataPool;
Divisors = ulongPool.Rent(capacity);
Exponents = ulongPool.Rent(capacity);
DivisorData = partialPool.Rent(capacity);
Offsets = intPool.Rent(capacity);
Counts = intPool.Rent(capacity);
Cycles = ulongPool.Rent(capacity);
lock (accelerator)
{
DivisorDataBuffer = accelerator.Allocate1D<GpuDivisorPartialData>(capacity);
OffsetBuffer = accelerator.Allocate1D<int>(capacity);
CountBuffer = accelerator.Allocate1D<int>(capacity);
CycleBuffer = accelerator.Allocate1D<ulong>(capacity);
ExponentBuffer = accelerator.Allocate1D<ulong>(capacity);
HitsBuffer = accelerator.Allocate1D<byte>(capacity);
HitIndexBuffer = accelerator.Allocate1D<int>(1);
CheckDivisorKernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>(DivisorByDivisorKernels.CheckKernel));
}
}
internal MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense> DivisorDataBuffer;
internal MemoryBuffer1D<int, Stride1D.Dense> OffsetBuffer;
internal MemoryBuffer1D<int, Stride1D.Dense> CountBuffer;
internal MemoryBuffer1D<ulong, Stride1D.Dense> CycleBuffer;
internal MemoryBuffer1D<ulong, Stride1D.Dense> ExponentBuffer;
internal MemoryBuffer1D<byte, Stride1D.Dense> HitsBuffer;
internal MemoryBuffer1D<int, Stride1D.Dense> HitIndexBuffer;
internal ulong[] Divisors;
internal ulong[] Exponents;
internal GpuDivisorPartialData[] DivisorData;
internal int[] Offsets;
internal int[] Counts;
internal ulong[] Cycles;
internal readonly Kernel CheckDivisorKernel;
public void EnsureCapacity(Accelerator accelerator, int requiredCapacity)
{
if (DivisorDataBuffer.Length < requiredCapacity)
{
ReallocateBuffers(accelerator, requiredCapacity);
}
}
private void ReallocateBuffers(Accelerator accelerator, int capacity)
{
DivisorDataBuffer.Dispose();
OffsetBuffer.Dispose();
CountBuffer.Dispose();
CycleBuffer.Dispose();
ExponentBuffer.Dispose();
HitsBuffer.Dispose();
HitIndexBuffer.Dispose();
lock (accelerator)
{
DivisorDataBuffer = accelerator.Allocate1D<GpuDivisorPartialData>(capacity);
OffsetBuffer = accelerator.Allocate1D<int>(capacity);
CountBuffer = accelerator.Allocate1D<int>(capacity);
CycleBuffer = accelerator.Allocate1D<ulong>(capacity);
ExponentBuffer = accelerator.Allocate1D<ulong>(capacity);
HitsBuffer = accelerator.Allocate1D<byte>(capacity);
HitIndexBuffer = accelerator.Allocate1D<int>(1);
}
ArrayPool<ulong> ulongPool = ThreadStaticPools.UlongPool;
ArrayPool<int> intPool = ThreadStaticPools.IntPool;
ArrayPool<GpuDivisorPartialData> partialPool = ThreadStaticPools.GpuDivisorPartialDataPool;
ulongPool.Return(Divisors, clearArray: false);
ulongPool.Return(Exponents, clearArray: false);
partialPool.Return(DivisorData, clearArray: false);
intPool.Return(Offsets, clearArray: false);
intPool.Return(Counts, clearArray: false);
ulongPool.Return(Cycles, clearArray: false);
Divisors = ulongPool.Rent(capacity);
Exponents = ulongPool.Rent(capacity);
DivisorData = partialPool.Rent(capacity);
Offsets = intPool.Rent(capacity);
Counts = intPool.Rent(capacity);
Cycles = ulongPool.Rent(capacity);
}
public void Dispose()
{
DivisorDataBuffer.Dispose();
OffsetBuffer.Dispose();
CountBuffer.Dispose();
CycleBuffer.Dispose();
ExponentBuffer.Dispose();
HitsBuffer.Dispose();
HitIndexBuffer.Dispose();
ArrayPool<ulong> ulongPool = ThreadStaticPools.UlongPool;
ArrayPool<int> intPool = ThreadStaticPools.IntPool;
ArrayPool<GpuDivisorPartialData> partialPool = ThreadStaticPools.GpuDivisorPartialDataPool;
ulongPool.Return(Divisors, clearArray: false);
ulongPool.Return(Exponents, clearArray: false);
partialPool.Return(DivisorData, clearArray: false);
intPool.Return(Offsets, clearArray: false);
intPool.Return(Counts, clearArray: false);
ulongPool.Return(Cycles, clearArray: false);
}
}
@@ -69,7 +69,7 @@ public sealed class PrimeOrderCalculatorAccelerator
AcceleratorStream stream = accelerator.CreateStream();
// TODO: Review which tables are needed when final execution path is defined.
LastDigitGpuTables.WarmUp(i, stream);
HeuristicCombinedGpuTables.WarmUp(i, stream);
// HeuristicCombinedGpuTables.WarmUp(i, stream);
// SmallPrimeFactorGpuTables.WarmUp(i, stream);
// SharedHeuristicGpuTables.EnsureStaticTables(accelerator, stream);
// _ = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.None);
@@ -115,13 +115,13 @@ public sealed class PrimeOrderCalculatorAccelerator
public Dictionary<ulong, int> Pow2ModEntriesToTestOnHost = [];
public readonly Action<AcceleratorStream, int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>> CheckFactorsKernelLauncher;
public readonly Kernel CheckFactorsKernel;
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>> ConvertToStandardKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews> HeuristicCombinedTrialDivisionKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>> KeepMontgomeryKernelLauncher;
public readonly Action<AcceleratorStream, ulong, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> PollardRhoKernelLauncher;
public readonly Kernel PollardRhoKernel;
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>> Pow2ModWideKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<byte>> SharesFactorKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SmallPrimeFactorKernelLauncher;
@@ -247,9 +247,9 @@ public sealed class PrimeOrderCalculatorAccelerator
DevicePrimesPow2LastThree = lastDigitSharedTables.DevicePrimesPow2LastThree.View;
DevicePrimesPow2LastNine = lastDigitSharedTables.DevicePrimesPow2LastNine.View;
DivisorTables = HeuristicCombinedGpuTables
.GetStaticTables(acceleratorIndex)
.CreateViews();
// DivisorTables = HeuristicCombinedGpuTables
// .GetStaticTables(acceleratorIndex)
// .CreateViews();
var smallPrimeFactorTables = SmallPrimeFactorGpuTables.GetStaticTables(acceleratorIndex);
@@ -260,7 +260,7 @@ public sealed class PrimeOrderCalculatorAccelerator
SmallPrimeFactorSquares = tables.Squares.View;
}
CheckFactorsKernelLauncher = KernelUtil.GetKernel(accelerator.LoadStreamKernel<int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderGpuHeuristics.CheckFactorsKernel)).CreateLauncherDelegate<Action<AcceleratorStream, int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
CheckFactorsKernel = KernelUtil.GetKernel(accelerator.LoadStreamKernel<int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderGpuHeuristics.CheckFactorsKernel));
ConvertToStandardKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelConvertToStandard)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
@@ -268,7 +268,7 @@ public sealed class PrimeOrderCalculatorAccelerator
KeepMontgomeryKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelKeepMontgomery)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
PollardRhoKernelLauncher = KernelUtil.GetKernel(accelerator.LoadStreamKernel<ulong, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.TryPollardRhoKernel)).CreateLauncherDelegate<Action<AcceleratorStream, ulong, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
PollardRhoKernel = KernelUtil.GetKernel(accelerator.LoadStreamKernel<ulong, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.TryPollardRhoKernel));
Pow2ModWideKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>>(PrimeOrderGpuHeuristics.Pow2ModKernelWide)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>>>();
@@ -1,4 +1,3 @@
using System.Buffers;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -12,7 +11,7 @@ namespace PerfectNumbers.Core.Gpu;
/// Scans Mersenne divisors on the GPU for prime exponents p >= 31 using cached GPU divisor partial data.
/// Consumers must call <see cref="ConfigureFromMaxPrime"/> before invoking other members so the divisor limits are populated.
/// </summary>
public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDivisorByDivisorTester
public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDivisorByDivisorTester
{
private int _gpuBatchSize = GpuConstants.ScanBatchSize;
// EvenPerfectBitScanner configures the GPU tester once before scanning and never mutates the configuration afterwards,
@@ -21,27 +20,10 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
private ulong _divisorLimit;
// private bool _isConfigured;
[ThreadStatic]
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>>? _kernel;
private readonly ConcurrentBag<MersenneNumberDivisorByDivisorAccelerator>[] _resourcePool = [..AcceleratorPool.Shared.Accelerators.Select(x => new ConcurrentBag<MersenneNumberDivisorByDivisorAccelerator>())];
private readonly ConcurrentBag<BatchResources> _resourcePool = [];
private readonly ConcurrentBag<DivisorScanSession> _sessionPool = [];
private static Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> GetKernel(Accelerator accelerator)
{
var pool = _kernel ??= [];
if (pool.TryGetValue(accelerator, out var cached))
{
return cached;
}
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>(DivisorByDivisorKernels.CheckKernel);
var kernel = KernelUtil.GetKernel(loaded);
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>>();
pool[accelerator] = cached;
return cached;
}
public int GpuBatchSize
{
get => _gpuBatchSize;
@@ -107,20 +89,16 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
// var accelerator = _accelerators[acceleratorIndex];
var gpu = PrimeOrderCalculatorAccelerator.Rent(batchCapacity);
var acceleratorIndex = gpu.AcceleratorIndex;
var accelerator = gpu.Accelerator;
var stream = AcceleratorStreamPool.Rent(acceleratorIndex);
// Monitor.Enter(gpuLease.ExecutionLock);
var kernel = GetKernel(accelerator);
var resources = RentBatchResources(accelerator, batchCapacity);
var resources = RentBatchResources(acceleratorIndex, batchCapacity);
composite = CheckDivisors(
gpu,
prime,
allowedMax,
kernel,
stream,
resources.CheckDivisorKernel,
resources.DivisorDataBuffer,
resources.OffsetBuffer,
resources.CountBuffer,
@@ -138,8 +116,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
out coveredRange,
out processedCount);
AcceleratorStreamPool.Return(acceleratorIndex, stream);
ReturnBatchResources(resources);
ReturnBatchResources(acceleratorIndex, resources);
PrimeOrderCalculatorAccelerator.Return(gpu);
// Monitor.Exit(gpuLease.ExecutionLock);
// GpuPrimeWorkLimiter.Release();
@@ -189,8 +166,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
PrimeOrderCalculatorAccelerator gpu,
ulong prime,
ulong allowedMax,
Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> kernel,
AcceleratorStream stream,
Kernel kernel,
MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense> divisorDataBuffer,
MemoryBuffer1D<int, Stride1D.Dense> offsetBuffer,
MemoryBuffer1D<int, Stride1D.Dense> countBuffer,
@@ -213,6 +189,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
bool processedAll;
processedCount = 0UL;
lastProcessed = 0UL;
var acceleratorIndex = gpu.AcceleratorIndex;
// EvenPerfectBitScanner always configures non-empty GPU divisor buffers, so the defensive guard stays
// commented out. Tests and benchmarks that rely on zero-capacity buffers must be updated instead of re-enabling
@@ -352,6 +329,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
continue;
}
var stream = AcceleratorStreamPool.Rent(acceleratorIndex);
divisorDataView.CopyFromCPU(stream, ref divisorDataRef, admissibleCount);
offsetView.CopyFromCPU(stream, ref offsetRef, admissibleCount);
countView.CopyFromCPU(stream, ref countRef, admissibleCount);
@@ -361,11 +339,12 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
int sentinel = int.MaxValue;
hitIndexView.CopyFromCPU(stream, ref sentinel, 1);
kernel(stream, new Index1D(admissibleCount), divisorDataView, offsetView, countView, exponentViewDevice, cycleView, hitsView, hitIndexView);
kernel.Launch(stream, 1, new Index1D(admissibleCount), divisorDataView, offsetView, countView, exponentViewDevice, cycleView, hitsView, hitIndexView);
int firstHit = sentinel;
hitIndexView.CopyToCPU(stream, ref firstHit, 1);
stream.Synchronize();
AcceleratorStreamPool.Return(acceleratorIndex, stream);
int hitIndex = firstHit >= admissibleCount ? -1 : firstHit;
bool hitFound = hitIndex >= 0;
composite = hitFound;
@@ -537,7 +516,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
if (_kernel == null)
{
_kernel = GetKernel(_accelerator);
_kernel = KernelUtil.GetKernel(_accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>(DivisorByDivisorKernels.CheckKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>>();
}
if (_divisorBuffer == null)
@@ -694,192 +673,20 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
private BatchResources RentBatchResources(Accelerator accelerator, int capacity)
private MersenneNumberDivisorByDivisorAccelerator RentBatchResources(int acceleratorIndex, int capacity)
{
if (!_resourcePool.TryTake(out BatchResources? resources))
var queue = _resourcePool[acceleratorIndex];
var accelerator = AcceleratorPool.Shared.Accelerators[acceleratorIndex];
if (!queue.TryTake(out MersenneNumberDivisorByDivisorAccelerator? resources))
{
resources = new BatchResources(Math.Max(1, capacity));
return new MersenneNumberDivisorByDivisorAccelerator(accelerator, capacity);
}
resources.Bind(accelerator, capacity);
resources.EnsureCapacity(accelerator, capacity);
return resources;
}
private void ReturnBatchResources(BatchResources resources) => _resourcePool.Add(resources);
private sealed class BatchResources
{
private Accelerator? _accelerator;
private MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense>? _divisorDataBuffer;
private MemoryBuffer1D<int, Stride1D.Dense>? _offsetBuffer;
private MemoryBuffer1D<int, Stride1D.Dense>? _countBuffer;
private MemoryBuffer1D<ulong, Stride1D.Dense>? _cycleBuffer;
private MemoryBuffer1D<ulong, Stride1D.Dense>? _exponentBuffer;
private MemoryBuffer1D<byte, Stride1D.Dense>? _hitsBuffer;
private MemoryBuffer1D<int, Stride1D.Dense>? _hitIndexBuffer;
private int _deviceCapacity;
private int _hostCapacity;
internal BatchResources(int capacity)
{
int actualCapacity = Math.Max(1, capacity);
RentHostArrays(actualCapacity);
}
internal MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense> DivisorDataBuffer => _divisorDataBuffer ?? throw new InvalidOperationException("Batch resources not bound to an accelerator.");
internal MemoryBuffer1D<int, Stride1D.Dense> OffsetBuffer => _offsetBuffer ?? throw new InvalidOperationException("Batch resources not bound to an accelerator.");
internal MemoryBuffer1D<int, Stride1D.Dense> CountBuffer => _countBuffer ?? throw new InvalidOperationException("Batch resources not bound to an accelerator.");
internal MemoryBuffer1D<ulong, Stride1D.Dense> CycleBuffer => _cycleBuffer ?? throw new InvalidOperationException("Batch resources not bound to an accelerator.");
internal MemoryBuffer1D<ulong, Stride1D.Dense> ExponentBuffer => _exponentBuffer ?? throw new InvalidOperationException("Batch resources not bound to an accelerator.");
internal MemoryBuffer1D<byte, Stride1D.Dense> HitsBuffer => _hitsBuffer ?? throw new InvalidOperationException("Batch resources not bound to an accelerator.");
internal MemoryBuffer1D<int, Stride1D.Dense> HitIndexBuffer => _hitIndexBuffer ?? throw new InvalidOperationException("Batch resources not bound to an accelerator.");
internal ulong[] Divisors { get; private set; } = null!;
internal ulong[] Exponents { get; private set; } = null!;
internal GpuDivisorPartialData[] DivisorData { get; private set; } = null!;
internal int[] Offsets { get; private set; } = null!;
internal int[] Counts { get; private set; } = null!;
internal ulong[] Cycles { get; private set; } = null!;
internal int Capacity => _hostCapacity;
internal void Bind(Accelerator accelerator, int requiredCapacity)
{
EnsureHostCapacity(requiredCapacity);
if (!ReferenceEquals(_accelerator, accelerator))
{
ReleaseDeviceBuffers();
_accelerator = accelerator;
}
EnsureDeviceCapacity(accelerator, requiredCapacity);
}
private void EnsureHostCapacity(int requiredCapacity)
{
int desiredCapacity = Math.Max(1, requiredCapacity);
if (_hostCapacity >= desiredCapacity)
{
return;
}
ReturnHostArrays();
RentHostArrays(desiredCapacity);
}
private void EnsureDeviceCapacity(Accelerator accelerator, int requiredCapacity)
{
int desiredCapacity = Math.Max(1, requiredCapacity);
if (_divisorDataBuffer is null)
{
AllocateDeviceBuffers(accelerator, desiredCapacity);
return;
}
if (_deviceCapacity >= desiredCapacity)
{
return;
}
ReleaseDeviceBuffers();
AllocateDeviceBuffers(accelerator, desiredCapacity);
}
private void AllocateDeviceBuffers(Accelerator accelerator, int capacity)
{
lock (accelerator)
{
_divisorDataBuffer = accelerator.Allocate1D<GpuDivisorPartialData>(capacity);
_offsetBuffer = accelerator.Allocate1D<int>(capacity);
_countBuffer = accelerator.Allocate1D<int>(capacity);
_cycleBuffer = accelerator.Allocate1D<ulong>(capacity);
_exponentBuffer = accelerator.Allocate1D<ulong>(capacity);
_hitsBuffer = accelerator.Allocate1D<byte>(capacity);
_hitIndexBuffer = accelerator.Allocate1D<int>(1);
}
_deviceCapacity = capacity;
}
private void RentHostArrays(int capacity)
{
ArrayPool<ulong> ulongPool = ThreadStaticPools.UlongPool;
ArrayPool<int> intPool = ThreadStaticPools.IntPool;
ArrayPool<GpuDivisorPartialData> partialPool = ThreadStaticPools.GpuDivisorPartialDataPool;
Divisors = ulongPool.Rent(capacity);
Exponents = ulongPool.Rent(capacity);
DivisorData = partialPool.Rent(capacity);
Offsets = intPool.Rent(capacity);
Counts = intPool.Rent(capacity);
Cycles = ulongPool.Rent(capacity);
_hostCapacity = capacity;
}
private void ReleaseDeviceBuffers()
{
_divisorDataBuffer?.Dispose();
_offsetBuffer?.Dispose();
_countBuffer?.Dispose();
_cycleBuffer?.Dispose();
_exponentBuffer?.Dispose();
_hitsBuffer?.Dispose();
_hitIndexBuffer?.Dispose();
_divisorDataBuffer = null;
_offsetBuffer = null;
_countBuffer = null;
_cycleBuffer = null;
_exponentBuffer = null;
_hitsBuffer = null;
_hitIndexBuffer = null;
_deviceCapacity = 0;
}
private void ReturnHostArrays()
{
if (_hostCapacity == 0)
{
return;
}
ArrayPool<ulong> ulongPool = ThreadStaticPools.UlongPool;
ArrayPool<int> intPool = ThreadStaticPools.IntPool;
ArrayPool<GpuDivisorPartialData> partialPool = ThreadStaticPools.GpuDivisorPartialDataPool;
ulongPool.Return(Divisors, clearArray: false);
ulongPool.Return(Exponents, clearArray: false);
partialPool.Return(DivisorData, clearArray: false);
intPool.Return(Offsets, clearArray: false);
intPool.Return(Counts, clearArray: false);
ulongPool.Return(Cycles, clearArray: false);
Divisors = Array.Empty<ulong>();
Exponents = Array.Empty<ulong>();
DivisorData = Array.Empty<GpuDivisorPartialData>();
Offsets = Array.Empty<int>();
Counts = Array.Empty<int>();
Cycles = Array.Empty<ulong>();
_hostCapacity = 0;
}
public void Dispose()
{
ReleaseDeviceBuffers();
_accelerator = null;
ReturnHostArrays();
}
}
private void ReturnBatchResources(int acceleratorIndex, MersenneNumberDivisorByDivisorAccelerator resources) => _resourcePool[acceleratorIndex].Add(resources);
private sealed class AcceleratorReferenceComparer : IEqualityComparer<Accelerator>
{
+4 -4
View File
@@ -412,8 +412,8 @@ public class MersenneDivisorCycles
// Console.WriteLine($"MersenneDivisorCycles.TryFactorIntoCountsInternal hits {Volatile.Read(ref _tryFactorIntoCountsInternalHits)}");
// HeuristicPrimeTester primeTester = _primeTester ??= new();
if (HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, remaining))
// if (PrimeTester.IsPrimeCpu(remaining, CancellationToken.None))
// if (HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, remaining))
if (PrimeTester.IsPrime(remaining))
{
AddFactor(counts, remaining);
return true;
@@ -958,8 +958,8 @@ public class MersenneDivisorCycles
// HeuristicPrimeTester primeTester = _primeTester ??= new();
// if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.Exclusive.IsPrimeCpu(divisor, CancellationToken.None))
// if (!skipPrimeOrderHeuristic && primeTester.IsPrimeGpu(divisor))
if (!skipPrimeOrderHeuristic && HeuristicCombinedPrimeTester.IsPrimeGpu(divisor))
if (!skipPrimeOrderHeuristic && PrimeTester.IsPrime(divisor))
// if (!skipPrimeOrderHeuristic && HeuristicCombinedPrimeTester.IsPrimeGpu(divisor))
{
ulong computedOrder = PrimeOrderCalculator.Calculate(
divisor,
@@ -8,12 +8,12 @@ public static class PerfectNumberConstants
public const int DefaultSmallPrimeFactorSlotCount = 64;
public const int DefaultSpecialMaxFactorCapacity = 1024;
public const ulong ExtraDivisorCycleSearchLimit = 64UL;
public const int GpuFrequency = 768 - 1;
public const int GpuFrequency = 16 - 1;
public const int MaxQForDivisorCycles = 4_000_000;
public static readonly uint PrimesLimit = 1_000_000; //(ulong)Array.MaxLength;// 1_000_000;
public const int MaxOddPowersCount = 128;
public const int PooledArrayThreshold = 64;
public static readonly int RollingAccelerators = 298; //SharedGpuContext.Device.MaxNumThreadsPerGroup;
public static readonly int RollingAccelerators = 298; //SharedGpuContext.Device.MaxNumThreadsPerGroup;
public const int ThreadsByAccelerator = 1;
// TODO: Load these limits from the benchmark-driven configuration so CPU and GPU scans stay aligned with the optimal
// divisor-cycle datasets we generate offline.
+46 -11
View File
@@ -66,9 +66,11 @@ internal static partial class PrimeOrderCalculator
ulong result;
if (phiFactors.Factors is null)
{
result = /*prime <= PerfectNumberConstants.MaxQForDivisorCycles
? CalculateByFactorizationGpu(gpu, prime, divisorData)
: */CalculateByFactorizationCpu(gpu, prime, divisorData);
// result = prime <= PerfectNumberConstants.MaxQForDivisorCycles
// ? CalculateByFactorizationGpu(gpu, prime, divisorData)
// : CalculateByFactorizationCpu(gpu, prime, divisorData);
result = CalculateByFactorizationGpu(gpu, prime, divisorData);
// result = CalculateByFactorizationCpu(gpu, prime, divisorData);
phiFactors.Dispose();
PrimeOrderCalculatorAccelerator.Return(gpu);
@@ -109,8 +111,8 @@ internal static partial class PrimeOrderCalculator
{
orderFactors?.Dispose();
// GpuPrimeWorkLimiter.Release();
return CalculateByFactorizationCpu(gpu, prime, divisorData);
// return CalculateByFactorizationGpu(gpu, prime, divisorData);
// return CalculateByFactorizationCpu(gpu, prime, divisorData);
return CalculateByFactorizationGpu(gpu, prime, divisorData);
}
if (TryHeuristicFinishCpu(gpu, prime, candidateOrder, previousOrder, divisorData, config, phiFactors, orderFactors, out ulong order))
@@ -138,7 +140,7 @@ internal static partial class PrimeOrderCalculator
Span<ulong> stackBuffer = stackalloc ulong[length];
if (length <= 9)
if (length <= 8)
{
return EvaluateSpecialMaxCandidates(stackBuffer, factorSpan, phi, prime, divisorData);
}
@@ -1174,9 +1176,10 @@ internal static partial class PrimeOrderCalculator
// bool isPrime = composite > PerfectNumberConstants.MaxQForDivisorCycles
// ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite)
// : PrimeTester.IsPrime(composite);
// bool isPrime = PrimeTester.IsPrime(composite);
// bool isPrime = HeuristicCombinedPrimeTester.IsPrimeCpu(composite);
// bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite);
bool runOnGpu = RunOnGpu();
bool isPrime = !runOnGpu ? PrimeTester.IsPrime(composite) : HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite);
// bool isPrime = PrimeTester.IsPrime(composite);
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
@@ -1243,13 +1246,18 @@ internal static partial class PrimeOrderCalculator
if (!entry.HasKnownPrimality)
{
// bool isPrime = primeTester.HeuristicIsPrimeGpu(composite);
// bool isPrime = HeuristicCombinedPrimeTester.IsPrimeCpu(composite);
// bool isPrime = PrimeTester.IsPrime(composite);
// bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(composite);
// bool isPrime = PrimeTester.IsPrimeInternal(composite, CancellationToken.None);
// Atomic.Add(ref _partialFactorPendingHits, 1UL);
// Console.WriteLine($"Partial factor pending hits {Volatile.Read(ref _partialFactorPendingHits)}");
bool isPrime = PrimeTester.IsPrime(composite);
bool isPrime = RunOnGpu()
? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite)
: PrimeTester.IsPrime(composite);
// bool isPrime = PrimeTester.IsPrime(composite);
// bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite);
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
@@ -1281,7 +1289,9 @@ internal static partial class PrimeOrderCalculator
}
else
{
// cofactorIsPrime = primeTester.HeuristicIsPrimeGpu(cofactor);
// cofactorIsPrime = PrimeTester.IsPrime(cofactor);
// cofactorIsPrime = HeuristicCombinedPrimeTester.IsPrimeCpu(cofactor);
// cofactorIsPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(cofactor);
// cofactorIsPrime = PrimeTester.IsPrimeInternal(cofactor, CancellationToken.None);
// Atomic.Add(ref _partialFactorCofactorHits, 1UL);
@@ -1289,7 +1299,10 @@ internal static partial class PrimeOrderCalculator
// cofactorIsPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, cofactor);
// cofactorIsPrime = PrimeTester.IsPrimeGpu(gpu, cofactor);
cofactorIsPrime = PrimeTester.IsPrime(cofactor);
cofactorIsPrime = RunOnGpu()
? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, cofactor)
: PrimeTester.IsPrime(cofactor);
// cofactorIsPrime = PrimeTester.IsPrime(cofactor);
// cofactorIsPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(cofactor);
}
@@ -1375,7 +1388,23 @@ internal static partial class PrimeOrderCalculator
private static bool RunOnGpu()
{
// return false;
// return true;
// int cpuCount;
// lock(_sync)
// {
// cpuCount = _cpuCount;
// cpuCount++;
// if (cpuCount == PerfectNumberConstants.GpuFrequency)
// {
// cpuCount = 0;
// }
// _cpuCount = cpuCount;
// }
// return cpuCount == 0;
int cpuCount = Atomic.Add(ref _cpuCount, 1);
// return (cpuCount % GpuFrequency) == 0;
if (cpuCount == PerfectNumberConstants.GpuFrequency)
@@ -1800,6 +1829,9 @@ internal static partial class PrimeOrderCalculator
// bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, value);
// bool isPrime = PrimeTester.IsPrimeGpu(value);
bool isPrime = PrimeTester.IsPrime(value);
// bool isPrime = RunOnGpu()
// ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, value)
// : PrimeTester.IsPrime(value);
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(value);
if (!knownComposite && isPrime)
{
@@ -1816,6 +1848,9 @@ internal static partial class PrimeOrderCalculator
// isPrime = primeTester.HeuristicIsPrimeGpu(factor);
// isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, factor);
// isPrime = PrimeTester.IsPrimeGpu(factor);
// isPrime = RunOnGpu()
// ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, factor)
// : PrimeTester.IsPrime(factor);
isPrime = PrimeTester.IsPrime(factor);
// isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(factor);
if (isPrime)
@@ -42,8 +42,9 @@ internal static partial class PrimeOrderCalculator
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
gpu.Pow2ModEntriesToTestOnDevice.View.CopyFromCPU(stream, entries);
var kernelLauncher = gpu.CheckFactorsKernelLauncher;
kernelLauncher(stream, entryCount, phi, gpu.Pow2ModEntriesToTestOnDevice.View, divisorData, gpu.OutputUlong.View);
// var kernelLauncher = gpu.CheckFactorsKernelLauncher;
var kernel = gpu.CheckFactorsKernel;
kernel.Launch(stream, 1, entryCount, phi, gpu.Pow2ModEntriesToTestOnDevice.View, divisorData, gpu.OutputUlong.View);
gpu.OutputUlong.View.CopyToCPU(stream, ref order, 1);
stream.Synchronize();
@@ -170,9 +171,9 @@ internal static partial class PrimeOrderCalculator
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
gpu.Input.View.CopyFromCPU(stream, randomStateSpan);
var kernelLauncher = gpu.PollardRhoKernelLauncher;
var kernel = gpu.PollardRhoKernel;
kernelLauncher(stream, n, 1, gpu.Input.View, gpu.OutputByte.View, gpu.OutputUlong.View);
kernel.Launch(stream, 1, n, 1, gpu.Input.View, gpu.OutputByte.View, gpu.OutputUlong.View);
gpu.OutputByte.View.CopyToCPU(stream, factoredSpan);
gpu.OutputUlong.View.CopyToCPU(stream, factorSpan);