Initial PollardRho implementation for GPU. Added pool for accelerator streams shared across the threads. GPU processing performance and stabilization improvements. GpuUInt128.BinaryGcd switched to the version which outperformed other in benchmarks.
This commit is contained in:
1 parent
735bfa1557
commit
5bc65464d0
15 files changed
+371
-121
No files matched your search
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
namespace PerfectNumbers.Core;
|
namespace PerfectNumbers.Core.Cpu;
|
||||||
|
|
||||||
internal static class DeterministicRandom
|
internal static class DeterministicRandomCpu
|
||||||
{
|
{
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static ulong s_threadState;
|
private static ulong s_threadState;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
|
public static class AcceleratorStreamPool
|
||||||
|
{
|
||||||
|
private static readonly ConcurrentDictionary<Accelerator, ConcurrentQueue<AcceleratorStream>> _streams = new(20_480, PerfectNumberConstants.RollingAccelerators);
|
||||||
|
// private static readonly ConcurrentDictionary<Accelerator, AcceleratorStream> _streams = new(AcceleratorPool.Shared.Accelerators.ToDictionary(static x => x, CreateStream));
|
||||||
|
|
||||||
|
private static SemaphoreSlim CreateLock() => new(PerfectNumberConstants.ThreadsByStream);
|
||||||
|
private static AcceleratorStream CreateStream(Accelerator accelerator) => accelerator.CreateStream();
|
||||||
|
|
||||||
|
private static readonly ConcurrentDictionary<Accelerator, SemaphoreSlim> _locks = new(AcceleratorPool.Shared.Accelerators.ToDictionary(x => x, static x => CreateLock()));
|
||||||
|
|
||||||
|
public static AcceleratorStream Rent(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
var streamLock = _locks.GetOrAdd(accelerator, static _ => new SemaphoreSlim(PerfectNumberConstants.ThreadsByStream));
|
||||||
|
|
||||||
|
streamLock.Wait();
|
||||||
|
var queue = _streams.GetOrAdd(accelerator, static _ => new());
|
||||||
|
|
||||||
|
return queue.TryDequeue(out var stream)
|
||||||
|
? stream
|
||||||
|
: accelerator.CreateStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Return(AcceleratorStream stream)
|
||||||
|
{
|
||||||
|
Accelerator accelerator = stream.Accelerator;
|
||||||
|
var streamLock = _locks.GetOrAdd(accelerator, static _ => CreateLock());
|
||||||
|
var queue = _streams.GetOrAdd(accelerator, static _ => new());
|
||||||
|
queue.Enqueue(stream);
|
||||||
|
streamLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ public sealed class Pow2MontgomeryAccelerator
|
|||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static Queue<Pow2MontgomeryAccelerator>? _pool;
|
private static Queue<Pow2MontgomeryAccelerator>? _pool;
|
||||||
|
|
||||||
private static AcceleratorPool _accelerators = new(PerfectNumberConstants.RollingAccelerators << 1);
|
private static AcceleratorPool _accelerators = new(PerfectNumberConstants.RollingAccelerators);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static Pow2MontgomeryAccelerator Rent(int primeTesterCapacity)
|
public static Pow2MontgomeryAccelerator Rent(int primeTesterCapacity)
|
||||||
@@ -25,7 +25,7 @@ public sealed class Pow2MontgomeryAccelerator
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gpu.Stream = gpu.Accelerator.CreateStream();
|
// gpu.Stream = gpu.Accelerator.CreateStream();
|
||||||
gpu.EnsureCapacity(PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity);
|
gpu.EnsureCapacity(PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,8 +35,8 @@ public sealed class Pow2MontgomeryAccelerator
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void Return(Pow2MontgomeryAccelerator gpu)
|
public static void Return(Pow2MontgomeryAccelerator gpu)
|
||||||
{
|
{
|
||||||
gpu.Stream!.Dispose();
|
// gpu.Stream!.Dispose();
|
||||||
gpu.Stream = null;
|
// gpu.Stream = null;
|
||||||
_pool!.Enqueue(gpu);
|
_pool!.Enqueue(gpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,9 +87,9 @@ public sealed class Pow2MontgomeryAccelerator
|
|||||||
|
|
||||||
public readonly Accelerator Accelerator;
|
public readonly Accelerator Accelerator;
|
||||||
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> Input;
|
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> Input;
|
||||||
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> Output;
|
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> OutputUlong;
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense> PrimeTestInput;
|
public MemoryBuffer1D<ulong, Stride1D.Dense> PrimeTestInput;
|
||||||
public MemoryBuffer1D<byte, Stride1D.Dense> PrimeTestOutput;
|
public MemoryBuffer1D<byte, Stride1D.Dense> OutputByte;
|
||||||
public MemoryBuffer1D<int, Stride1D.Dense> HeuristicFlag;
|
public MemoryBuffer1D<int, Stride1D.Dense> HeuristicFlag;
|
||||||
public MemoryBuffer1D<KeyValuePair<ulong, int>, Stride1D.Dense> Pow2ModEntriesToTestOnDevice;
|
public MemoryBuffer1D<KeyValuePair<ulong, int>, Stride1D.Dense> Pow2ModEntriesToTestOnDevice;
|
||||||
public Dictionary<ulong, int> Pow2ModEntriesToTestOnHost = [];
|
public Dictionary<ulong, int> Pow2ModEntriesToTestOnHost = [];
|
||||||
@@ -101,14 +101,14 @@ public sealed class Pow2MontgomeryAccelerator
|
|||||||
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastSeven;
|
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastSeven;
|
||||||
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastThree;
|
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastThree;
|
||||||
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastNine;
|
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastNine;
|
||||||
public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> SmallPrimeSieveKernel;
|
public readonly Kernel SmallPrimeSieveKernel;
|
||||||
|
|
||||||
public AcceleratorStream? Stream;
|
// public AcceleratorStream? Stream;
|
||||||
|
|
||||||
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>> ConvertToStandardKernel;
|
|
||||||
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>> KeepMontgomeryKernel;
|
|
||||||
|
|
||||||
public readonly Kernel CheckFactorsKernel;
|
public readonly Kernel CheckFactorsKernel;
|
||||||
|
public readonly Kernel ConvertToStandardKernel;
|
||||||
|
public readonly Kernel KeepMontgomeryKernel;
|
||||||
|
public readonly Kernel PollardRhoKernel;
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private 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>>>? SmallPrimeSieveKernelPool;
|
private 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>>>? SmallPrimeSieveKernelPool;
|
||||||
@@ -137,15 +137,14 @@ public sealed class Pow2MontgomeryAccelerator
|
|||||||
|
|
||||||
if (primeTesterCapacity > PrimeTestInput.Length)
|
if (primeTesterCapacity > PrimeTestInput.Length)
|
||||||
{
|
{
|
||||||
|
|
||||||
PrimeTestInput?.Dispose();
|
PrimeTestInput?.Dispose();
|
||||||
PrimeTestOutput?.Dispose();
|
OutputByte?.Dispose();
|
||||||
HeuristicFlag?.Dispose();
|
HeuristicFlag?.Dispose();
|
||||||
|
|
||||||
// lock (Accelerator)
|
// lock (Accelerator)
|
||||||
{
|
{
|
||||||
PrimeTestInput = Accelerator.Allocate1D<ulong>(primeTesterCapacity);
|
PrimeTestInput = Accelerator.Allocate1D<ulong>(primeTesterCapacity);
|
||||||
PrimeTestOutput = Accelerator.Allocate1D<byte>(primeTesterCapacity);
|
OutputByte = Accelerator.Allocate1D<byte>(primeTesterCapacity);
|
||||||
HeuristicFlag = Accelerator.Allocate1D<int>(primeTesterCapacity);
|
HeuristicFlag = Accelerator.Allocate1D<int>(primeTesterCapacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,14 +153,16 @@ public sealed class Pow2MontgomeryAccelerator
|
|||||||
public Pow2MontgomeryAccelerator(Accelerator accelerator, int factorsCount, int primeTesterCapacity)
|
public Pow2MontgomeryAccelerator(Accelerator accelerator, int factorsCount, int primeTesterCapacity)
|
||||||
{
|
{
|
||||||
Accelerator = accelerator;
|
Accelerator = accelerator;
|
||||||
AcceleratorStream stream = accelerator.CreateStream();
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(accelerator);
|
||||||
Stream = stream;
|
// AcceleratorStream stream = accelerator.DefaultStream;
|
||||||
|
// AcceleratorStream stream = accelerator.CreateStream();
|
||||||
|
// Stream = stream;
|
||||||
|
|
||||||
Input = accelerator.Allocate1D<ulong>(1);
|
Input = accelerator.Allocate1D<ulong>(1);
|
||||||
Output = accelerator.Allocate1D<ulong>(1);
|
OutputUlong = accelerator.Allocate1D<ulong>(1);
|
||||||
Pow2ModEntriesToTestOnDevice = accelerator.Allocate1D<KeyValuePair<ulong, int>>(factorsCount);
|
Pow2ModEntriesToTestOnDevice = accelerator.Allocate1D<KeyValuePair<ulong, int>>(factorsCount);
|
||||||
PrimeTestInput = Accelerator.Allocate1D<ulong>(primeTesterCapacity);
|
PrimeTestInput = Accelerator.Allocate1D<ulong>(primeTesterCapacity);
|
||||||
PrimeTestOutput = Accelerator.Allocate1D<byte>(primeTesterCapacity);
|
OutputByte = Accelerator.Allocate1D<byte>(primeTesterCapacity);
|
||||||
HeuristicFlag = Accelerator.Allocate1D<int>(primeTesterCapacity);
|
HeuristicFlag = Accelerator.Allocate1D<int>(primeTesterCapacity);
|
||||||
|
|
||||||
var sharedTables = LastDigitGpuTables.EnsureStaticTables(accelerator, stream);
|
var sharedTables = LastDigitGpuTables.EnsureStaticTables(accelerator, stream);
|
||||||
@@ -174,30 +175,30 @@ public sealed class Pow2MontgomeryAccelerator
|
|||||||
DevicePrimesPow2LastThree = sharedTables.DevicePrimesPow2LastThree;
|
DevicePrimesPow2LastThree = sharedTables.DevicePrimesPow2LastThree;
|
||||||
DevicePrimesPow2LastNine = sharedTables.DevicePrimesPow2LastNine;
|
DevicePrimesPow2LastNine = sharedTables.DevicePrimesPow2LastNine;
|
||||||
|
|
||||||
var keepKernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelKeepMontgomery);
|
|
||||||
var keepLauncher = KernelUtil.GetKernel(keepKernel);
|
|
||||||
|
|
||||||
KeepMontgomeryKernel = keepLauncher.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
|
|
||||||
|
|
||||||
var convertKernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelConvertToStandard);
|
|
||||||
var convertLauncher = KernelUtil.GetKernel(convertKernel);
|
|
||||||
|
|
||||||
ConvertToStandardKernel = convertLauncher.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
|
|
||||||
|
|
||||||
var checkFactorsKernel = accelerator.LoadStreamKernel<int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderGpuHeuristics.CheckFactorsKernel);
|
var checkFactorsKernel = accelerator.LoadStreamKernel<int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderGpuHeuristics.CheckFactorsKernel);
|
||||||
CheckFactorsKernel = KernelUtil.GetKernel(checkFactorsKernel);
|
CheckFactorsKernel = KernelUtil.GetKernel(checkFactorsKernel);
|
||||||
|
|
||||||
SmallPrimeSieveKernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>();
|
var convertKernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelConvertToStandard);
|
||||||
|
ConvertToStandardKernel = KernelUtil.GetKernel(convertKernel);
|
||||||
|
|
||||||
|
var keepKernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelKeepMontgomery);
|
||||||
|
KeepMontgomeryKernel = KernelUtil.GetKernel(keepKernel);
|
||||||
|
|
||||||
|
SmallPrimeSieveKernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel));
|
||||||
|
|
||||||
|
PollardRhoKernel = KernelUtil.GetKernel(accelerator.LoadStreamKernel<ulong, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.TryPollardRhoKernel));
|
||||||
|
|
||||||
|
AcceleratorStreamPool.Return(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Input.Dispose();
|
Input.Dispose();
|
||||||
Output.Dispose();
|
OutputUlong.Dispose();
|
||||||
Stream?.Dispose();
|
// Stream?.Dispose();
|
||||||
Pow2ModEntriesToTestOnDevice.Dispose();
|
Pow2ModEntriesToTestOnDevice.Dispose();
|
||||||
PrimeTestInput?.Dispose();
|
PrimeTestInput?.Dispose();
|
||||||
PrimeTestOutput?.Dispose();
|
OutputByte?.Dispose();
|
||||||
HeuristicFlag?.Dispose();
|
HeuristicFlag?.Dispose();
|
||||||
|
|
||||||
// Stream is disposed upon return to the queue, so we don't need to dispose it here.
|
// Stream is disposed upon return to the queue, so we don't need to dispose it here.
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using ILGPU;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
|
public static class ThreadStaticDeterministicRandomGpu
|
||||||
|
{
|
||||||
|
[ThreadStatic]
|
||||||
|
private static DeterministicRandomGpu? _exclusive;
|
||||||
|
internal static DeterministicRandomGpu Exclusive => _exclusive ?? new DeterministicRandomGpu();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal struct DeterministicRandomGpu
|
||||||
|
{
|
||||||
|
private static long s_seedCounter;
|
||||||
|
|
||||||
|
private ulong _state;
|
||||||
|
public readonly ulong State
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeterministicRandomGpu()
|
||||||
|
{
|
||||||
|
_state = CreateThreadState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeterministicRandomGpu(ulong initialState)
|
||||||
|
{
|
||||||
|
_state = initialState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong NextUInt64()
|
||||||
|
{
|
||||||
|
ulong state = _state;
|
||||||
|
state ^= state >> 12;
|
||||||
|
state ^= state << 25;
|
||||||
|
state ^= state >> 27;
|
||||||
|
_state = state;
|
||||||
|
return state * 2685821657736338717UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GpuUInt128 NextUInt128()
|
||||||
|
{
|
||||||
|
GpuUInt128 high = (GpuUInt128)NextUInt64();
|
||||||
|
ReadOnlyGpuUInt128 low = (ReadOnlyGpuUInt128)NextUInt64();
|
||||||
|
high.ShiftLeft(64);
|
||||||
|
high.Add(low);
|
||||||
|
return high;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetState(ulong state)
|
||||||
|
{
|
||||||
|
_state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ulong CreateThreadState()
|
||||||
|
{
|
||||||
|
ulong seed = (ulong)Atomic.Add(ref s_seedCounter, 1);
|
||||||
|
seed += 0x9E3779B97F4A7C15UL;
|
||||||
|
seed = (seed ^ (seed >> 30)) * 0xBF58476D1CE4E5B9UL;
|
||||||
|
seed = (seed ^ (seed >> 27)) * 0x94D049BB133111EBUL;
|
||||||
|
seed ^= seed >> 31;
|
||||||
|
if (seed == 0UL)
|
||||||
|
{
|
||||||
|
seed = 0x9E3779B97F4A7C15UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -261,13 +261,13 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
|
|||||||
public override readonly int GetHashCode() => HashCode.Combine(High, Low);
|
public override readonly int GetHashCode() => HashCode.Combine(High, Low);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Add(in ReadOnlyGpuUInt128 other) => AddInternal(other.High, other.Low);
|
public void Add(in ReadOnlyGpuUInt128 other) => Add(other.High, other.Low);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Add(GpuUInt128 other) => AddInternal(other.High, other.Low);
|
public void Add(GpuUInt128 other) => Add(other.High, other.Low);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void AddInternal(ulong otherHigh, ulong otherLow)
|
public void Add(ulong otherHigh, ulong otherLow)
|
||||||
{
|
{
|
||||||
ulong originalLow = Low;
|
ulong originalLow = Low;
|
||||||
// We're reusing originalLow for carry to avoid an extra local.
|
// We're reusing originalLow for carry to avoid an extra local.
|
||||||
@@ -316,7 +316,7 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AddInternal(valueHigh, valueLow);
|
Add(valueHigh, valueLow);
|
||||||
if (CompareTo(in modulus) >= 0)
|
if (CompareTo(in modulus) >= 0)
|
||||||
{
|
{
|
||||||
Sub(in modulus);
|
Sub(in modulus);
|
||||||
@@ -589,9 +589,6 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static GpuUInt128 BinaryGcd(GpuUInt128 u, GpuUInt128 v)
|
public static GpuUInt128 BinaryGcd(GpuUInt128 u, GpuUInt128 v)
|
||||||
{
|
{
|
||||||
// TODO: Replace this scalar binary GCD with the branchless reduction from
|
|
||||||
// GpuUInt128BinaryGcdBenchmarks so CPU fallbacks stay aligned with the GPU kernel
|
|
||||||
// performance when resolving large divisor residues.
|
|
||||||
if (u.IsZero)
|
if (u.IsZero)
|
||||||
{
|
{
|
||||||
return v;
|
return v;
|
||||||
@@ -602,26 +599,50 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
|
|||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
int shift = TrailingZeroCount(new GpuUInt128(u.High | v.High, u.Low | v.Low));
|
ulong combinedLow = u.Low | v.Low;
|
||||||
|
int shift;
|
||||||
|
if (combinedLow != 0UL)
|
||||||
|
{
|
||||||
|
shift = BitOperations.TrailingZeroCount(combinedLow);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ulong combinedHigh = u.High | v.High;
|
||||||
|
shift = 64 + BitOperations.TrailingZeroCount(combinedHigh);
|
||||||
|
}
|
||||||
|
|
||||||
int zu = TrailingZeroCount(u);
|
int zu = TrailingZeroCount(u);
|
||||||
u >>= zu;
|
u >>= zu;
|
||||||
|
|
||||||
do
|
GpuUInt128 currentV = v;
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
int zv = TrailingZeroCount(v);
|
int zv = TrailingZeroCount(currentV);
|
||||||
v >>= zv;
|
currentV >>= zv;
|
||||||
if (u > v)
|
|
||||||
|
bool swap = u > currentV;
|
||||||
|
GpuUInt128 min = ConditionalSelect(u, currentV, swap);
|
||||||
|
GpuUInt128 max = ConditionalSelect(currentV, u, swap);
|
||||||
|
max.Sub(min);
|
||||||
|
|
||||||
|
if (max.IsZero)
|
||||||
{
|
{
|
||||||
(u, v) = (v, u);
|
return min << shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
v -= u;
|
u = min;
|
||||||
|
currentV = max;
|
||||||
}
|
}
|
||||||
while (!v.IsZero);
|
|
||||||
|
|
||||||
return u << shift;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static GpuUInt128 ConditionalSelect(in GpuUInt128 left, in GpuUInt128 right, bool useRight)
|
||||||
|
{
|
||||||
|
ulong mask = useRight ? ulong.MaxValue : 0UL;
|
||||||
|
return new GpuUInt128(
|
||||||
|
left.High ^ ((left.High ^ right.High) & mask),
|
||||||
|
left.Low ^ ((left.Low ^ right.Low) & mask));
|
||||||
|
}
|
||||||
|
|
||||||
public void Sub(ulong value)
|
public void Sub(ulong value)
|
||||||
{
|
{
|
||||||
@@ -1529,6 +1550,15 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
|
|||||||
high = hi;
|
high = hi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Pow2()
|
||||||
|
{
|
||||||
|
ulong operand = Low;
|
||||||
|
Low = operand * operand;
|
||||||
|
ulong highProduct = operand * High;
|
||||||
|
High = highProduct + MulHigh(operand, operand);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Check if the TODO below is still relevant.
|
// TODO: Check if the TODO below is still relevant.
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
@@ -5,13 +6,21 @@ namespace PerfectNumbers.Core.Gpu.Kernels;
|
|||||||
|
|
||||||
internal static class Pow2MontgomeryKernels
|
internal static class Pow2MontgomeryKernels
|
||||||
{
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static GpuUInt128 AdvancePolynomialGpu(GpuUInt128 x, ulong c, ulong modulus)
|
||||||
|
{
|
||||||
|
x.Pow2();
|
||||||
|
x.AddMod(c, modulus);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
public static void Pow2MontgomeryKernelKeepMontgomery(
|
public static void Pow2MontgomeryKernelKeepMontgomery(
|
||||||
Index1D index,
|
Index1D index,
|
||||||
ArrayView1D<ulong, Stride1D.Dense> exponents,
|
ArrayView1D<ulong, Stride1D.Dense> exponents,
|
||||||
MontgomeryDivisorData divisor,
|
MontgomeryDivisorData divisor,
|
||||||
ArrayView1D<ulong, Stride1D.Dense> results)
|
ArrayView1D<ulong, Stride1D.Dense> results)
|
||||||
{
|
{
|
||||||
results[index] = ULongExtensions.Pow2MontgomeryModWindowedGpuKeepMontgomery(divisor, exponents[index]);
|
results[0] = ULongExtensions.Pow2MontgomeryModWindowedGpuKeepMontgomery(divisor, exponents[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Pow2MontgomeryKernelConvertToStandard(
|
public static void Pow2MontgomeryKernelConvertToStandard(
|
||||||
@@ -20,6 +29,52 @@ internal static class Pow2MontgomeryKernels
|
|||||||
MontgomeryDivisorData divisor,
|
MontgomeryDivisorData divisor,
|
||||||
ArrayView1D<ulong, Stride1D.Dense> results)
|
ArrayView1D<ulong, Stride1D.Dense> results)
|
||||||
{
|
{
|
||||||
results[index] = ULongExtensions.Pow2MontgomeryModWindowedGpuConvertToStandard(divisor, exponents[index]);
|
results[0] = ULongExtensions.Pow2MontgomeryModWindowedGpuConvertToStandard(divisor, exponents[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void TryPollardRhoKernel(ulong n, int limit, ArrayView1D<ulong, Stride1D.Dense> randomState, ArrayView1D<byte, Stride1D.Dense> factored, ArrayView1D<ulong, Stride1D.Dense> factor)
|
||||||
|
{
|
||||||
|
factored[0] = 0;
|
||||||
|
if ((n & 1UL) == 0UL)
|
||||||
|
{
|
||||||
|
factor[0] = 2UL;
|
||||||
|
factored[0] = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int attempt = 0;
|
||||||
|
GpuUInt128 nGpuUInt128 = (GpuUInt128)n;
|
||||||
|
DeterministicRandomGpu random = new(randomState[0]);
|
||||||
|
while (attempt < limit)
|
||||||
|
{
|
||||||
|
ulong c = (random.NextUInt64() % (n - 1UL)) + 1UL;
|
||||||
|
GpuUInt128 x = (random.NextUInt64() % (n - 2UL)) + 2UL;
|
||||||
|
GpuUInt128 y = x;
|
||||||
|
GpuUInt128 d = GpuUInt128.One;
|
||||||
|
|
||||||
|
while (d.CompareTo(GpuUInt128.One) == 0)
|
||||||
|
{
|
||||||
|
x = AdvancePolynomialGpu(x, c, modulus: n);
|
||||||
|
y = AdvancePolynomialGpu(y, c, n);
|
||||||
|
y = AdvancePolynomialGpu(y, c, n);
|
||||||
|
GpuUInt128 diff = x.CompareTo(y) > 0 ? x - y : y - x;
|
||||||
|
d = GpuUInt128.BinaryGcd(diff, nGpuUInt128);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d != n)
|
||||||
|
{
|
||||||
|
if (d.High == 0)
|
||||||
|
{
|
||||||
|
factor[0] = d.Low;
|
||||||
|
factored[0] = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attempt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
randomState[0] = random.State;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ using System.Numerics;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
|
using PerfectNumbers.Core.Cpu;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
@@ -1410,5 +1411,4 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
ulong directMask = (1UL << windowBitCount) - 1UL;
|
ulong directMask = (1UL << windowBitCount) - 1UL;
|
||||||
return exponent.Low & directMask;
|
return exponent.Low & directMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -147,13 +147,11 @@ public class MersenneNumberLucasLehmerGpuTester
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
|
||||||
bool result;
|
bool result;
|
||||||
if (exponent >= 128UL)
|
if (exponent >= 128UL)
|
||||||
{
|
{
|
||||||
if (!TryGetNttParameters(exponent, out var nttMod, out var primitiveRoot))
|
if (!TryGetNttParameters(exponent, out var nttMod, out var primitiveRoot))
|
||||||
{
|
{
|
||||||
GpuPrimeWorkLimiter.Release();
|
|
||||||
throw new NotSupportedException("NTT parameters for the given exponent are not available.");
|
throw new NotSupportedException("NTT parameters for the given exponent are not available.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,6 +159,7 @@ public class MersenneNumberLucasLehmerGpuTester
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
var accelerator = AcceleratorPool.Shared.Rent();
|
var accelerator = AcceleratorPool.Shared.Rent();
|
||||||
var stream = accelerator.CreateStream();
|
var stream = accelerator.CreateStream();
|
||||||
var modulus = new GpuUInt128(((UInt128)1 << (int)exponent) - 1UL); // TODO: Cache these Mersenne moduli per exponent so LL GPU runs skip rebuilding them every launch.
|
var modulus = new GpuUInt128(((UInt128)1 << (int)exponent) - 1UL); // TODO: Cache these Mersenne moduli per exponent so LL GPU runs skip rebuilding them every launch.
|
||||||
|
|||||||
@@ -10,25 +10,28 @@ public static class Pow2MontgomeryGpuCalculator
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ulong CalculateKeep(ulong exponent, in MontgomeryDivisorData divisor)
|
public static ulong CalculateKeep(ulong exponent, in MontgomeryDivisorData divisor)
|
||||||
{
|
{
|
||||||
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
|
|
||||||
var gpu = Pow2MontgomeryAccelerator.Rent(1);
|
var gpu = Pow2MontgomeryAccelerator.Rent(1);
|
||||||
Accelerator? accelerator = gpu.Accelerator;
|
Accelerator? accelerator = gpu.Accelerator;
|
||||||
AcceleratorStream stream = gpu.Stream!;
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(accelerator);
|
||||||
|
// AcceleratorStream stream = gpu.Stream!;
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input;
|
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input;
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.Output;
|
|
||||||
|
|
||||||
exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1);
|
exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1);
|
||||||
// We don't need to worry about any left-overs here.
|
// We don't need to worry about any left-overs here.
|
||||||
// resultBuffer.MemSetToZero(stream);
|
// resultBuffer.MemSetToZero(stream);
|
||||||
|
|
||||||
|
MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.OutputUlong;
|
||||||
var kernel = gpu.KeepMontgomeryKernel;
|
var kernel = gpu.KeepMontgomeryKernel;
|
||||||
|
var kernelLauncher = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||||
|
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
kernelLauncher(stream, 1, exponentBuffer.View, divisor, resultBuffer.View);
|
||||||
kernel(stream, 1, exponentBuffer.View, divisor, resultBuffer.View);
|
|
||||||
|
|
||||||
ulong result = 0UL;
|
ulong result = 0UL;
|
||||||
resultBuffer.View.CopyToCPU(stream, ref result, 1);
|
resultBuffer.View.CopyToCPU(stream, ref result, 1);
|
||||||
stream.Synchronize();
|
stream.Synchronize();
|
||||||
|
AcceleratorStreamPool.Return(stream);
|
||||||
GpuPrimeWorkLimiter.Release();
|
GpuPrimeWorkLimiter.Release();
|
||||||
|
|
||||||
Pow2MontgomeryAccelerator.Return(gpu);
|
Pow2MontgomeryAccelerator.Return(gpu);
|
||||||
@@ -38,24 +41,26 @@ public static class Pow2MontgomeryGpuCalculator
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ulong CalculateConvert(ulong exponent, in MontgomeryDivisorData divisor)
|
public static ulong CalculateConvert(ulong exponent, in MontgomeryDivisorData divisor)
|
||||||
{
|
{
|
||||||
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
|
|
||||||
var gpu = Pow2MontgomeryAccelerator.Rent(1);
|
var gpu = Pow2MontgomeryAccelerator.Rent(1);
|
||||||
AcceleratorStream stream = gpu.Stream!;
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(gpu.Accelerator);
|
||||||
|
// AcceleratorStream stream = gpu.Stream!;
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input;
|
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input;
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.Output;
|
|
||||||
|
|
||||||
exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1);
|
exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1);
|
||||||
// We don't need to worry about any left-overs here.
|
// We don't need to worry about any left-overs here.
|
||||||
// resultBuffer.MemSetToZero(stream);
|
// resultBuffer.MemSetToZero(stream);
|
||||||
|
|
||||||
|
MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.OutputUlong;
|
||||||
var kernel = gpu.ConvertToStandardKernel;
|
var kernel = gpu.ConvertToStandardKernel;
|
||||||
|
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
kernel.Launch(stream, 1, 1, exponentBuffer.View, divisor, resultBuffer.View);
|
||||||
kernel(stream, 1, exponentBuffer.View, divisor, resultBuffer.View);
|
|
||||||
|
|
||||||
ulong result = 0UL;
|
ulong result = 0UL;
|
||||||
resultBuffer.View.CopyToCPU(stream, ref result, 1);
|
resultBuffer.View.CopyToCPU(stream, ref result, 1);
|
||||||
stream.Synchronize();
|
stream.Synchronize();
|
||||||
|
AcceleratorStreamPool.Return(stream);
|
||||||
GpuPrimeWorkLimiter.Release();
|
GpuPrimeWorkLimiter.Release();
|
||||||
|
|
||||||
Pow2MontgomeryAccelerator.Return(gpu);
|
Pow2MontgomeryAccelerator.Return(gpu);
|
||||||
@@ -65,20 +70,24 @@ public static class Pow2MontgomeryGpuCalculator
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ulong CalculateConvert(Pow2MontgomeryAccelerator gpu, ulong exponent, in MontgomeryDivisorData divisor)
|
public static ulong CalculateConvert(Pow2MontgomeryAccelerator gpu, ulong exponent, in MontgomeryDivisorData divisor)
|
||||||
{
|
{
|
||||||
AcceleratorStream stream = gpu.Stream!;
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(gpu.Accelerator);
|
||||||
|
// AcceleratorStream stream = gpu.Stream!;
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input;
|
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input;
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.Output;
|
|
||||||
|
|
||||||
exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1);
|
exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1);
|
||||||
// We don't need to worry about any left-overs here.
|
// We don't need to worry about any left-overs here.
|
||||||
// resultBuffer.MemSetToZero(stream);
|
// resultBuffer.MemSetToZero(stream);
|
||||||
|
|
||||||
|
MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.OutputUlong;
|
||||||
var kernel = gpu.ConvertToStandardKernel;
|
var kernel = gpu.ConvertToStandardKernel;
|
||||||
kernel(stream, 1, exponentBuffer.View, divisor, resultBuffer.View);
|
var kernelLauncher = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||||
|
|
||||||
|
kernelLauncher(stream, 1,exponentBuffer.View, divisor, resultBuffer.View);
|
||||||
|
|
||||||
ulong result = 0UL;
|
ulong result = 0UL;
|
||||||
resultBuffer.View.CopyToCPU(stream, ref result, 1);
|
resultBuffer.View.CopyToCPU(stream, ref result, 1);
|
||||||
stream.Synchronize();
|
stream.Synchronize();
|
||||||
|
AcceleratorStreamPool.Return(stream);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -224,6 +224,7 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
|
|
||||||
private static bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10)
|
private static bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10)
|
||||||
{
|
{
|
||||||
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
var gpu = HeuristicPrimeTesterAccelerator.Rent(1);
|
var gpu = HeuristicPrimeTesterAccelerator.Rent(1);
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = gpu.Accelerator;
|
||||||
var stream = gpu.Stream;
|
var stream = gpu.Stream;
|
||||||
@@ -238,7 +239,6 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
flagView1D.CopyFromCPU(stream, ref compositeFlag, 1);
|
flagView1D.CopyFromCPU(stream, ref compositeFlag, 1);
|
||||||
stream!.Synchronize();
|
stream!.Synchronize();
|
||||||
|
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
|
||||||
|
|
||||||
kernel(
|
kernel(
|
||||||
stream,
|
stream,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Numerics;
|
|||||||
using UInt128 = System.UInt128;
|
using UInt128 = System.UInt128;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
|
using PerfectNumbers.Core.Cpu;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core;
|
namespace PerfectNumbers.Core;
|
||||||
|
|
||||||
@@ -362,8 +363,8 @@ public static class MersennePrimeFactorTester
|
|||||||
return 0UL;
|
return 0UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL;
|
c = (DeterministicRandomCpu.NextUInt64() % (n - 1UL)) + 1UL;
|
||||||
x = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL;
|
x = (DeterministicRandomCpu.NextUInt64() % (n - 2UL)) + 2UL;
|
||||||
y = x;
|
y = x;
|
||||||
d = 1UL;
|
d = 1UL;
|
||||||
|
|
||||||
@@ -505,8 +506,8 @@ public static class MersennePrimeFactorTester
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
c = (DeterministicRandom.NextUInt128() % (n - 1)) + 1;
|
c = (DeterministicRandomCpu.NextUInt128() % (n - 1)) + 1;
|
||||||
x = (DeterministicRandom.NextUInt128() % (n - 2)) + 2;
|
x = (DeterministicRandomCpu.NextUInt128() % (n - 2)) + 2;
|
||||||
y = x;
|
y = x;
|
||||||
d = 1;
|
d = 1;
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ public static class PerfectNumberConstants
|
|||||||
public static readonly uint PrimesLimit = 1_000_000; //(ulong)Array.MaxLength;// 1_000_000;
|
public static readonly uint PrimesLimit = 1_000_000; //(ulong)Array.MaxLength;// 1_000_000;
|
||||||
public const int MaxOddPowersCount = 128;
|
public const int MaxOddPowersCount = 128;
|
||||||
public const int PooledArrayThreshold = 64;
|
public const int PooledArrayThreshold = 64;
|
||||||
public static readonly int RollingAccelerators = 128; //SharedGpuContext.Device.MaxNumThreadsPerGroup;
|
public static readonly int RollingAccelerators = 256; //SharedGpuContext.Device.MaxNumThreadsPerGroup;
|
||||||
|
public const int ThreadsByStream = 1;
|
||||||
// TODO: Load these limits from the benchmark-driven configuration so CPU and GPU scans stay aligned with the optimal
|
// 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.
|
// divisor-cycle datasets we generate offline.
|
||||||
// TODO: Promote these magic numbers into a runtime profile derived from EvenPerfectBitScanner.Benchmarks so we can retune
|
// TODO: Promote these magic numbers into a runtime profile derived from EvenPerfectBitScanner.Benchmarks so we can retune
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Diagnostics;
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using PerfectNumbers.Core.Cpu;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
@@ -1162,7 +1163,8 @@ internal static partial class PrimeOrderCalculator
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryPollardRho(composite, deadlineTimestamp, out ulong factor))
|
if (!TryPollardRhoGpu(gpu, composite, out ulong factor))
|
||||||
|
// if (!TryPollardRhoCpu(composite, deadlineTimestamp, out ulong factor))
|
||||||
{
|
{
|
||||||
pending.Add(new PendingEntry(composite, knownComposite: true));
|
pending.Add(new PendingEntry(composite, knownComposite: true));
|
||||||
continue;
|
continue;
|
||||||
@@ -1492,7 +1494,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return baseTicks + (remainder / 1000L);
|
return baseTicks + (remainder / 1000L);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryPollardRho(ulong n, long deadlineTimestamp, out ulong factor)
|
private static bool TryPollardRhoCpu(ulong n, long deadlineTimestamp, out ulong factor)
|
||||||
{
|
{
|
||||||
factor = 0UL;
|
factor = 0UL;
|
||||||
if ((n & 1UL) == 0UL)
|
if ((n & 1UL) == 0UL)
|
||||||
@@ -1510,8 +1512,8 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL;
|
ulong c = (DeterministicRandomCpu.NextUInt64() % (n - 1UL)) + 1UL;
|
||||||
ulong x = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL;
|
ulong x = (DeterministicRandomCpu.NextUInt64() % (n - 2UL)) + 2UL;
|
||||||
ulong y = x;
|
ulong y = x;
|
||||||
ulong d = 1UL;
|
ulong d = 1UL;
|
||||||
|
|
||||||
@@ -1523,9 +1525,9 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
x = AdvancePolynomial(x, c, n);
|
x = AdvancePolynomialCpu(x, c, n);
|
||||||
y = AdvancePolynomial(y, c, n);
|
y = AdvancePolynomialCpu(y, c, n);
|
||||||
y = AdvancePolynomial(y, c, n);
|
y = AdvancePolynomialCpu(y, c, n);
|
||||||
ulong diff = x > y ? x - y : y - x;
|
ulong diff = x > y ? x - y : y - x;
|
||||||
d = BinaryGcd(diff, n);
|
d = BinaryGcd(diff, n);
|
||||||
}
|
}
|
||||||
@@ -1539,7 +1541,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static ulong AdvancePolynomial(ulong x, ulong c, ulong modulus)
|
private static ulong AdvancePolynomialCpu(ulong x, ulong c, ulong modulus)
|
||||||
{
|
{
|
||||||
UInt128 value = (UInt128)x * x + c;
|
UInt128 value = (UInt128)x * x + c;
|
||||||
return (ulong)(value % modulus);
|
return (ulong)(value % modulus);
|
||||||
@@ -1810,16 +1812,16 @@ internal static partial class PrimeOrderCalculator
|
|||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
ulong c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL;
|
ulong c = (DeterministicRandomCpu.NextUInt64() % (n - 1UL)) + 1UL;
|
||||||
ulong x = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL;
|
ulong x = (DeterministicRandomCpu.NextUInt64() % (n - 2UL)) + 2UL;
|
||||||
ulong y = x;
|
ulong y = x;
|
||||||
ulong d = 1UL;
|
ulong d = 1UL;
|
||||||
|
|
||||||
while (d == 1UL)
|
while (d == 1UL)
|
||||||
{
|
{
|
||||||
x = AdvancePolynomial(x, c, n);
|
x = AdvancePolynomialCpu(x, c, n);
|
||||||
y = AdvancePolynomial(y, c, n);
|
y = AdvancePolynomialCpu(y, c, n);
|
||||||
y = AdvancePolynomial(y, c, n);
|
y = AdvancePolynomialCpu(y, c, n);
|
||||||
ulong diff = x > y ? x - y : y - x;
|
ulong diff = x > y ? x - y : y - x;
|
||||||
d = BinaryGcd(diff, n);
|
d = BinaryGcd(diff, n);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
@@ -37,19 +39,21 @@ internal static partial class PrimeOrderCalculator
|
|||||||
|
|
||||||
entries.Sort(entries, static (a, b) => a.Key.CompareTo(b.Key));
|
entries.Sort(entries, static (a, b) => a.Key.CompareTo(b.Key));
|
||||||
|
|
||||||
AcceleratorStream stream = gpu.Stream!;
|
// AcceleratorStream stream = gpu.Stream!;
|
||||||
gpu.EnsureCapacity(entryCount, 1);
|
gpu.EnsureCapacity(entryCount, 1);
|
||||||
|
|
||||||
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(gpu.Accelerator);
|
||||||
gpu.Pow2ModEntriesToTestOnDevice.View.CopyFromCPU(stream, entries);
|
gpu.Pow2ModEntriesToTestOnDevice.View.CopyFromCPU(stream, entries);
|
||||||
|
|
||||||
ulong order = phi;
|
ulong order = phi;
|
||||||
var kernel = gpu.CheckFactorsKernel;
|
var kernel = gpu.CheckFactorsKernel;
|
||||||
stream.Synchronize();
|
stream.Synchronize();
|
||||||
|
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
kernel.Launch(stream, 1, entryCount, phi, gpu.Pow2ModEntriesToTestOnDevice.View, divisorData, gpu.OutputUlong.View);
|
||||||
|
gpu.OutputUlong.View.CopyToCPU(stream, ref order, 1);
|
||||||
kernel.Launch(stream, 1, entryCount, phi, gpu.Pow2ModEntriesToTestOnDevice.View, divisorData, gpu.Output.View);
|
|
||||||
gpu.Output.View.CopyToCPU(stream, ref order, 1);
|
|
||||||
stream.Synchronize();
|
stream.Synchronize();
|
||||||
|
AcceleratorStreamPool.Return(stream);
|
||||||
|
|
||||||
GpuPrimeWorkLimiter.Release();
|
GpuPrimeWorkLimiter.Release();
|
||||||
return order;
|
return order;
|
||||||
@@ -186,5 +190,34 @@ internal static partial class PrimeOrderCalculator
|
|||||||
GpuPrimeWorkLimiter.Release();
|
GpuPrimeWorkLimiter.Release();
|
||||||
return result != 0;
|
return result != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryPollardRhoGpu(Pow2MontgomeryAccelerator gpu, ulong n, out ulong factor)
|
||||||
|
{
|
||||||
|
// var stream = gpu.Stream!;
|
||||||
|
Span<ulong> randomStateSpan = stackalloc ulong[1];
|
||||||
|
randomStateSpan[0] = ThreadStaticDeterministicRandomGpu.Exclusive.State;
|
||||||
|
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(gpu.Accelerator);
|
||||||
|
gpu.Input.View.CopyFromCPU(stream, randomStateSpan);
|
||||||
|
|
||||||
|
Span<byte> factoredSpan = stackalloc byte[1];
|
||||||
|
Span<ulong> factorSpan = stackalloc ulong[1];
|
||||||
|
var kernelLauncher = gpu.PollardRhoKernel.CreateLauncherDelegate<Action<AcceleratorStream, ulong, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||||
|
|
||||||
|
kernelLauncher(stream, n, 1, gpu.Input.View, gpu.OutputByte.View, gpu.OutputUlong.View);
|
||||||
|
|
||||||
|
gpu.OutputByte.View.CopyToCPU(stream, factoredSpan);
|
||||||
|
gpu.OutputUlong.View.CopyToCPU(stream, factorSpan);
|
||||||
|
gpu.Input.View.CopyToCPU(stream, randomStateSpan);
|
||||||
|
stream.Synchronize();
|
||||||
|
|
||||||
|
AcceleratorStreamPool.Return(stream);
|
||||||
|
ThreadStaticDeterministicRandomGpu.Exclusive.SetState(randomStateSpan[0]);
|
||||||
|
|
||||||
|
bool factored = factoredSpan[0] != 0;
|
||||||
|
factor = factored ? factorSpan[0] : 0UL;
|
||||||
|
return factored;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,23 +93,21 @@ public sealed class PrimeTester
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool IsPrimeGpu(ulong n)
|
public static bool IsPrimeGpu(ulong n)
|
||||||
{
|
{
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
|
||||||
var gpu = Pow2MontgomeryAccelerator.Rent(1);
|
var gpu = Pow2MontgomeryAccelerator.Rent(1);
|
||||||
var accelerator = gpu.Accelerator;
|
var kernel = gpu.SmallPrimeSieveKernel!;
|
||||||
var stream = gpu.Stream!;
|
|
||||||
var kernel = gpu.SmallPrimeSieveKernel;
|
|
||||||
|
|
||||||
ulong value = n;
|
|
||||||
// Span<byte> flag = stackalloc byte[1];
|
// Span<byte> flag = stackalloc byte[1];
|
||||||
byte flag = 0;
|
|
||||||
|
|
||||||
var input = gpu.PrimeTestInput;
|
var inputView = gpu.PrimeTestInput.View;
|
||||||
var output = gpu.PrimeTestOutput;
|
|
||||||
var inputView = input.View;
|
|
||||||
var outputView = output.View;
|
|
||||||
|
|
||||||
inputView.CopyFromCPU(stream, ref value, 1);
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
kernel(
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(gpu.Accelerator);
|
||||||
|
// var stream = gpu.Stream!;
|
||||||
|
inputView.CopyFromCPU(stream, ref n, 1);
|
||||||
|
|
||||||
|
var outputView = gpu.OutputByte.View;
|
||||||
|
|
||||||
|
kernel.Launch(
|
||||||
stream,
|
stream,
|
||||||
1,
|
1,
|
||||||
inputView,
|
inputView,
|
||||||
@@ -122,9 +120,12 @@ public sealed class PrimeTester
|
|||||||
gpu.DevicePrimesPow2LastThree.View,
|
gpu.DevicePrimesPow2LastThree.View,
|
||||||
gpu.DevicePrimesPow2LastNine.View,
|
gpu.DevicePrimesPow2LastNine.View,
|
||||||
outputView);
|
outputView);
|
||||||
|
|
||||||
|
byte flag = 0;
|
||||||
outputView.CopyToCPU(stream, ref flag, 1);
|
outputView.CopyToCPU(stream, ref flag, 1);
|
||||||
stream.Synchronize();
|
stream.Synchronize();
|
||||||
|
|
||||||
|
AcceleratorStreamPool.Return(stream);
|
||||||
// gpu.Dispose();
|
// gpu.Dispose();
|
||||||
Pow2MontgomeryAccelerator.Return(gpu);
|
Pow2MontgomeryAccelerator.Return(gpu);
|
||||||
GpuPrimeWorkLimiter.Release();
|
GpuPrimeWorkLimiter.Release();
|
||||||
@@ -135,21 +136,21 @@ public sealed class PrimeTester
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool IsPrimeGpu(Pow2MontgomeryAccelerator gpu, ulong n)
|
public static bool IsPrimeGpu(Pow2MontgomeryAccelerator gpu, ulong n)
|
||||||
{
|
{
|
||||||
var accelerator = gpu.Accelerator;
|
// var stream = gpu.Stream!;
|
||||||
var stream = gpu.Stream!;
|
|
||||||
Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>? kernel = gpu.SmallPrimeSieveKernel;
|
|
||||||
|
|
||||||
ulong value = n;
|
|
||||||
// Span<byte> flag = stackalloc byte[1];
|
|
||||||
byte flag = 0;
|
|
||||||
|
|
||||||
var input = gpu.PrimeTestInput;
|
var input = gpu.PrimeTestInput;
|
||||||
var output = gpu.PrimeTestOutput;
|
|
||||||
var inputView = input.View;
|
var inputView = input.View;
|
||||||
|
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(gpu.Accelerator);
|
||||||
|
inputView.CopyFromCPU(stream, ref n, 1);
|
||||||
|
|
||||||
|
// Span<byte> flag = stackalloc byte[1];
|
||||||
|
var output = gpu.OutputByte;
|
||||||
var outputView = output.View;
|
var outputView = output.View;
|
||||||
|
|
||||||
inputView.CopyFromCPU(stream, ref value, 1);
|
Kernel kernel = gpu.SmallPrimeSieveKernel!;
|
||||||
kernel(
|
var kernelLauncher = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>();
|
||||||
|
|
||||||
|
kernelLauncher(
|
||||||
stream,
|
stream,
|
||||||
1,
|
1,
|
||||||
inputView,
|
inputView,
|
||||||
@@ -162,8 +163,11 @@ public sealed class PrimeTester
|
|||||||
gpu.DevicePrimesPow2LastThree.View,
|
gpu.DevicePrimesPow2LastThree.View,
|
||||||
gpu.DevicePrimesPow2LastNine.View,
|
gpu.DevicePrimesPow2LastNine.View,
|
||||||
outputView);
|
outputView);
|
||||||
|
|
||||||
|
byte flag = 0;
|
||||||
outputView.CopyToCPU(stream, ref flag, 1);
|
outputView.CopyToCPU(stream, ref flag, 1);
|
||||||
stream.Synchronize();
|
stream.Synchronize();
|
||||||
|
AcceleratorStreamPool.Return(stream);
|
||||||
|
|
||||||
return flag != 0;
|
return flag != 0;
|
||||||
}
|
}
|
||||||
@@ -199,27 +203,27 @@ public sealed class PrimeTester
|
|||||||
GpuPrimeWorkLimiter.Acquire();
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
var gpu = Pow2MontgomeryAccelerator.Rent(GpuBatchSize);
|
var gpu = Pow2MontgomeryAccelerator.Rent(GpuBatchSize);
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = gpu.Accelerator;
|
||||||
var stream = gpu.Stream!;
|
// var stream = gpu.Stream!;
|
||||||
var kernel = gpu.SmallPrimeSieveKernel;
|
var kernel = gpu.SmallPrimeSieveKernel!;
|
||||||
int totalLength = values.Length;
|
int totalLength = values.Length;
|
||||||
int batchSize = GpuBatchSize;
|
int batchSize = GpuBatchSize;
|
||||||
|
|
||||||
var input = gpu.PrimeTestInput;
|
var input = gpu.PrimeTestInput;
|
||||||
var output = gpu.PrimeTestOutput;
|
var output = gpu.OutputByte;
|
||||||
var inputView = input.View;
|
var inputView = input.View;
|
||||||
var outputView = output.View;
|
var outputView = output.View;
|
||||||
|
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(accelerator);
|
||||||
while (pos < totalLength)
|
while (pos < totalLength)
|
||||||
{
|
{
|
||||||
int remaining = totalLength - pos;
|
int remaining = totalLength - pos;
|
||||||
int count = remaining > batchSize ? batchSize : remaining;
|
int count = remaining > batchSize ? batchSize : remaining;
|
||||||
|
|
||||||
var valueSlice = values.Slice(pos, count);
|
var valueSlice = values.Slice(pos, count);
|
||||||
ref ulong valueRef = ref MemoryMarshal.GetReference(valueSlice);
|
inputView.CopyFromCPU(stream, valueSlice);
|
||||||
inputView.CopyFromCPU(stream, ref valueRef, count);
|
|
||||||
|
|
||||||
kernel(
|
kernel.Launch(
|
||||||
stream,
|
stream,
|
||||||
count,
|
count,
|
||||||
inputView,
|
inputView,
|
||||||
@@ -241,6 +245,7 @@ public sealed class PrimeTester
|
|||||||
}
|
}
|
||||||
|
|
||||||
stream.Synchronize();
|
stream.Synchronize();
|
||||||
|
AcceleratorStreamPool.Return(stream);
|
||||||
Pow2MontgomeryAccelerator.Return(gpu);
|
Pow2MontgomeryAccelerator.Return(gpu);
|
||||||
// gpu.Dispose();
|
// gpu.Dispose();
|
||||||
GpuPrimeWorkLimiter.Release();
|
GpuPrimeWorkLimiter.Release();
|
||||||
@@ -281,7 +286,7 @@ public sealed class PrimeTester
|
|||||||
// and divisor-cycle staging instead of allocating new device buffers per call.
|
// and divisor-cycle staging instead of allocating new device buffers per call.
|
||||||
var gpu = Pow2MontgomeryAccelerator.Rent(1);
|
var gpu = Pow2MontgomeryAccelerator.Rent(1);
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = gpu.Accelerator;
|
||||||
var stream = gpu.Stream!;
|
// var stream = gpu.Stream!;
|
||||||
|
|
||||||
int length = values.Length;
|
int length = values.Length;
|
||||||
ArrayPool<ulong> pool = ThreadStaticPools.UlongPool;
|
ArrayPool<ulong> pool = ThreadStaticPools.UlongPool;
|
||||||
@@ -297,11 +302,14 @@ public sealed class PrimeTester
|
|||||||
resultBuffer = accelerator.Allocate1D<byte>(length);
|
resultBuffer = accelerator.Allocate1D<byte>(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(accelerator);
|
||||||
inputBuffer.View.CopyFromCPU(stream, ref temp[0], length);
|
inputBuffer.View.CopyFromCPU(stream, ref temp[0], length);
|
||||||
var kernel = GetSharesFactorKernel(accelerator);
|
var kernel = GetSharesFactorKernel(accelerator);
|
||||||
kernel(stream, length, inputBuffer.View, resultBuffer.View);
|
kernel(stream, length, inputBuffer.View, resultBuffer.View);
|
||||||
resultBuffer.View.CopyToCPU(stream, in results);
|
resultBuffer.View.CopyToCPU(stream, in results);
|
||||||
stream.Synchronize();
|
stream.Synchronize();
|
||||||
|
|
||||||
|
AcceleratorStreamPool.Return(stream);
|
||||||
pool.Return(temp, clearArray: false);
|
pool.Return(temp, clearArray: false);
|
||||||
resultBuffer.Dispose();
|
resultBuffer.Dispose();
|
||||||
inputBuffer.Dispose();
|
inputBuffer.Dispose();
|
||||||
|
|||||||
Reference in new issue
Block a user