Heuristic tables split into 2 groups

This commit is contained in:
mlemanczyk committed 2025-11-17 16:04:27 +01:00
1 parent c413ee1162
commit 183b147135
26 files changed
+697 -465

No files matched your search

@@ -2,6 +2,7 @@ using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using Open.Numeric.Primes;
using PerfectNumbers.Core;
using PerfectNumbers.Core.Gpu.Accelerators;
namespace EvenPerfectBitScanner.Benchmarks;
@@ -25,6 +26,8 @@ public class PrimeTesterBenchmarks
return;
}
PrimeOrderCalculatorAccelerator.WarmUp();
// HeuristicGroupABPrimeTesterAccelerator.WarmUp();
_ = _heuristicTester.IsPrimeCpu(_candidates[0]);
_ = _heuristicTester.IsPrimeGpu(_candidates[0]);
_ = Prime.Numbers.IsPrime(_candidates[0]);
@@ -48,7 +51,7 @@ public class PrimeTesterBenchmarks
return primeCount;
}
// [Benchmark]
[Benchmark]
public int HeuristicGpu()
{
HeuristicPrimeTester tester = _heuristicTester;
@@ -66,7 +69,7 @@ public class PrimeTesterBenchmarks
return primeCount;
}
// [Benchmark]
[Benchmark]
public int HeuristicCombinedGpu()
{
ulong[] values = _candidates;
@@ -83,7 +86,7 @@ public class PrimeTesterBenchmarks
return primeCount;
}
// [Benchmark]
[Benchmark]
public int HeuristicCombinedCpu()
{
ulong[] values = _candidates;
+1 -1
View File
@@ -628,7 +628,7 @@ internal static class Program
{
if (!(_runPrimesOnCpu
? PrimeTester.IsPrime(p)
: PrimeTester.IsPrimeGpu(p)))
: HeuristicCombinedPrimeTester.IsPrimeGpu(p)))
{
_lastCompositeP = true;
return false;
@@ -83,7 +83,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
throw new NotSupportedException();
}
public void Dispose()
public void Return()
{
}
}
@@ -47,7 +47,7 @@ public class MersenneNumberDivisorCpuTesterTests
}
finally
{
session.Dispose();
session.Return();
}
}
@@ -72,7 +72,7 @@ public class MersenneNumberDivisorCpuTesterTests
}
finally
{
session.Dispose();
session.Return();
}
}
@@ -97,7 +97,7 @@ public class MersenneNumberDivisorCpuTesterTests
}
finally
{
session.Dispose();
session.Return();
}
}
}
@@ -161,7 +161,7 @@ public class MersenneNumberDivisorGpuTesterTests
}
finally
{
session.Dispose();
session.Return();
}
}
@@ -189,7 +189,7 @@ public class MersenneNumberDivisorGpuTesterTests
}
finally
{
session.Dispose();
session.Return();
}
}
@@ -217,7 +217,7 @@ public class MersenneNumberDivisorGpuTesterTests
}
finally
{
session.Dispose();
session.Return();
}
}
@@ -246,7 +246,7 @@ public class MersenneNumberDivisorGpuTesterTests
}
finally
{
session.Dispose();
session.Return();
}
}
@@ -275,7 +275,7 @@ public class MersenneNumberDivisorGpuTesterTests
}
finally
{
session.Dispose();
session.Return();
}
}
@@ -344,7 +344,7 @@ public class MersenneNumberDivisorGpuTesterTests
}
finally
{
session.Dispose();
session.Return();
}
}
}
@@ -62,7 +62,7 @@ internal sealed class MersenneCpuDivisorScanSession : IMersenneNumberDivisorByDi
}
}
public void Dispose()
public void Return()
{
ThreadStaticPools.ReturnMersenneCpuDivisorSession(this);
}
@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices;
using PerfectNumbers.Core;
using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core.Cpu;
@@ -131,7 +132,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
// Intentionally recomputes factorizations without a per-thread cache.
// The previous factor cache recorded virtually no hits and only slowed down the scan.
DivisorCycleCache cycleCache = DivisorCycleCache.Shared;
_ = DivisorCycleCache.Shared;
ulong stepHigh = step.High;
ulong stepLow = step.Low;
@@ -201,6 +202,8 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
remainder7 = (byte)((((divisorHigh % 7UL) * 2UL) + (divisorLow % 7UL)) % 7UL);
remainder11 = (byte)((((divisorHigh % 11UL) * 5UL) + (divisorLow % 11UL)) % 11UL);
var gpu = HeuristicCombinedPrimeTesterAccelerator.Rent(1);
while (divisor.CompareTo(limit) <= 0)
{
bool passesSmallModuli = remainder3 != 0 && remainder7 != 0 && remainder11 != 0;
@@ -217,6 +220,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
// else
{
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
gpu,
candidate,
prime,
divisorData,
@@ -241,6 +245,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
// A cycle equal to the tested exponent (which is prime in this path) guarantees that the candidate divides
// the corresponding Mersenne number because the order of 2 modulo the divisor is exactly p.
processedAll = true;
HeuristicCombinedPrimeTesterAccelerator.Return(gpu);
return true;
}
@@ -257,6 +262,8 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
remainder7 = AddMod7(remainder7, step7);
remainder11 = AddMod11(remainder11, step11);
}
HeuristicCombinedPrimeTesterAccelerator.Return(gpu);
processedAll = true;
return false;
}
@@ -282,6 +289,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
processedAll = false;
bool canAdvance = step <= limit;
var gpu = HeuristicCombinedPrimeTesterAccelerator.Rent(1);
if (!canAdvance)
{
if (divisor <= limit)
@@ -291,6 +299,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
ulong divisorCycle;
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
gpu,
divisor,
prime,
divisorData,
@@ -310,6 +319,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
if (divisorCycle == prime)
{
processedAll = true;
HeuristicCombinedPrimeTesterAccelerator.Return(gpu);
return true;
}
@@ -340,6 +350,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
ulong divisorCycle;
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
gpu,
divisor,
prime,
divisorData,
@@ -359,6 +370,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
if (divisorCycle == prime)
{
processedAll = true;
HeuristicCombinedPrimeTesterAccelerator.Return(gpu);
return true;
}
@@ -0,0 +1,34 @@
using ILGPU;
using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu.Accelerators;
public static class AcceleratorExtensions
{
public static MemoryBuffer1D<ulong, Stride1D.Dense> CopySpanToDevice(this Accelerator accelerator, AcceleratorStream stream, in ulong[] span)
{
MemoryBuffer1D<ulong, Stride1D.Dense>? buffer;
buffer = accelerator.Allocate1D(stream, span);
return buffer;
}
public static MemoryBuffer1D<ulong, Stride1D.Dense> CopyUintSpanToDevice(this Accelerator accelerator, AcceleratorStream stream, ReadOnlySpan<uint> span)
{
MemoryBuffer1D<ulong, Stride1D.Dense>? buffer;
buffer = accelerator.Allocate1D<ulong>(span.Length);
if (!span.IsEmpty)
{
var converted = new ulong[span.Length];
for (int i = 0; i < span.Length; i++)
{
converted[i] = span[i];
}
buffer.View.CopyFromCPU(stream, converted);
}
return buffer;
}
}
@@ -0,0 +1,147 @@
using System.Runtime.CompilerServices;
using ILGPU;
using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu.Accelerators;
public sealed class HeuristicCombinedPrimeTesterAccelerator
{
#region Pool
[ThreadStatic]
private static Queue<HeuristicCombinedPrimeTesterAccelerator>? Pool;
public static void WarmUp(int acceleratorIndex, AcceleratorStream stream)
{
Console.WriteLine($"Preparing heuristic accelerator {acceleratorIndex}...");
HeuristicGpuCombinedTables.WarmUp(acceleratorIndex, stream);
// _ = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.None);
// KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream);
// GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream);
Console.WriteLine($"Heuristic accelerator {acceleratorIndex} is ready");
}
public static HeuristicCombinedPrimeTesterAccelerator Rent(int minBufferCapacity = 1)
{
var pool = Pool ??= new();
if (!pool.TryDequeue(out var lease))
{
lease = new HeuristicCombinedPrimeTesterAccelerator(minBufferCapacity);
}
else
{
lease.EnsureCapacity(minBufferCapacity);
}
return lease;
}
public static void Return(HeuristicCombinedPrimeTesterAccelerator lease)
{
var pool = Pool ??= new();
pool.Enqueue(lease);
}
public static void DisposeAll()
{
if (Pool != null)
{
while (Pool.TryDequeue(out var lease))
{
lease.DisposeResources();
}
}
}
public static void Clear()
{
if (Pool != null)
{
var retained = new List<HeuristicCombinedPrimeTesterAccelerator>();
while (Pool.TryDequeue(out var lease))
{
lease.DisposeResources();
}
}
}
#endregion
public readonly HeuristicGpuCombinedDivisorTables DivisorTables;
internal readonly Context Context;
public readonly Accelerator Accelerator;
public readonly Kernel HeuristicCombinedTrialDivisionKernel;
public MemoryBuffer1D<ulong, Stride1D.Dense>? Input = null;
public MemoryBuffer1D<byte, Stride1D.Dense>? Output = null;
public MemoryBuffer1D<int, Stride1D.Dense>? HeuristicFlag = null!;
public int BufferCapacity;
private HeuristicGpuCombinedTables _sharedTables;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding1 => _sharedTables.HeuristicCombinedDivisorsEnding1;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding1 => _sharedTables.HeuristicCombinedDivisorSquaresEnding1;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding3 => _sharedTables.HeuristicCombinedDivisorsEnding3;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding3 => _sharedTables.HeuristicCombinedDivisorSquaresEnding3;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding7 => _sharedTables.HeuristicCombinedDivisorsEnding7;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding7 => _sharedTables.HeuristicCombinedDivisorSquaresEnding7;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding9 => _sharedTables.HeuristicCombinedDivisorsEnding9;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding9 => _sharedTables.HeuristicCombinedDivisorSquaresEnding9;
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
internal readonly int AcceleratorIndex;
public HeuristicCombinedPrimeTesterAccelerator(int minBufferCapacity)
{
var acceleratorIndex = AcceleratorPool.Shared.Rent();
AcceleratorIndex = acceleratorIndex;
Accelerator accelerator = _accelerators[acceleratorIndex];
Context = accelerator.Context;
Accelerator = accelerator;
EnsureCapacity(minBufferCapacity);
HeuristicCombinedTrialDivisionKernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicGpuCombinedDivisorTables>(PrimeTesterKernels.HeuristicTrialCombinedDivisionKernel));
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
// GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream);
_sharedTables = HeuristicGpuCombinedTables.GetStaticTables(acceleratorIndex);
DivisorTables = _sharedTables.CreateHeuristicDivisorTables();
stream.Synchronize();
AcceleratorStreamPool.Return(acceleratorIndex, stream);
}
public void EnsureCapacity(int minCapacity)
{
if (minCapacity <= BufferCapacity)
{
return;
}
Input?.Dispose();
Output?.Dispose();
HeuristicFlag?.Dispose();
Input = Accelerator.Allocate1D<ulong>(minCapacity);
Output = Accelerator.Allocate1D<byte>(minCapacity);
HeuristicFlag = Accelerator.Allocate1D<int>(minCapacity);
BufferCapacity = minCapacity;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Return() => Return(this);
public void DisposeResources()
{
Input?.Dispose();
Output?.Dispose();
HeuristicFlag?.Dispose();
// ReleaseSharedTables(_sharedTables);
// These resources are shared between GPU leases
// Accelerator.Dispose();
// AcceleratorContext.Dispose();
}
}
@@ -0,0 +1,69 @@
using ILGPU;
using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu.Accelerators;
internal sealed class HeuristicGpuCombinedTables
{
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
private static readonly HeuristicGpuCombinedTables[] _sharedTables = new HeuristicGpuCombinedTables[PerfectNumberConstants.RollingAccelerators];
internal static void WarmUp(int acceleratorIndex, AcceleratorStream stream) => _sharedTables[acceleratorIndex] = new(_accelerators[acceleratorIndex], stream);
internal static HeuristicGpuCombinedTables GetStaticTables(int acceleratorIndex) => _sharedTables[acceleratorIndex];
internal HeuristicGpuCombinedTables(Accelerator accelerator, AcceleratorStream stream)
{
var combinedEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1;
HeuristicCombinedDivisorsEnding1 = accelerator.CopySpanToDevice(stream, combinedEnding1);
var combinedSquaresEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1Squares;
HeuristicCombinedDivisorSquaresEnding1 = accelerator.CopySpanToDevice(stream, combinedSquaresEnding1);
var combinedEnding3 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding3;
HeuristicCombinedDivisorsEnding3 = accelerator.CopySpanToDevice(stream, combinedEnding3);
var combinedSquaresEnding3 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding3Squares;
HeuristicCombinedDivisorSquaresEnding3 = accelerator.CopySpanToDevice(stream, combinedSquaresEnding3);
var combinedEnding7 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding7;
HeuristicCombinedDivisorsEnding7 = accelerator.CopySpanToDevice(stream, combinedEnding7);
var combinedSquaresEnding7 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding7Squares;
HeuristicCombinedDivisorSquaresEnding7 = accelerator.CopySpanToDevice(stream, combinedSquaresEnding7);
var combinedEnding9 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding9;
HeuristicCombinedDivisorsEnding9 = accelerator.CopySpanToDevice(stream, combinedEnding9);
var combinedSquaresEnding9 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding9Squares;
HeuristicCombinedDivisorSquaresEnding9 = accelerator.CopySpanToDevice(stream, combinedSquaresEnding9);
}
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding1;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding1;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding3;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding3;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding7;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding7;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding9;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding9;
internal HeuristicGpuCombinedDivisorTables CreateHeuristicDivisorTables() => new(
HeuristicCombinedDivisorsEnding1.View,
HeuristicCombinedDivisorsEnding3.View,
HeuristicCombinedDivisorsEnding7.View,
HeuristicCombinedDivisorsEnding9.View,
HeuristicCombinedDivisorSquaresEnding1.View,
HeuristicCombinedDivisorSquaresEnding3.View,
HeuristicCombinedDivisorSquaresEnding7.View,
HeuristicCombinedDivisorSquaresEnding9.View);
internal void Dispose()
{
HeuristicCombinedDivisorsEnding1.Dispose();
HeuristicCombinedDivisorSquaresEnding1.Dispose();
HeuristicCombinedDivisorsEnding3.Dispose();
HeuristicCombinedDivisorSquaresEnding3.Dispose();
HeuristicCombinedDivisorsEnding7.Dispose();
HeuristicCombinedDivisorSquaresEnding7.Dispose();
HeuristicCombinedDivisorsEnding9.Dispose();
HeuristicCombinedDivisorSquaresEnding9.Dispose();
}
}
@@ -0,0 +1,79 @@
using ILGPU;
using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu.Accelerators;
internal sealed class HeuristicGpuGroupABTables
{
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
private static readonly HeuristicGpuGroupABTables[] _sharedTables = new HeuristicGpuGroupABTables[PerfectNumberConstants.RollingAccelerators];
private static readonly SemaphoreSlim[] _locks = [..Enumerable.Range(1, PerfectNumberConstants.RollingAccelerators).Select(_ => new SemaphoreSlim(1))];
internal static HeuristicGpuGroupABTables EnsureStaticTables(int acceleratorIndex, AcceleratorStream stream)
{
var @lock = _locks[acceleratorIndex];
@lock.Wait();
if (_sharedTables[acceleratorIndex] is {} existing)
{
@lock.Release();
return existing;
}
var accelerator = _accelerators[acceleratorIndex];
existing = new(accelerator, stream);
_sharedTables[acceleratorIndex] = existing;
@lock.Release();
return existing;
}
internal HeuristicGpuGroupABTables(Accelerator accelerator, AcceleratorStream stream)
{
var heuristicGroupA = HeuristicPrimeSieves.GroupADivisorsStorage;
HeuristicGroupADivisors = accelerator.CopySpanToDevice(stream, heuristicGroupA);
var heuristicGroupASquares = HeuristicPrimeSieves.GroupADivisorSquaresStorage;
HeuristicGroupADivisorSquares = accelerator.CopySpanToDevice(stream, heuristicGroupASquares);
HeuristicGroupBDivisorsEnding1 = accelerator.CopyUintSpanToDevice(stream, DivisorGenerator.SmallPrimesLastOneWithoutLastThree);
HeuristicGroupBDivisorSquaresEnding1 = accelerator.CopySpanToDevice(stream, DivisorGenerator.SmallPrimesPow2LastOneWithoutLastThree);
HeuristicGroupBDivisorsEnding7 = accelerator.CopyUintSpanToDevice(stream, DivisorGenerator.SmallPrimesLastSevenWithoutLastThree);
HeuristicGroupBDivisorSquaresEnding7 = accelerator.CopySpanToDevice(stream, DivisorGenerator.SmallPrimesPow2LastSevenWithoutLastThree);
HeuristicGroupBDivisorsEnding9 = accelerator.CopyUintSpanToDevice(stream, DivisorGenerator.SmallPrimesLastNineWithoutLastThree);
HeuristicGroupBDivisorSquaresEnding9 = accelerator.CopySpanToDevice(stream, DivisorGenerator.SmallPrimesPow2LastNineWithoutLastThree);
}
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupADivisors;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupADivisorSquares;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding1;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding1;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding7;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding7;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding9;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding9;
internal HeuristicGpuGroupABDivisorTables CreateHeuristicDivisorTables() => new(
HeuristicGroupADivisors.View,
HeuristicGroupADivisorSquares.View,
HeuristicGroupBDivisorsEnding1.View,
HeuristicGroupBDivisorSquaresEnding1.View,
HeuristicGroupBDivisorsEnding7.View,
HeuristicGroupBDivisorSquaresEnding7.View,
HeuristicGroupBDivisorsEnding9.View,
HeuristicGroupBDivisorSquaresEnding9.View);
internal void Dispose()
{
HeuristicGroupADivisors.Dispose();
HeuristicGroupADivisorSquares.Dispose();
HeuristicGroupBDivisorsEnding1.Dispose();
HeuristicGroupBDivisorSquaresEnding1.Dispose();
HeuristicGroupBDivisorsEnding7.Dispose();
HeuristicGroupBDivisorSquaresEnding7.Dispose();
HeuristicGroupBDivisorsEnding9.Dispose();
HeuristicGroupBDivisorSquaresEnding9.Dispose();
}
}
@@ -1,150 +0,0 @@
using ILGPU;
using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu.Accelerators;
internal sealed class HeuristicGpuTables
{
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
private static readonly HeuristicGpuTables[] _sharedTables = new HeuristicGpuTables[PerfectNumberConstants.RollingAccelerators];
private static readonly SemaphoreSlim[] _locks = [..Enumerable.Range(1, PerfectNumberConstants.RollingAccelerators).Select(_ => new SemaphoreSlim(1))];
internal static HeuristicGpuTables EnsureStaticTables(int acceleratorIndex, AcceleratorStream stream)
{
var @lock = _locks[acceleratorIndex];
@lock.Wait();
if (_sharedTables[acceleratorIndex] is {} existing)
{
@lock.Release();
return existing;
}
var accelerator = _accelerators[acceleratorIndex];
existing = new(accelerator, stream);
_sharedTables[acceleratorIndex] = existing;
@lock.Release();
return existing;
}
internal HeuristicGpuTables(Accelerator accelerator, AcceleratorStream stream)
{
var heuristicGroupA = HeuristicPrimeSieves.GroupADivisorsStorage;
HeuristicGroupADivisors = CopySpanToDevice(accelerator, stream, heuristicGroupA);
var heuristicGroupASquares = HeuristicPrimeSieves.GroupADivisorSquaresStorage;
HeuristicGroupADivisorSquares = CopySpanToDevice(accelerator, stream, heuristicGroupASquares);
HeuristicGroupBDivisorsEnding1 = CopyUintSpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesLastOneWithoutLastThree);
HeuristicGroupBDivisorSquaresEnding1 = CopySpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesPow2LastOneWithoutLastThree);
HeuristicGroupBDivisorsEnding7 = CopyUintSpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesLastSevenWithoutLastThree);
HeuristicGroupBDivisorSquaresEnding7 = CopySpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesPow2LastSevenWithoutLastThree);
HeuristicGroupBDivisorsEnding9 = CopyUintSpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesLastNineWithoutLastThree);
HeuristicGroupBDivisorSquaresEnding9 = CopySpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesPow2LastNineWithoutLastThree);
var combinedEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1;
HeuristicCombinedDivisorsEnding1 = CopySpanToDevice(accelerator, stream, combinedEnding1);
var combinedSquaresEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1Squares;
HeuristicCombinedDivisorSquaresEnding1 = CopySpanToDevice(accelerator, stream, combinedSquaresEnding1);
var combinedEnding3 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding3;
HeuristicCombinedDivisorsEnding3 = CopySpanToDevice(accelerator, stream, combinedEnding3);
var combinedSquaresEnding3 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding3Squares;
HeuristicCombinedDivisorSquaresEnding3 = CopySpanToDevice(accelerator, stream, combinedSquaresEnding3);
var combinedEnding7 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding7;
HeuristicCombinedDivisorsEnding7 = CopySpanToDevice(accelerator, stream, combinedEnding7);
var combinedSquaresEnding7 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding7Squares;
HeuristicCombinedDivisorSquaresEnding7 = CopySpanToDevice(accelerator, stream, combinedSquaresEnding7);
var combinedEnding9 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding9;
HeuristicCombinedDivisorsEnding9 = CopySpanToDevice(accelerator, stream, combinedEnding9);
var combinedSquaresEnding9 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding9Squares;
HeuristicCombinedDivisorSquaresEnding9 = CopySpanToDevice(accelerator, stream, combinedSquaresEnding9);
}
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupADivisors;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupADivisorSquares;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding1;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding1;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding7;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding7;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding9;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding9;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding1;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding1;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding3;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding3;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding7;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding7;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding9;
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding9;
internal HeuristicGpuDivisorTables CreateHeuristicDivisorTables() => new(
HeuristicCombinedDivisorsEnding1.View,
HeuristicCombinedDivisorsEnding3.View,
HeuristicCombinedDivisorsEnding7.View,
HeuristicCombinedDivisorsEnding9.View,
HeuristicCombinedDivisorSquaresEnding1.View,
HeuristicCombinedDivisorSquaresEnding3.View,
HeuristicCombinedDivisorSquaresEnding7.View,
HeuristicCombinedDivisorSquaresEnding9.View,
HeuristicGroupADivisors.View,
HeuristicGroupADivisorSquares.View,
HeuristicGroupBDivisorsEnding1.View,
HeuristicGroupBDivisorSquaresEnding1.View,
HeuristicGroupBDivisorsEnding7.View,
HeuristicGroupBDivisorSquaresEnding7.View,
HeuristicGroupBDivisorsEnding9.View,
HeuristicGroupBDivisorSquaresEnding9.View);
private static MemoryBuffer1D<ulong, Stride1D.Dense> CopySpanToDevice(Accelerator accelerator, AcceleratorStream stream, in ulong[] span)
{
MemoryBuffer1D<ulong, Stride1D.Dense>? buffer;
buffer = accelerator.Allocate1D(stream, span);
return buffer;
}
private static MemoryBuffer1D<ulong, Stride1D.Dense> CopyUintSpanToDevice(Accelerator accelerator, AcceleratorStream stream, ReadOnlySpan<uint> span)
{
MemoryBuffer1D<ulong, Stride1D.Dense>? buffer;
buffer = accelerator.Allocate1D<ulong>(span.Length);
if (!span.IsEmpty)
{
var converted = new ulong[span.Length];
for (int i = 0; i < span.Length; i++)
{
converted[i] = span[i];
}
buffer.View.CopyFromCPU(stream, converted);
}
return buffer;
}
internal void Dispose()
{
HeuristicGroupADivisors.Dispose();
HeuristicGroupADivisorSquares.Dispose();
HeuristicGroupBDivisorsEnding1.Dispose();
HeuristicGroupBDivisorSquaresEnding1.Dispose();
HeuristicGroupBDivisorsEnding7.Dispose();
HeuristicGroupBDivisorSquaresEnding7.Dispose();
HeuristicGroupBDivisorsEnding9.Dispose();
HeuristicGroupBDivisorSquaresEnding9.Dispose();
HeuristicCombinedDivisorsEnding1.Dispose();
HeuristicCombinedDivisorSquaresEnding1.Dispose();
HeuristicCombinedDivisorsEnding3.Dispose();
HeuristicCombinedDivisorSquaresEnding3.Dispose();
HeuristicCombinedDivisorsEnding7.Dispose();
HeuristicCombinedDivisorSquaresEnding7.Dispose();
HeuristicCombinedDivisorsEnding9.Dispose();
HeuristicCombinedDivisorSquaresEnding9.Dispose();
}
}
@@ -1,15 +1,14 @@
using System.Runtime.CompilerServices;
using ILGPU;
using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu.Accelerators;
internal sealed class HeuristicPrimeTesterAccelerator
internal sealed class HeuristicGroupABPrimeTesterAccelerator
{
#region Pool
[ThreadStatic]
private static Queue<HeuristicPrimeTesterAccelerator>? Pool;
private static Queue<HeuristicGroupABPrimeTesterAccelerator>? Pool;
internal static void WarmUp()
{
@@ -20,23 +19,22 @@ internal sealed class HeuristicPrimeTesterAccelerator
Console.WriteLine($"Preparing heuristic accelerator {i}...");
var accelerator = accelerators[i];
AcceleratorStream stream = accelerator.CreateStream();
Accelerators.HeuristicGpuTables.EnsureStaticTables(i, stream);
_ = HeuristicGpuGroupABTables.EnsureStaticTables(i, stream);
// _ = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.None);
// KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream);
// GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream);
stream.Synchronize();
stream.Dispose();
accelerator.Synchronize();
Console.WriteLine($"Heuristic accelerator {i} is ready");
}
}
internal static HeuristicPrimeTesterAccelerator Rent(int minBufferCapacity = 1)
internal static HeuristicGroupABPrimeTesterAccelerator Rent(int minBufferCapacity = 1)
{
var pool = Pool ??= new();
if (!pool.TryDequeue(out var lease))
{
lease = new HeuristicPrimeTesterAccelerator(minBufferCapacity);
lease = new HeuristicGroupABPrimeTesterAccelerator(minBufferCapacity);
}
else
{
@@ -46,7 +44,7 @@ internal sealed class HeuristicPrimeTesterAccelerator
return lease;
}
internal static void Return(HeuristicPrimeTesterAccelerator lease)
internal static void Return(HeuristicGroupABPrimeTesterAccelerator lease)
{
var pool = Pool ??= new();
pool.Enqueue(lease);
@@ -67,7 +65,7 @@ internal sealed class HeuristicPrimeTesterAccelerator
{
if (Pool != null)
{
var retained = new List<HeuristicPrimeTesterAccelerator>();
var retained = new List<HeuristicGroupABPrimeTesterAccelerator>();
while (Pool.TryDequeue(out var lease))
{
lease.DisposeResources();
@@ -77,10 +75,9 @@ internal sealed class HeuristicPrimeTesterAccelerator
#endregion
private readonly HeuristicGpuDivisorTables _heuristicDivisorTables;
public readonly HeuristicGpuGroupABDivisorTables DivisorTables;
internal readonly Context Context;
public readonly Accelerator Accelerator;
public AcceleratorStream? Stream;
[ThreadStatic]
public static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>? SmallPrimeSieveKernel;
@@ -100,12 +97,12 @@ internal sealed class HeuristicPrimeTesterAccelerator
public readonly Kernel HeuristicTrialDivisionKernel;
public MemoryBuffer1D<ulong, Stride1D.Dense> Input = null!;
public MemoryBuffer1D<byte, Stride1D.Dense> Output = null!;
public MemoryBuffer1D<int, Stride1D.Dense> HeuristicFlag = null!;
public MemoryBuffer1D<ulong, Stride1D.Dense>? Input = null;
public MemoryBuffer1D<byte, Stride1D.Dense>? Output = null;
public MemoryBuffer1D<int, Stride1D.Dense>? HeuristicFlag = null!;
public int BufferCapacity;
private HeuristicGpuTables _sharedTables;
private HeuristicGpuGroupABTables _sharedTables;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupADivisors => _sharedTables.HeuristicGroupADivisors;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupADivisorSquares => _sharedTables.HeuristicGroupADivisorSquares;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding1 => _sharedTables.HeuristicGroupBDivisorsEnding1;
@@ -114,36 +111,28 @@ internal sealed class HeuristicPrimeTesterAccelerator
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding7 => _sharedTables.HeuristicGroupBDivisorSquaresEnding7;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding9 => _sharedTables.HeuristicGroupBDivisorsEnding9;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding9 => _sharedTables.HeuristicGroupBDivisorSquaresEnding9;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding1 => _sharedTables.HeuristicCombinedDivisorsEnding1;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding1 => _sharedTables.HeuristicCombinedDivisorSquaresEnding1;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding3 => _sharedTables.HeuristicCombinedDivisorsEnding3;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding3 => _sharedTables.HeuristicCombinedDivisorSquaresEnding3;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding7 => _sharedTables.HeuristicCombinedDivisorsEnding7;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding7 => _sharedTables.HeuristicCombinedDivisorSquaresEnding7;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding9 => _sharedTables.HeuristicCombinedDivisorsEnding9;
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding9 => _sharedTables.HeuristicCombinedDivisorSquaresEnding9;
public HeuristicGpuDivisorTables HeuristicGpuTables => _heuristicDivisorTables;
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
internal readonly int AcceleratorIndex;
internal HeuristicPrimeTesterAccelerator(int minBufferCapacity)
internal HeuristicGroupABPrimeTesterAccelerator(int minBufferCapacity)
{
var acceleratorIndex = AcceleratorPool.Shared.Rent();
AcceleratorIndex = acceleratorIndex;
Accelerator accelerator = _accelerators[acceleratorIndex];
Context = accelerator.Context;
Accelerator = accelerator;
EnsureCapacity(minBufferCapacity);
HeuristicTrialDivisionKernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables>(PrimeTesterKernels.HeuristicTrialDivisionKernel));
HeuristicTrialDivisionKernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuGroupABDivisorTables>(PrimeTesterKernels.HeuristicTrialGroupABDivisionKernel));
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
// GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream);
_sharedTables = Accelerators.HeuristicGpuTables.EnsureStaticTables(acceleratorIndex, stream);
_heuristicDivisorTables = _sharedTables.CreateHeuristicDivisorTables();
_sharedTables = HeuristicGpuGroupABTables.EnsureStaticTables(acceleratorIndex, stream);
DivisorTables = _sharedTables.CreateHeuristicDivisorTables();
stream.Synchronize();
AcceleratorStreamPool.Return(acceleratorIndex, stream);
@@ -156,8 +145,8 @@ internal sealed class HeuristicPrimeTesterAccelerator
return;
}
Input.Dispose();
Output.Dispose();
Input?.Dispose();
Output?.Dispose();
HeuristicFlag?.Dispose();
Input = Accelerator.Allocate1D<ulong>(minCapacity);
@@ -169,8 +158,6 @@ internal sealed class HeuristicPrimeTesterAccelerator
public void Dispose()
{
Stream?.Dispose();
Stream = null;
Return(this);
}
@@ -179,8 +166,6 @@ internal sealed class HeuristicPrimeTesterAccelerator
Input?.Dispose();
Output?.Dispose();
HeuristicFlag?.Dispose();
Stream?.Dispose();
Stream = null;
// ReleaseSharedTables(_sharedTables);
// These resources are shared between GPU leases
// Accelerator.Dispose();
@@ -68,6 +68,7 @@ public sealed class PrimeOrderCalculatorAccelerator
// Don't take this from the pool as quick uploads of data to the accelerator consumes much of GPU's memory and throws.
AcceleratorStream stream = accelerator.CreateStream();
LastDigitGpuTables.WarmUp(i, stream);
HeuristicCombinedPrimeTesterAccelerator.WarmUp(i, stream);
// SharedHeuristicGpuTables.EnsureStaticTables(accelerator, stream);
// _ = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.None);
// KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream);
@@ -76,6 +77,7 @@ public sealed class PrimeOrderCalculatorAccelerator
stream.Dispose();
accelerator.Synchronize();
}
}
#endregion
@@ -0,0 +1,45 @@
using System.Runtime.CompilerServices;
using ILGPU;
using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu;
public readonly struct HeuristicGpuCombinedDivisorTables(
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding1,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding3,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding7,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding9,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding1,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding3,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding7,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding9)
{
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding1 = combinedDivisorsEnding1;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding3 = combinedDivisorsEnding3;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding7 = combinedDivisorsEnding7;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding9 = combinedDivisorsEnding9;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding1 = combinedDivisorSquaresEnding1;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding3 = combinedDivisorSquaresEnding3;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding7 = combinedDivisorSquaresEnding7;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding9 = combinedDivisorSquaresEnding9;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectDivisors(byte ending) => ending switch
{
1 => CombinedDivisorsEnding1,
3 => CombinedDivisorsEnding3,
7 => CombinedDivisorsEnding7,
9 => CombinedDivisorsEnding9,
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectDivisorSquares(byte ending) => ending switch
{
1 => CombinedDivisorSquaresEnding1,
3 => CombinedDivisorSquaresEnding3,
7 => CombinedDivisorSquaresEnding7,
9 => CombinedDivisorSquaresEnding9,
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
}
@@ -1,139 +0,0 @@
using System.Runtime.CompilerServices;
using ILGPU;
using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu;
public readonly struct HeuristicGpuDivisorTables
{
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding1;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding3;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding7;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding9;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding1;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding3;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding7;
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding9;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupADivisors;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupADivisorSquares;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding1;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding1;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding7;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding7;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding9;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding9;
public HeuristicGpuDivisorTables(
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding1,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding3,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding7,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding9,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding1,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding3,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding7,
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding9,
ArrayView1D<ulong, Stride1D.Dense> groupADivisors,
ArrayView1D<ulong, Stride1D.Dense> groupADivisorSquares,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding1,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding1,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding7,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding7,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding9,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding9)
{
CombinedDivisorsEnding1 = combinedDivisorsEnding1;
CombinedDivisorsEnding3 = combinedDivisorsEnding3;
CombinedDivisorsEnding7 = combinedDivisorsEnding7;
CombinedDivisorsEnding9 = combinedDivisorsEnding9;
CombinedDivisorSquaresEnding1 = combinedDivisorSquaresEnding1;
CombinedDivisorSquaresEnding3 = combinedDivisorSquaresEnding3;
CombinedDivisorSquaresEnding7 = combinedDivisorSquaresEnding7;
CombinedDivisorSquaresEnding9 = combinedDivisorSquaresEnding9;
GroupADivisors = groupADivisors;
GroupADivisorSquares = groupADivisorSquares;
GroupBDivisorsEnding1 = groupBDivisorsEnding1;
GroupBDivisorSquaresEnding1 = groupBDivisorSquaresEnding1;
GroupBDivisorsEnding7 = groupBDivisorsEnding7;
GroupBDivisorSquaresEnding7 = groupBDivisorSquaresEnding7;
GroupBDivisorsEnding9 = groupBDivisorsEnding9;
GroupBDivisorSquaresEnding9 = groupBDivisorSquaresEnding9;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectCombined(byte ending)
{
return ending switch
{
1 => CombinedDivisorsEnding1,
3 => CombinedDivisorsEnding3,
7 => CombinedDivisorsEnding7,
9 => CombinedDivisorsEnding9,
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectCombinedSquares(byte ending)
{
return ending switch
{
1 => CombinedDivisorSquaresEnding1,
3 => CombinedDivisorSquaresEnding3,
7 => CombinedDivisorSquaresEnding7,
9 => CombinedDivisorSquaresEnding9,
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectGroupB(byte ending)
{
return ending switch
{
1 => GroupBDivisorsEnding1,
7 => GroupBDivisorsEnding7,
9 => GroupBDivisorsEnding9,
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectGroupBSquares(byte ending)
{
return ending switch
{
1 => GroupBDivisorSquaresEnding1,
7 => GroupBDivisorSquaresEnding7,
9 => GroupBDivisorSquaresEnding9,
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectDivisors(HeuristicGpuDivisorTableKind kind, byte ending)
{
return kind switch
{
HeuristicGpuDivisorTableKind.GroupA => GroupADivisors,
HeuristicGpuDivisorTableKind.GroupBEnding1 => GroupBDivisorsEnding1,
HeuristicGpuDivisorTableKind.GroupBEnding7 => GroupBDivisorsEnding7,
HeuristicGpuDivisorTableKind.GroupBEnding9 => GroupBDivisorsEnding9,
HeuristicGpuDivisorTableKind.Combined => SelectCombined(ending),
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectDivisorSquares(HeuristicGpuDivisorTableKind kind, byte ending)
{
return kind switch
{
HeuristicGpuDivisorTableKind.GroupA => GroupADivisorSquares,
HeuristicGpuDivisorTableKind.GroupBEnding1 => GroupBDivisorSquaresEnding1,
HeuristicGpuDivisorTableKind.GroupBEnding7 => GroupBDivisorSquaresEnding7,
HeuristicGpuDivisorTableKind.GroupBEnding9 => GroupBDivisorSquaresEnding9,
HeuristicGpuDivisorTableKind.Combined => SelectCombinedSquares(ending),
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
}
}
@@ -0,0 +1,63 @@
using System.Runtime.CompilerServices;
using ILGPU;
using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu;
public readonly struct HeuristicGpuGroupABDivisorTables(
ArrayView1D<ulong, Stride1D.Dense> groupADivisors,
ArrayView1D<ulong, Stride1D.Dense> groupADivisorSquares,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding1,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding1,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding7,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding7,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding9,
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding9)
{
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupADivisors = groupADivisors;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupADivisorSquares = groupADivisorSquares;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding1 = groupBDivisorsEnding1;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding1 = groupBDivisorSquaresEnding1;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding7 = groupBDivisorsEnding7;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding7 = groupBDivisorSquaresEnding7;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding9 = groupBDivisorsEnding9;
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding9 = groupBDivisorSquaresEnding9;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectGroupB(byte ending) => ending switch
{
1 => GroupBDivisorsEnding1,
7 => GroupBDivisorsEnding7,
9 => GroupBDivisorsEnding9,
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectGroupBSquares(byte ending) => ending switch
{
1 => GroupBDivisorSquaresEnding1,
7 => GroupBDivisorSquaresEnding7,
9 => GroupBDivisorSquaresEnding9,
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectDivisors(HeuristicGpuDivisorTableKind kind) => kind switch
{
HeuristicGpuDivisorTableKind.GroupA => GroupADivisors,
HeuristicGpuDivisorTableKind.GroupBEnding1 => GroupBDivisorsEnding1,
HeuristicGpuDivisorTableKind.GroupBEnding7 => GroupBDivisorsEnding7,
HeuristicGpuDivisorTableKind.GroupBEnding9 => GroupBDivisorsEnding9,
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayView1D<ulong, Stride1D.Dense> SelectDivisorSquares(HeuristicGpuDivisorTableKind kind) => kind switch
{
HeuristicGpuDivisorTableKind.GroupA => GroupADivisorSquares,
HeuristicGpuDivisorTableKind.GroupBEnding1 => GroupBDivisorSquaresEnding1,
HeuristicGpuDivisorTableKind.GroupBEnding7 => GroupBDivisorSquaresEnding7,
HeuristicGpuDivisorTableKind.GroupBEnding9 => GroupBDivisorSquaresEnding9,
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
};
}
@@ -11,7 +11,6 @@ public enum HeuristicGpuDivisorTableKind : byte
GroupBEnding1 = 1,
GroupBEnding7 = 7,
GroupBEnding9 = 9,
Combined = 255,
}
internal static class PrimeTesterKernels
@@ -78,17 +77,49 @@ internal static class PrimeTesterKernels
results[index] = result;
}
public static void HeuristicTrialDivisionKernel(
public static void HeuristicTrialCombinedDivisionKernel(
Index1D index,
byte nMod10,
ArrayView<int> resultFlag,
ulong n,
ulong maxDivisorSquare,
HeuristicGpuCombinedDivisorTables tables)
{
// byte nMod10 = (byte)(n % 10UL);
ArrayView1D<ulong, Stride1D.Dense> divisors = tables.SelectDivisors(nMod10);
ArrayView1D<ulong, Stride1D.Dense> divisorSquares = tables.SelectDivisorSquares(nMod10);
int divisorLength = (int)divisorSquares.Length;
int threadIndex = index;
if (threadIndex >= divisorLength)
{
return;
}
ulong divisorSquare = divisorSquares[threadIndex];
if (divisorSquare > maxDivisorSquare)
{
return;
}
ulong divisor = divisors[threadIndex];
divisor = n % divisor;
if (divisor == 0UL)
{
resultFlag[0] = 1;
}
}
public static void HeuristicTrialGroupABDivisionKernel(
Index1D index,
ArrayView<int> resultFlag,
ulong n,
ulong maxDivisorSquare,
HeuristicGpuDivisorTableKind tableKind,
HeuristicGpuDivisorTables tables)
HeuristicGpuGroupABDivisorTables tables)
{
byte nMod10 = (byte)(n % 10UL);
ArrayView1D<ulong, Stride1D.Dense> divisors = tables.SelectDivisors(tableKind, nMod10);
ArrayView1D<ulong, Stride1D.Dense> divisorSquares = tables.SelectDivisorSquares(tableKind, nMod10);
ArrayView1D<ulong, Stride1D.Dense> divisors = tables.SelectDivisors(tableKind);
ArrayView1D<ulong, Stride1D.Dense> divisorSquares = tables.SelectDivisorSquares(tableKind);
int divisorLength = (int)divisorSquares.Length;
int threadIndex = index;
@@ -8,6 +8,7 @@ using System.Threading;
using PerfectNumbers.Core;
using ILGPU;
using ILGPU.Runtime;
using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core.Gpu;
@@ -106,9 +107,12 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
ulong lastProcessed;
// GpuPrimeWorkLimiter();
int acceleratorIndex = AcceleratorPool.Shared.Rent();
var accelerator = _accelerators[acceleratorIndex];
var stream = accelerator.CreateStream();
// int acceleratorIndex = AcceleratorPool.Shared.Rent();
// var accelerator = _accelerators[acceleratorIndex];
var gpu = HeuristicCombinedPrimeTesterAccelerator.Rent(batchCapacity);
var acceleratorIndex = gpu.AcceleratorIndex;
var accelerator = gpu.Accelerator;
var stream = AcceleratorStreamPool.Rent(acceleratorIndex);
// Monitor.Enter(gpuLease.ExecutionLock);
@@ -116,6 +120,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
var resources = RentBatchResources(accelerator, batchCapacity);
composite = CheckDivisors(
gpu,
prime,
allowedMax,
kernel,
@@ -137,8 +142,9 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
out coveredRange,
out processedCount);
stream.Dispose();
AcceleratorStreamPool.Return(acceleratorIndex, stream);
ReturnBatchResources(resources);
HeuristicCombinedPrimeTesterAccelerator.Return(gpu);
// Monitor.Exit(gpuLease.ExecutionLock);
// GpuPrimeWorkLimiter.Release();
@@ -182,6 +188,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
}
private static bool CheckDivisors(
HeuristicCombinedPrimeTesterAccelerator gpu,
ulong prime,
ulong allowedMax,
Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> kernel,
@@ -205,7 +212,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
{
int batchCapacity = (int)divisorDataBuffer.Length;
bool composite = false;
bool processedAll = false;
bool processedAll;
processedCount = 0UL;
lastProcessed = 0UL;
@@ -303,7 +310,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
{
ulong divisorValue = (ulong)nextDivisor128;
MontgomeryDivisorData montgomeryData = MontgomeryDivisorData.FromModulus(divisorValue);
ulong divisorCycle = ResolveDivisorCycle(divisorValue, prime, in montgomeryData);
ulong divisorCycle = ResolveDivisorCycle(gpu, divisorValue, prime, in montgomeryData);
if (divisorCycle == prime)
{
processedCount += (ulong)(i + 1);
@@ -375,9 +382,9 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong ResolveDivisorCycle(ulong divisor, ulong prime, in MontgomeryDivisorData divisorData)
private static ulong ResolveDivisorCycle(HeuristicCombinedPrimeTesterAccelerator gpu, ulong divisor, ulong prime, in MontgomeryDivisorData divisorData)
{
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(divisor, prime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL)
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(gpu, divisor, prime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL)
{
return MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed);
}
@@ -498,6 +505,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
private readonly MersenneNumberDivisorByDivisorGpuTester _owner;
private readonly HeuristicCombinedPrimeTesterAccelerator _primeTesterAccelerator;
private readonly Accelerator _accelerator;
private readonly AcceleratorStream _stream;
private Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> _kernel = null!;
@@ -516,8 +524,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
{
_owner = owner;
// GpuPrimeWorkLimiter.Acquire();
int acceleratorIndex = AcceleratorPool.Shared.Rent();
Accelerator accelerator = _accelerators[acceleratorIndex];
_primeTesterAccelerator = HeuristicCombinedPrimeTesterAccelerator.Rent();
Accelerator accelerator = _primeTesterAccelerator.Accelerator;
_accelerator = accelerator;
_stream = accelerator.CreateStream();
_capacity = Math.Max(1, owner._gpuBatchSize);
@@ -600,7 +608,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
if (cycle == 0UL)
{
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(divisor, firstPrime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL)
var gpu = _primeTesterAccelerator;
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(gpu, divisor, firstPrime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL)
{
cycle = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed);
}
@@ -657,10 +666,11 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
ref byte hitRef = ref MemoryMarshal.GetReference(hitSlice);
hitView.CopyToCPU(stream, ref hitRef, length);
stream.Synchronize();
// Monitor.Exit(_lease.ExecutionLock);
}
public void Dispose()
public void Return()
{
_owner._sessionPool.Add(this);
}
@@ -3,6 +3,7 @@ using ILGPU.Runtime;
using System.Runtime.CompilerServices;
using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
using ILGPU;
namespace PerfectNumbers.Core;
@@ -172,10 +173,20 @@ public sealed class HeuristicCombinedPrimeTester
{
byte nMod10 = (byte)n.Mod10();
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
return IsPrimeGpu(n, maxDivisorSquare, nMod10);
var gpu = HeuristicCombinedPrimeTesterAccelerator.Rent(1);
bool isPrime = IsPrimeGpu(gpu, n, maxDivisorSquare, nMod10);
HeuristicCombinedPrimeTesterAccelerator.Return(gpu);
return isPrime;
}
public static bool IsPrimeGpu(ulong n, ulong maxDivisorSquare, byte nMod10)
public static bool IsPrimeGpu(HeuristicCombinedPrimeTesterAccelerator gpu, ulong n)
{
byte nMod10 = (byte)n.Mod10();
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
return IsPrimeGpu(gpu, n, maxDivisorSquare, nMod10);
}
public static bool IsPrimeGpu(HeuristicCombinedPrimeTesterAccelerator gpu, ulong n, ulong maxDivisorSquare, byte nMod10)
{
// TODO: Is this condition ever met on the execution path in EvenPerfectBitScanner?
if (maxDivisorSquare < 9UL)
@@ -183,7 +194,7 @@ public sealed class HeuristicCombinedPrimeTester
return EvaluateWithOpenNumericFallback(n);
}
bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(n, maxDivisorSquare, nMod10);
bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(gpu, n, maxDivisorSquare, nMod10);
return !compositeDetected;
}
@@ -222,40 +233,38 @@ public sealed class HeuristicCombinedPrimeTester
};
private static bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10)
private static bool HeuristicTrialDivisionGpuDetectsDivisor(HeuristicCombinedPrimeTesterAccelerator gpu, ulong n, ulong maxDivisorSquare, byte nMod10)
{
// GpuPrimeWorkLimiter.Acquire();
var gpu = HeuristicPrimeTesterAccelerator.Rent(1);
var accelerator = gpu.Accelerator;
var stream = gpu.Stream;
var kernel = gpu.HeuristicTrialDivisionKernel;
var flagView1D = gpu.HeuristicFlag.View;
var flagView = flagView1D.AsContiguous();
// var gpu = HeuristicCombinedPrimeTesterAccelerator.Rent(1);
var acceleratorIndex = gpu.AcceleratorIndex;
var stream = AcceleratorStreamPool.Rent(acceleratorIndex);
var kernel = gpu.HeuristicCombinedTrialDivisionKernel;
var flagView1D = gpu.HeuristicFlag!.View;
bool compositeDetected = false;
int divisorLength = GetCombinedDivisors(nMod10).Length;
int compositeFlag = 0;
flagView1D.CopyFromCPU(stream, ref compositeFlag, 1);
stream!.Synchronize();
kernel.Launch(
var kernelLauncher = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicGpuCombinedDivisorTables>>();
kernelLauncher(
stream,
1,
divisorLength,
flagView,
nMod10,
flagView1D,
n,
maxDivisorSquare,
HeuristicGpuDivisorTableKind.Combined,
gpu.HeuristicGpuTables);
gpu.DivisorTables);
flagView1D.CopyToCPU(stream, ref compositeFlag, 1);
stream.Synchronize();
AcceleratorStreamPool.Return(acceleratorIndex, stream);
// GpuPrimeWorkLimiter.Release();
compositeDetected = compositeFlag != 0;
HeuristicPrimeTesterAccelerator.Return(gpu);
HeuristicCombinedPrimeTesterAccelerator.Return(gpu);
return compositeDetected;
}
@@ -490,6 +499,7 @@ public sealed class HeuristicCombinedPrimeTester
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static ulong ResolveHeuristicCycleLength(
HeuristicCombinedPrimeTesterAccelerator gpu,
ulong exponent,
in HeuristicDivisorPreparation preparation,
out bool cycleFromHint,
@@ -508,6 +518,7 @@ public sealed class HeuristicCombinedPrimeTester
MontgomeryDivisorData divisorData = preparation.DivisorData;
bool trySuccess = MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
gpu,
divisor,
exponent,
divisorData,
+59 -48
View File
@@ -3,6 +3,7 @@ using System.Runtime.CompilerServices;
using ILGPU.Runtime;
using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
using ILGPU;
namespace PerfectNumbers.Core;
@@ -242,72 +243,80 @@ public sealed class HeuristicPrimeTester
private bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10)
{
// GpuPrimeWorkLimiter.Acquire();
var gpu = HeuristicPrimeTesterAccelerator.Rent(1);
var stream = gpu.Stream;
var kernel = gpu.HeuristicTrialDivisionKernel;
var flagView1D = gpu.HeuristicFlag.View;
var flagView = flagView1D.AsContiguous();
var gpu = HeuristicCombinedPrimeTesterAccelerator.Rent(1);
int acceleratorIndex = gpu.AcceleratorIndex;
var stream = AcceleratorStreamPool.Rent(acceleratorIndex);
var kernel = gpu.HeuristicCombinedTrialDivisionKernel;
var flagView1D = gpu.HeuristicFlag!.View;
bool compositeDetected = false;
bool compositeDetected;
int compositeFlag = 0;
int groupALength = HeuristicPrimeSieves.GroupADivisors.Length;
flagView1D.CopyFromCPU(stream, ref compositeFlag, 1);
kernel.Launch(
stream!,
// Index1D index,
// ArrayView<int> resultFlag,
// ulong n,
// ulong maxDivisorSquare,
// HeuristicGpuDivisorTableKind tableKind,
// HeuristicGpuDivisorTables tables)
var kernelLauncher = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuCombinedDivisorTables>>();
kernelLauncher(
stream,
1,
groupALength,
flagView,
nMod10,
flagView1D,
n,
maxDivisorSquare,
HeuristicGpuDivisorTableKind.GroupA,
gpu.HeuristicGpuTables);
gpu.DivisorTables);
flagView1D.CopyToCPU(stream, ref compositeFlag, 1);
stream!.Synchronize();
compositeDetected = compositeFlag != 0;
// compositeDetected = compositeFlag != 0;
if (!compositeDetected)
{
ReadOnlySpan<byte> endingOrder = GetGroupBEndingOrder(nMod10);
for (int i = 0; i < endingOrder.Length && !compositeDetected; i++)
{
byte ending = endingOrder[i];
HeuristicGpuDivisorTableKind tableKind = ending switch
{
1 => HeuristicGpuDivisorTableKind.GroupBEnding1,
7 => HeuristicGpuDivisorTableKind.GroupBEnding7,
9 => HeuristicGpuDivisorTableKind.GroupBEnding9,
_ => HeuristicGpuDivisorTableKind.GroupA,
};
// if (!compositeDetected)
// {
// ReadOnlySpan<byte> endingOrder = GetGroupBEndingOrder(nMod10);
// for (int i = 0; i < endingOrder.Length && !compositeDetected; i++)
// {
// byte ending = endingOrder[i];
// HeuristicGpuDivisorTableKind tableKind = ending switch
// {
// 1 => HeuristicGpuDivisorTableKind.GroupBEnding1,
// 7 => HeuristicGpuDivisorTableKind.GroupBEnding7,
// 9 => HeuristicGpuDivisorTableKind.GroupBEnding9,
// _ => HeuristicGpuDivisorTableKind.GroupA,
// };
if (tableKind == HeuristicGpuDivisorTableKind.GroupA)
{
continue;
}
// if (tableKind == HeuristicGpuDivisorTableKind.GroupA)
// {
// continue;
// }
int divisorLength = GetGroupBDivisors(ending).Length;
// int divisorLength = GetGroupBDivisors(ending).Length;
compositeFlag = 0;
flagView1D.CopyFromCPU(stream, ref compositeFlag, 1);
kernel.Launch(
stream,
1,
divisorLength,
flagView,
n,
maxDivisorSquare,
tableKind,
gpu.HeuristicGpuTables);
flagView1D.CopyToCPU(stream, ref compositeFlag, 1);
stream.Synchronize();
compositeDetected = compositeFlag != 0;
}
}
// compositeFlag = 0;
// flagView1D.CopyFromCPU(stream, ref compositeFlag, 1);
// kernelLauncher(
// stream,
// 1,
// flagView1D,
// n,
// maxDivisorSquare,
// tableKind,
// gpu.HeuristicGpuTables);
// flagView1D.CopyToCPU(stream, ref compositeFlag, 1);
// stream.Synchronize();
// compositeDetected = compositeFlag != 0;
// }
// }
gpu.Dispose();
AcceleratorStreamPool.Return(acceleratorIndex, stream);
gpu.Return();
// GpuPrimeWorkLimiter.Release();
return compositeDetected;
}
@@ -457,6 +466,7 @@ public sealed class HeuristicPrimeTester
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static ulong ResolveHeuristicCycleLength(
HeuristicCombinedPrimeTesterAccelerator gpu,
ulong exponent,
in HeuristicDivisorPreparation preparation,
out bool cycleFromHint,
@@ -475,6 +485,7 @@ public sealed class HeuristicPrimeTester
MontgomeryDivisorData divisorData = preparation.DivisorData;
bool trySuccess = MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
gpu,
divisor,
exponent,
divisorData,
@@ -20,6 +20,6 @@ public interface IMersenneNumberDivisorByDivisorTester
{
void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan<ulong> primes, Span<byte> hits);
void Dispose();
void Return();
}
}
+12 -10
View File
@@ -52,7 +52,7 @@ public class MersenneDivisorCycles
return _smallCycles!;
}
public static bool CycleEqualsExponent(ulong divisor, in MontgomeryDivisorData divisorData, ulong exponent)
public static bool CycleEqualsExponent(HeuristicCombinedPrimeTesterAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData, ulong exponent)
{
if (CycleEqualsExponentForMersenneCandidate(divisor, divisorData, exponent))
{
@@ -82,7 +82,7 @@ public class MersenneDivisorCycles
return heuristicCycle == exponent;
}
if (TryCalculateCycleLengthForExponentCpu(divisor, exponent, divisorData, out ulong cycleLength, out bool _) && cycleLength != 0UL)
if (TryCalculateCycleLengthForExponentCpu(gpu, divisor, exponent, divisorData, out ulong cycleLength, out bool _) && cycleLength != 0UL)
{
return cycleLength == exponent;
}
@@ -260,6 +260,7 @@ public class MersenneDivisorCycles
}
public static bool TryCalculateCycleLengthForExponentCpu(
HeuristicCombinedPrimeTesterAccelerator gpu,
ulong divisor,
ulong exponent,
in MontgomeryDivisorData divisorData,
@@ -328,8 +329,8 @@ public class MersenneDivisorCycles
factorCounts[2UL] = twoCount;
}
bool exponentAccumulated = AccumulateFactors(exponent, factorCounts);
bool factorsAccumulated = exponentAccumulated && AccumulateFactors(k, factorCounts);
bool exponentAccumulated = AccumulateFactors(gpu, exponent, factorCounts);
bool factorsAccumulated = exponentAccumulated && AccumulateFactors(gpu, k, factorCounts);
if (factorsAccumulated)
{
@@ -344,6 +345,7 @@ public class MersenneDivisorCycles
}
private static bool AccumulateFactors(
HeuristicCombinedPrimeTesterAccelerator gpu,
ulong value,
Dictionary<ulong, int> counts)
{
@@ -353,7 +355,7 @@ public class MersenneDivisorCycles
}
Dictionary<ulong, int> scratch = ThreadStaticPools.RentFactorScratchDictionary();
bool success = TryFactorIntoCountsInternal(value, scratch);
bool success = TryFactorIntoCountsInternal(gpu, value, scratch);
if (!success)
{
scratch.Clear();
@@ -375,7 +377,7 @@ public class MersenneDivisorCycles
// private static ulong _tryFactorIntoCountsInternalHits;
// private static ulong _tryCalculateCycleLengthHeuristicHits;
private static bool TryFactorIntoCountsInternal(ulong value, Dictionary<ulong, int> counts)
private static bool TryFactorIntoCountsInternal(HeuristicCombinedPrimeTesterAccelerator gpu, ulong value, Dictionary<ulong, int> counts)
{
// The EvenPerfectBitScanner only requests factors for values >= 2 here. Leave the guard documented without executing it.
// if (value <= 1UL)
@@ -412,7 +414,7 @@ public class MersenneDivisorCycles
// Console.WriteLine($"MersenneDivisorCycles.TryFactorIntoCountsInternal hits {Volatile.Read(ref _tryFactorIntoCountsInternalHits)}");
// HeuristicPrimeTester primeTester = _primeTester ??= new();
if (PrimeTester.IsPrimeGpu(remaining))
if (HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, remaining))
// if (PrimeTester.IsPrimeCpu(remaining, CancellationToken.None))
{
AddFactor(counts, remaining);
@@ -437,13 +439,13 @@ public class MersenneDivisorCycles
// return false;
// }
if (!TryFactorIntoCountsInternal(factor, counts))
if (!TryFactorIntoCountsInternal(gpu, factor, counts))
{
return false;
}
// Guard the recursive calls to terminate once Pollard-Rho reduces either branch to 1.
if (quotient > 1UL && !TryFactorIntoCountsInternal(quotient, counts))
if (quotient > 1UL && !TryFactorIntoCountsInternal(gpu, quotient, counts))
{
return false;
}
@@ -958,7 +960,7 @@ public class MersenneDivisorCycles
// HeuristicPrimeTester primeTester = _primeTester ??= new();
// if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.Exclusive.IsPrimeCpu(divisor, CancellationToken.None))
// if (!skipPrimeOrderHeuristic && primeTester.IsPrimeGpu(divisor))
if (!skipPrimeOrderHeuristic && PrimeTester.IsPrimeGpu(divisor))
if (!skipPrimeOrderHeuristic && HeuristicCombinedPrimeTester.IsPrimeGpu(divisor))
{
ulong computedOrder = PrimeOrderCalculator.Calculate(
divisor,
@@ -21,3 +21,4 @@ public static class PerfectNumberConstants
// TODO: Promote these magic numbers into a runtime profile derived from EvenPerfectBitScanner.Benchmarks so we can retune
// them automatically when the optimal ranges for 2kp+1 >= 138M change.
}
+44 -28
View File
@@ -3,6 +3,7 @@ using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using ILGPU;
using PerfectNumbers.Core.Cpu;
using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
@@ -54,34 +55,39 @@ internal static partial class PrimeOrderCalculator
ulong phi = prime - 1UL;
PrimeOrderCalculatorAccelerator gpu = PrimeOrderCalculatorAccelerator.Rent(1);
HeuristicCombinedPrimeTesterAccelerator gpu2 = HeuristicCombinedPrimeTesterAccelerator.Rent(1);
if (IsGpuHeuristicDevice && PrimeOrderGpuHeuristics.TryCalculateOrder(prime, previousOrder, config, divisorData, out ulong gpuOrder))
{
PrimeOrderCalculatorAccelerator.Return(gpu);
HeuristicCombinedPrimeTesterAccelerator.Return(gpu2);
return gpuOrder;
}
PartialFactorResult phiFactors = PartialFactor(gpu, phi, config);
PartialFactorResult phiFactors = PartialFactor(gpu, gpu2, phi, config);
ulong result;
if (phiFactors.Factors is null)
{
result = prime <= PerfectNumberConstants.MaxQForDivisorCycles
? CalculateByFactorizationGpu(gpu, prime, divisorData)
: CalculateByFactorizationCpu(gpu, prime, divisorData);
? CalculateByFactorizationGpu(gpu, gpu2, prime, divisorData)
: CalculateByFactorizationCpu(gpu, gpu2, prime, divisorData);
phiFactors.Dispose();
PrimeOrderCalculatorAccelerator.Return(gpu);
HeuristicCombinedPrimeTesterAccelerator.Return(gpu2);
return result;
}
result = RunHeuristicPipelineCpu(gpu, prime, previousOrder, config, divisorData, phi, phiFactors);
result = RunHeuristicPipelineCpu(gpu, gpu2, prime, previousOrder, config, divisorData, phi, phiFactors);
phiFactors.Dispose();
PrimeOrderCalculatorAccelerator.Return(gpu);
HeuristicCombinedPrimeTesterAccelerator.Return(gpu2);
return result;
}
private static ulong RunHeuristicPipelineCpu(
PrimeOrderCalculatorAccelerator gpu,
HeuristicCombinedPrimeTesterAccelerator gpu2,
ulong prime,
ulong? previousOrder,
in PrimeOrderSearchConfig config,
@@ -98,7 +104,7 @@ internal static partial class PrimeOrderCalculator
ulong candidateOrder = InitializeStartingOrderCpu(gpu, prime, phi, divisorData);
candidateOrder = ExponentLoweringCpu(candidateOrder, prime, phiFactors, divisorData);
if (TryConfirmOrderCpu(gpu, prime, candidateOrder, divisorData, config, out PartialFactorResult? orderFactors))
if (TryConfirmOrderCpu(gpu, gpu2, prime, candidateOrder, divisorData, config, out PartialFactorResult? orderFactors))
{
// GpuPrimeWorkLimiter.Release();
return candidateOrder;
@@ -108,10 +114,10 @@ internal static partial class PrimeOrderCalculator
{
orderFactors?.Dispose();
// GpuPrimeWorkLimiter.Release();
return CalculateByFactorizationGpu(gpu, prime, divisorData);
return CalculateByFactorizationGpu(gpu, gpu2, prime, divisorData);
}
if (TryHeuristicFinishCpu(gpu, prime, candidateOrder, previousOrder, divisorData, config, phiFactors, orderFactors, out ulong order))
if (TryHeuristicFinishCpu(gpu, gpu2, prime, candidateOrder, previousOrder, divisorData, config, phiFactors, orderFactors, out ulong order))
{
// GpuPrimeWorkLimiter.Release();
return order;
@@ -340,6 +346,7 @@ internal static partial class PrimeOrderCalculator
private static bool TryConfirmOrderCpu(
PrimeOrderCalculatorAccelerator gpu,
HeuristicCombinedPrimeTesterAccelerator gpu2,
ulong prime,
ulong order,
in MontgomeryDivisorData divisorData,
@@ -358,7 +365,7 @@ internal static partial class PrimeOrderCalculator
return false;
}
PartialFactorResult factorization = PartialFactor(gpu, order, config);
PartialFactorResult factorization = PartialFactor(gpu, gpu2, order, config);
if (factorization.Factors is null)
{
factorization.Dispose();
@@ -503,6 +510,7 @@ internal static partial class PrimeOrderCalculator
private static bool TryHeuristicFinishCpu(
PrimeOrderCalculatorAccelerator gpu,
HeuristicCombinedPrimeTesterAccelerator gpu2,
ulong prime,
ulong order,
ulong? previousOrder,
@@ -520,7 +528,7 @@ internal static partial class PrimeOrderCalculator
}
// Reuse the partial factorization from TryConfirmOrderCpu when available.
PartialFactorResult orderFactors = cachedOrderFactors ?? PartialFactor(gpu, order, config);
PartialFactorResult orderFactors = cachedOrderFactors ?? PartialFactor(gpu, gpu2, order, config);
try
{
if (orderFactors.Factors is null)
@@ -636,7 +644,7 @@ internal static partial class PrimeOrderCalculator
bool equalsOne = powStepper.InitializeCpuIsUnity(candidate);
powStepperInitialized = true;
if (equalsOne && TryConfirmCandidateCpu(gpu, prime, candidate, divisorData, config, ref powUsed, powBudget))
if (equalsOne && TryConfirmCandidateCpu(gpu, gpu2, prime, candidate, divisorData, config, ref powUsed, powBudget))
{
if (gpuPool is not null)
{
@@ -673,7 +681,7 @@ internal static partial class PrimeOrderCalculator
continue;
}
if (!TryConfirmCandidateCpu(gpu, prime, candidate, divisorData, config, ref powUsed, powBudget))
if (!TryConfirmCandidateCpu(gpu, gpu2, prime, candidate, divisorData, config, ref powUsed, powBudget))
{
continue;
}
@@ -884,9 +892,9 @@ internal static partial class PrimeOrderCalculator
}
}
private static bool TryConfirmCandidateCpu(PrimeOrderCalculatorAccelerator gpu, ulong prime, ulong candidate, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config, ref int powUsed, int powBudget)
private static bool TryConfirmCandidateCpu(PrimeOrderCalculatorAccelerator gpu, HeuristicCombinedPrimeTesterAccelerator gpu2, ulong prime, ulong candidate, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config, ref int powUsed, int powBudget)
{
PartialFactorResult factorization = PartialFactor(gpu, candidate, config);
PartialFactorResult factorization = PartialFactor(gpu, gpu2, candidate, config);
try
{
if (factorization.Factors is null)
@@ -1082,7 +1090,9 @@ internal static partial class PrimeOrderCalculator
// private static ulong _partialFactorPendingHits;
// private static ulong _partialFactorCofactorHits;
private static PartialFactorResult PartialFactor(PrimeOrderCalculatorAccelerator gpu, ulong value, in PrimeOrderSearchConfig config)
private static bool _goToGpu = true;
private static PartialFactorResult PartialFactor(PrimeOrderCalculatorAccelerator gpu, HeuristicCombinedPrimeTesterAccelerator gpu2, ulong value, in PrimeOrderSearchConfig config)
{
if (value <= 1UL)
{
@@ -1169,7 +1179,11 @@ internal static partial class PrimeOrderCalculator
// Console.WriteLine($"Partial factor hits {Volatile.Read(ref _partialFactorHits)}");
// bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None);
bool isPrime = PrimeTester.IsPrimeGpu(gpu, composite);
bool goToGpu = Volatile.Read(ref _goToGpu);
Volatile.Write(ref _goToGpu, !goToGpu);
bool isPrime = goToGpu
? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu2, composite)
: PrimeTester.IsPrime(composite);
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
if (isPrime)
@@ -1241,7 +1255,7 @@ internal static partial class PrimeOrderCalculator
// Console.WriteLine($"Partial factor pending hits {Volatile.Read(ref _partialFactorPendingHits)}");
// bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None);
bool isPrime = PrimeTester.IsPrimeGpu(gpu, composite);
bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu2, composite);
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
entry = entry.WithPrimality(isPrime);
@@ -1278,7 +1292,8 @@ internal static partial class PrimeOrderCalculator
// Atomic.Add(ref _partialFactorCofactorHits, 1UL);
// Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}");
cofactorIsPrime = PrimeTester.IsPrimeGpu(gpu, cofactor);
cofactorIsPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu2, cofactor);
// cofactorIsPrime = PrimeTester.IsPrimeGpu(gpu, cofactor);
// cofactorIsPrime = PrimeTester.IsPrimeGpu(cofactor);
// cofactorIsPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(cofactor);
}
@@ -1698,11 +1713,11 @@ internal static partial class PrimeOrderCalculator
exponents = newExponentSpan;
}
private static ulong CalculateByFactorizationCpu(PrimeOrderCalculatorAccelerator gpu, ulong prime, in MontgomeryDivisorData divisorData)
private static ulong CalculateByFactorizationCpu(PrimeOrderCalculatorAccelerator gpu, HeuristicCombinedPrimeTesterAccelerator gpu2, ulong prime, in MontgomeryDivisorData divisorData)
{
ulong phi = prime - 1UL;
Dictionary<ulong, int> counts = new(capacity: 8);
FactorCompletelyCpu(phi, counts);
FactorCompletelyCpu(gpu2, phi, counts);
if (counts.Count == 0)
{
return phi;
@@ -1746,9 +1761,9 @@ internal static partial class PrimeOrderCalculator
return order;
}
private static void FactorCompletelyCpu(ulong value, Dictionary<ulong, int> counts)
private static void FactorCompletelyCpu(HeuristicCombinedPrimeTesterAccelerator gpu, ulong value, Dictionary<ulong, int> counts)
{
FactorCompletely(value, counts, knownComposite: false);
FactorCompletely(gpu, value, counts, knownComposite: false);
}
// private static ulong _factorCompletelyHits;
@@ -1756,7 +1771,7 @@ internal static partial class PrimeOrderCalculator
// [ThreadStatic]
// private static HeuristicPrimeTester? _tester;
private static void FactorCompletely(ulong value, Dictionary<ulong, int> counts, bool knownComposite)
private static void FactorCompletely(HeuristicCombinedPrimeTesterAccelerator gpu, ulong value, Dictionary<ulong, int> counts, bool knownComposite)
{
if (value <= 1UL)
{
@@ -1768,7 +1783,8 @@ internal static partial class PrimeOrderCalculator
// HeuristicPrimeTester tester = _tester ??= new();
// bool isPrime = primeTester.HeuristicIsPrimeGpu(value);
bool isPrime = PrimeTester.IsPrimeGpu(value);
bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, value);
// bool isPrime = PrimeTester.IsPrimeGpu(value);
// bool isPrime = PrimeTester.IsPrimeGpu(value);
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(value);
if (!knownComposite && isPrime)
@@ -1784,7 +1800,7 @@ internal static partial class PrimeOrderCalculator
// Console.WriteLine($"Factor completely after PollardRho hits {Volatile.Read(ref _factorCompletelyPollardRhoHits)}");
// isPrime = primeTester.HeuristicIsPrimeGpu(factor);
isPrime = PrimeTester.IsPrimeGpu(factor);
isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, factor);
// isPrime = PrimeTester.IsPrimeGpu(factor);
// isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(factor);
if (isPrime)
@@ -1801,13 +1817,13 @@ internal static partial class PrimeOrderCalculator
if (remaining > 1UL)
{
FactorCompletely(remaining, counts, knownComposite: false);
FactorCompletely(gpu, remaining, counts, knownComposite: false);
}
return;
}
FactorCompletely(factor, counts, knownComposite: true);
FactorCompletely(gpu, factor, counts, knownComposite: true);
if (quotient == factor)
{
@@ -1821,7 +1837,7 @@ internal static partial class PrimeOrderCalculator
// Atomic.Add(ref _partialFactorCofactorHits, 1UL);
// Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}");
isPrime = PrimeTester.IsPrimeGpu(quotient);
isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, quotient);
// isPrime = PrimeTester.IsPrimeGpu(quotient);
// isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(quotient);
}
@@ -1832,7 +1848,7 @@ internal static partial class PrimeOrderCalculator
}
else
{
FactorCompletely(quotient, counts, knownComposite: true);
FactorCompletely(gpu, quotient, counts, knownComposite: true);
}
}
@@ -12,13 +12,13 @@ internal static partial class PrimeOrderCalculator
private const int GpuSmallPrimeFactorSlots = 64;
private static ulong CalculateByFactorizationGpu(PrimeOrderCalculatorAccelerator gpu, ulong prime, in MontgomeryDivisorData divisorData)
private static ulong CalculateByFactorizationGpu(PrimeOrderCalculatorAccelerator gpu, HeuristicCombinedPrimeTesterAccelerator gpu2, ulong prime, in MontgomeryDivisorData divisorData)
{
ulong phi = prime - 1UL;
Dictionary<ulong, int> counts = gpu.Pow2ModEntriesToTestOnHost;
counts.Clear();
FactorCompletelyCpu(phi, counts);
FactorCompletelyCpu(gpu2, phi, counts);
if (counts.Count == 0)
{
return phi;