Revert "Merge pull request #251 from mlemanczyk/codex/refactor-prime-checking-on-gpu-n5t2pm"
This reverts commit936ce239db, reversing changes made toe14b77e504.
This commit is contained in:
1 parent
936ce239db
commit
2281d9535e
23 files changed
+880
-1187
No files matched your search
@@ -1,142 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
using ILGPU;
|
||||
using ILGPU.Runtime;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
internal static class DivisorGeneratorGpuCache
|
||||
{
|
||||
private sealed class AcceleratorReferenceComparer : IEqualityComparer<Accelerator>
|
||||
{
|
||||
public static AcceleratorReferenceComparer Instance { get; } = new();
|
||||
|
||||
public bool Equals(Accelerator? x, Accelerator? y)
|
||||
{
|
||||
return ReferenceEquals(x, y);
|
||||
}
|
||||
|
||||
public int GetHashCode(Accelerator obj)
|
||||
{
|
||||
return RuntimeHelpers.GetHashCode(obj);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class DivisorGeneratorGpuTables : IDisposable
|
||||
{
|
||||
internal DivisorGeneratorGpuTables(Accelerator accelerator)
|
||||
{
|
||||
SmallPrimes = AllocateAndCopyUInt(accelerator, DivisorGenerator.SmallPrimes);
|
||||
SmallPrimesPow2 = AllocateAndCopyULong(accelerator, DivisorGenerator.SmallPrimesPow2);
|
||||
SmallPrimesLastOne = AllocateAndCopyUInt(accelerator, DivisorGenerator.SmallPrimesLastOne);
|
||||
SmallPrimesPow2LastOne = AllocateAndCopyULong(accelerator, DivisorGenerator.SmallPrimesPow2LastOne);
|
||||
SmallPrimesLastSeven = AllocateAndCopyUInt(accelerator, DivisorGenerator.SmallPrimesLastSeven);
|
||||
SmallPrimesPow2LastSeven = AllocateAndCopyULong(accelerator, DivisorGenerator.SmallPrimesPow2LastSeven);
|
||||
SmallPrimesLastThree = AllocateAndCopyUInt(accelerator, DivisorGenerator.SmallPrimesLastThree);
|
||||
SmallPrimesPow2LastThree = AllocateAndCopyULong(accelerator, DivisorGenerator.SmallPrimesPow2LastThree);
|
||||
SmallPrimesLastNine = AllocateAndCopyUInt(accelerator, DivisorGenerator.SmallPrimesLastNine);
|
||||
SmallPrimesPow2LastNine = AllocateAndCopyULong(accelerator, DivisorGenerator.SmallPrimesPow2LastNine);
|
||||
SmallPrimesLastOneWithoutLastThree = AllocateAndCopyUInt(accelerator, DivisorGenerator.SmallPrimesLastOneWithoutLastThree);
|
||||
SmallPrimesPow2LastOneWithoutLastThree = AllocateAndCopyULong(accelerator, DivisorGenerator.SmallPrimesPow2LastOneWithoutLastThree);
|
||||
SmallPrimesLastSevenWithoutLastThree = AllocateAndCopyUInt(accelerator, DivisorGenerator.SmallPrimesLastSevenWithoutLastThree);
|
||||
SmallPrimesPow2LastSevenWithoutLastThree = AllocateAndCopyULong(accelerator, DivisorGenerator.SmallPrimesPow2LastSevenWithoutLastThree);
|
||||
SmallPrimesLastNineWithoutLastThree = AllocateAndCopyUInt(accelerator, DivisorGenerator.SmallPrimesLastNineWithoutLastThree);
|
||||
SmallPrimesPow2LastNineWithoutLastThree = AllocateAndCopyULong(accelerator, DivisorGenerator.SmallPrimesPow2LastNineWithoutLastThree);
|
||||
}
|
||||
|
||||
internal MemoryBuffer1D<uint, Stride1D.Dense> SmallPrimes { get; }
|
||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimesPow2 { get; }
|
||||
internal MemoryBuffer1D<uint, Stride1D.Dense> SmallPrimesLastOne { get; }
|
||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimesPow2LastOne { get; }
|
||||
internal MemoryBuffer1D<uint, Stride1D.Dense> SmallPrimesLastSeven { get; }
|
||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimesPow2LastSeven { get; }
|
||||
internal MemoryBuffer1D<uint, Stride1D.Dense> SmallPrimesLastThree { get; }
|
||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimesPow2LastThree { get; }
|
||||
internal MemoryBuffer1D<uint, Stride1D.Dense> SmallPrimesLastNine { get; }
|
||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimesPow2LastNine { get; }
|
||||
internal MemoryBuffer1D<uint, Stride1D.Dense> SmallPrimesLastOneWithoutLastThree { get; }
|
||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimesPow2LastOneWithoutLastThree { get; }
|
||||
internal MemoryBuffer1D<uint, Stride1D.Dense> SmallPrimesLastSevenWithoutLastThree { get; }
|
||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimesPow2LastSevenWithoutLastThree { get; }
|
||||
internal MemoryBuffer1D<uint, Stride1D.Dense> SmallPrimesLastNineWithoutLastThree { get; }
|
||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> SmallPrimesPow2LastNineWithoutLastThree { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
SmallPrimes.Dispose();
|
||||
SmallPrimesPow2.Dispose();
|
||||
SmallPrimesLastOne.Dispose();
|
||||
SmallPrimesPow2LastOne.Dispose();
|
||||
SmallPrimesLastSeven.Dispose();
|
||||
SmallPrimesPow2LastSeven.Dispose();
|
||||
SmallPrimesLastThree.Dispose();
|
||||
SmallPrimesPow2LastThree.Dispose();
|
||||
SmallPrimesLastNine.Dispose();
|
||||
SmallPrimesPow2LastNine.Dispose();
|
||||
SmallPrimesLastOneWithoutLastThree.Dispose();
|
||||
SmallPrimesPow2LastOneWithoutLastThree.Dispose();
|
||||
SmallPrimesLastSevenWithoutLastThree.Dispose();
|
||||
SmallPrimesPow2LastSevenWithoutLastThree.Dispose();
|
||||
SmallPrimesLastNineWithoutLastThree.Dispose();
|
||||
SmallPrimesPow2LastNineWithoutLastThree.Dispose();
|
||||
}
|
||||
|
||||
private static MemoryBuffer1D<uint, Stride1D.Dense> AllocateAndCopyUInt(Accelerator accelerator, uint[] source)
|
||||
{
|
||||
var buffer = accelerator.Allocate1D<uint>(source.Length);
|
||||
if (source.Length > 0)
|
||||
{
|
||||
buffer.View.CopyFromCPU(source);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static MemoryBuffer1D<ulong, Stride1D.Dense> AllocateAndCopyULong(Accelerator accelerator, ulong[] source)
|
||||
{
|
||||
var buffer = accelerator.Allocate1D<ulong>(source.Length);
|
||||
if (source.Length > 0)
|
||||
{
|
||||
buffer.View.CopyFromCPU(source);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<Accelerator, DivisorGeneratorGpuTables> Cache = new(AcceleratorReferenceComparer.Instance);
|
||||
|
||||
public static void WarmUpByDivisorTables(Accelerator accelerator)
|
||||
{
|
||||
if (accelerator is null || accelerator.AcceleratorType == AcceleratorType.CPU)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Cache.GetOrAdd(accelerator, acc => new DivisorGeneratorGpuTables(acc));
|
||||
}
|
||||
|
||||
public static void Clear(Accelerator accelerator)
|
||||
{
|
||||
if (accelerator is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Cache.TryRemove(accelerator, out var tables))
|
||||
{
|
||||
tables.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static void DisposeAll()
|
||||
{
|
||||
foreach (var entry in Cache)
|
||||
{
|
||||
entry.Value.Dispose();
|
||||
}
|
||||
|
||||
Cache.Clear();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using ILGPU;
|
||||
using ILGPU.Runtime;
|
||||
@@ -7,19 +6,24 @@ namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
public static class GpuContextPool
|
||||
{
|
||||
// The pool intentionally skips pre-loading ProcessEightBitWindows kernels so accelerator initialization
|
||||
// stays stable; GpuKernelPool JIT-compiles them on first use alongside the small-cycle uploads.
|
||||
// Default device preference for generic GPU kernels (prime scans, NTT, etc.)
|
||||
internal sealed class PooledContext
|
||||
{
|
||||
public Context Context { get; }
|
||||
public Accelerator Accelerator { get; }
|
||||
private static readonly bool PoolingEnabled = true;
|
||||
// The pool intentionally skips pre-loading ProcessEightBitWindows kernels so accelerator initialization
|
||||
// stays stable; GpuKernelPool JIT-compiles them on first use alongside the small-cycle uploads.
|
||||
// Default device preference for generic GPU kernels (prime scans, NTT, etc.)
|
||||
public static bool ForceCpu { get; set; } = false;
|
||||
|
||||
public PooledContext()
|
||||
internal sealed class PooledContext
|
||||
{
|
||||
public Context Context { get; }
|
||||
public Accelerator Accelerator { get; }
|
||||
public bool IsCpu { get; }
|
||||
public object ExecutionLock { get; } = new();
|
||||
|
||||
public PooledContext(bool preferCpu)
|
||||
{
|
||||
Context = Context.CreateDefault();
|
||||
Accelerator = Context.GetPreferredDevice(false).CreateAccelerator(Context);
|
||||
DivisorGeneratorGpuCache.WarmUpByDivisorTables(Accelerator);
|
||||
Accelerator = Context.GetPreferredDevice(preferCpu).CreateAccelerator(Context);
|
||||
IsCpu = Accelerator.AcceleratorType == AcceleratorType.CPU;
|
||||
// NOTE: Avoid loading/compiling any kernel here to prevent implicit
|
||||
// CL stream/queue creation during accelerator construction.
|
||||
// Some OpenCL drivers are fragile when a queue is created immediately
|
||||
@@ -28,12 +32,12 @@ public static class GpuContextPool
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
// Ensure all cached GPU buffers for this accelerator are released.
|
||||
NttGpuMath.ClearCaches(Accelerator);
|
||||
// Release PrimeTester GPU state for this accelerator (kernel/device primes)
|
||||
// Clear any per-accelerator cached resources to avoid releasing
|
||||
// after the accelerator is destroyed.
|
||||
DivisorGeneratorGpuCache.Clear(Accelerator);
|
||||
PrimeTester.ClearGpuCaches(Accelerator);
|
||||
|
||||
Accelerator.Dispose();
|
||||
@@ -41,77 +45,96 @@ public static class GpuContextPool
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ConcurrentQueue<PooledContext> CpuPool = new();
|
||||
private static readonly ConcurrentQueue<PooledContext> GpuPool = new();
|
||||
private static int WarmedGpuContextCount;
|
||||
|
||||
// Allows callers to choose CPU/GPU per use-case, decoupled from ForceCpu.
|
||||
public static GpuContextLease Rent()
|
||||
{
|
||||
if (GpuPool.TryDequeue(out var gpu))
|
||||
return RentPreferred(ForceCpu);
|
||||
}
|
||||
|
||||
// Allows callers to choose CPU/GPU per use-case, decoupled from ForceCpu.
|
||||
public static GpuContextLease RentPreferred(bool preferCpu)
|
||||
{
|
||||
if (PoolingEnabled)
|
||||
{
|
||||
return new GpuContextLease(gpu);
|
||||
if (preferCpu && CpuPool.TryDequeue(out var cpu))
|
||||
{
|
||||
return new GpuContextLease(cpu);
|
||||
}
|
||||
|
||||
if (!preferCpu && GpuPool.TryDequeue(out var gpu))
|
||||
{
|
||||
return new GpuContextLease(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new accelerator when the pool does not have one available.
|
||||
return new GpuContextLease(new PooledContext());
|
||||
}
|
||||
|
||||
public static void WarmUpPool(int threadCount)
|
||||
{
|
||||
if (threadCount <= WarmedGpuContextCount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int toCreate = threadCount - WarmedGpuContextCount;
|
||||
var contexts = new PooledContext[toCreate];
|
||||
for (int i = 0; i < toCreate; i++)
|
||||
{
|
||||
contexts[i] = new PooledContext();
|
||||
}
|
||||
|
||||
for (int i = 0; i < toCreate; i++)
|
||||
{
|
||||
GpuPool.Enqueue(contexts[i]);
|
||||
}
|
||||
|
||||
WarmedGpuContextCount = threadCount;
|
||||
return new GpuContextLease(new PooledContext(preferCpu));
|
||||
}
|
||||
|
||||
public static void DisposeAll()
|
||||
{
|
||||
if (!PoolingEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (CpuPool.TryDequeue(out var cpu))
|
||||
{
|
||||
cpu.Dispose();
|
||||
}
|
||||
|
||||
while (GpuPool.TryDequeue(out var gpu))
|
||||
{
|
||||
gpu.Dispose();
|
||||
}
|
||||
|
||||
DivisorGeneratorGpuCache.DisposeAll();
|
||||
PrimeTester.DisposeGpuContexts();
|
||||
WarmedGpuContextCount = 0;
|
||||
}
|
||||
|
||||
private static void Return(PooledContext ctx)
|
||||
{
|
||||
ctx.Accelerator.Synchronize();
|
||||
GpuPool.Enqueue(ctx);
|
||||
}
|
||||
|
||||
public readonly struct GpuContextLease
|
||||
{
|
||||
private readonly PooledContext _ctx;
|
||||
|
||||
internal GpuContextLease(PooledContext ctx)
|
||||
if (PoolingEnabled)
|
||||
{
|
||||
_ctx = ctx;
|
||||
ctx.Accelerator.Synchronize();
|
||||
if (ctx.IsCpu)
|
||||
{
|
||||
CpuPool.Enqueue(ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
GpuPool.Enqueue(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly Context Context => _ctx.Context;
|
||||
public readonly Accelerator Accelerator => _ctx.Accelerator;
|
||||
else
|
||||
{
|
||||
ctx.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public struct GpuContextLease
|
||||
{
|
||||
private readonly PooledContext _ctx;
|
||||
|
||||
internal GpuContextLease(PooledContext ctx)
|
||||
{
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
public Context Context => _ctx.Context;
|
||||
|
||||
public Accelerator Accelerator => _ctx.Accelerator;
|
||||
|
||||
public object ExecutionLock => _ctx.ExecutionLock;
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
public readonly void Dispose()
|
||||
{
|
||||
Return(_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +280,7 @@ public class GpuKernelPool
|
||||
public static GpuKernelLease GetKernel(bool useGpuOrder)
|
||||
{
|
||||
var limiter = GpuPrimeWorkLimiter.Acquire();
|
||||
var gpu = Rent();
|
||||
var gpu = RentPreferred(preferCpu: !useGpuOrder);
|
||||
var accelerator = gpu.Accelerator;
|
||||
var kernels = GetKernels(accelerator);
|
||||
return GpuKernelLease.Rent(limiter, gpu, kernels);
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using ILGPU;
|
||||
using ILGPU.Runtime;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
internal readonly struct HeuristicGpuDivisorTables
|
||||
{
|
||||
public static readonly HeuristicGpuDivisorTables Shared = default;
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
internal static void InitializeShared(in HeuristicGpuDivisorTables tables)
|
||||
{
|
||||
Unsafe.AsRef(in Shared) = tables;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,9 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using ILGPU;
|
||||
using ILGPU.Algorithms;
|
||||
using ILGPU.Runtime;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
internal enum HeuristicGpuDivisorTableKind : byte
|
||||
{
|
||||
GroupA = 0,
|
||||
GroupBEnding1 = 1,
|
||||
GroupBEnding7 = 7,
|
||||
GroupBEnding9 = 9,
|
||||
Combined = 255,
|
||||
}
|
||||
|
||||
internal static class PrimeTesterKernels
|
||||
{
|
||||
// GPU kernel: small-prime sieve only. Returns 1 if passes sieve (probable prime), 0 otherwise.
|
||||
@@ -61,13 +51,13 @@ internal static class PrimeTesterKernels
|
||||
int length = (int)primes.Length;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
ulong prime = primes[i];
|
||||
ulong primeSquare = primeSquares[i];
|
||||
if (primeSquare > n)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ulong prime = primes[i];
|
||||
if (n % prime == 0UL)
|
||||
{
|
||||
result = 0;
|
||||
@@ -78,31 +68,19 @@ internal static class PrimeTesterKernels
|
||||
results[index] = result;
|
||||
}
|
||||
|
||||
public static void HeuristicTrialDivisionKernel(
|
||||
Index1D index,
|
||||
ArrayView<int> resultFlag,
|
||||
ulong n,
|
||||
ulong maxDivisorSquare,
|
||||
HeuristicGpuDivisorTableKind tableKind,
|
||||
HeuristicGpuDivisorTables tables)
|
||||
public static void HeuristicTrialDivisionKernel(Index1D index, ArrayView<ulong> divisors, ulong n, ArrayView<int> resultFlag)
|
||||
{
|
||||
byte nMod10 = (byte)(n % 10UL);
|
||||
ArrayView1D<ulong, Stride1D.Dense> divisors = tables.SelectDivisors(tableKind, nMod10);
|
||||
ArrayView1D<ulong, Stride1D.Dense> divisorSquares = tables.SelectDivisorSquares(tableKind, nMod10);
|
||||
ulong divisor = divisors[index];
|
||||
if (divisor <= 1UL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int divisorLength = (int)divisorSquares.Length;
|
||||
int threadIndex = index;
|
||||
bool skipThread = threadIndex >= divisorLength;
|
||||
ulong divisorSquare = skipThread ? 0UL : divisorSquares[threadIndex];
|
||||
skipThread = skipThread | (divisorSquare > maxDivisorSquare);
|
||||
|
||||
ulong divisor = skipThread ? 1UL : divisors[threadIndex];
|
||||
ulong remainder = skipThread ? 1UL : n % divisor;
|
||||
bool hasFactor = !skipThread & (remainder == 0UL);
|
||||
if (hasFactor)
|
||||
{
|
||||
resultFlag[0] = 1;
|
||||
}
|
||||
ulong residue = n % divisor;
|
||||
if (residue == 0UL)
|
||||
{
|
||||
resultFlag[0] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SharesFactorKernel(Index1D index, ArrayView<ulong> numbers, ArrayView<byte> results)
|
||||
|
||||
@@ -492,7 +492,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner)
|
||||
{
|
||||
_owner = owner;
|
||||
_lease = GpuContextPool.Rent();
|
||||
_lease = GpuContextPool.RentPreferred(preferCpu: false);
|
||||
_accelerator = _lease.Accelerator;
|
||||
_capacity = Math.Max(1, owner._gpuBatchSize);
|
||||
}
|
||||
@@ -657,7 +657,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
return lease;
|
||||
}
|
||||
|
||||
return GpuContextPool.Rent();
|
||||
return GpuContextPool.RentPreferred(preferCpu: false);
|
||||
}
|
||||
|
||||
private void ReturnAccelerator(GpuContextPool.GpuContextLease lease) => _acceleratorPool.Add(lease);
|
||||
|
||||
@@ -37,7 +37,7 @@ public sealed class MersenneNumberDivisorGpuTester
|
||||
|
||||
public bool IsDivisible(ulong exponent, in ReadOnlyGpuUInt128 divisor)
|
||||
{
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var gpu = GpuContextPool.RentPreferred(preferCpu: false);
|
||||
var accelerator = gpu.Accelerator;
|
||||
var kernel = GetKernel(accelerator);
|
||||
var resultBuffer = _resultBuffers.GetOrAdd(accelerator, acc => acc.Allocate1D<byte>(1));
|
||||
|
||||
@@ -38,6 +38,16 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
// Configurable LL slice size to keep kernels short. Default 32.
|
||||
public int SliceSize = 32;
|
||||
|
||||
public bool IsMersennePrime(ulong exponent)
|
||||
{
|
||||
// Default to global kernel preference for backward compatibility.
|
||||
bool runOnGpu = !GpuContextPool.ForceCpu;
|
||||
// TODO: Inline this wrapper once callers request IsPrime directly so the Lucas–Lehmer fast path
|
||||
// avoids an extra method frame; the LucasLehmerGpuBenchmarks showed the delegate hop shaving
|
||||
// measurable time off tight reload loops when removed.
|
||||
return IsPrime(exponent, runOnGpu);
|
||||
}
|
||||
|
||||
public bool IsPrime(ulong exponent, bool runOnGpu)
|
||||
{
|
||||
// Early rejections aligned with incremental/order sieves, but safe for small p:
|
||||
@@ -68,7 +78,7 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
}
|
||||
else
|
||||
{
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var gpu = GpuContextPool.RentPreferred(preferCpu: !runOnGpu);
|
||||
var accelerator = gpu.Accelerator;
|
||||
var kernel = GetKernel(accelerator);
|
||||
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.
|
||||
@@ -100,7 +110,7 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
}
|
||||
}
|
||||
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var gpu = GpuContextPool.RentPreferred(preferCpu: false);
|
||||
var accelerator = gpu.Accelerator;
|
||||
var kernel = GetBatchKernel(accelerator);
|
||||
|
||||
@@ -152,7 +162,7 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
|
||||
private bool IsMersennePrimeNtt(ulong exponent, GpuUInt128 nttMod, GpuUInt128 primitiveRoot, bool runOnGpu)
|
||||
{
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var gpu = GpuContextPool.RentPreferred(preferCpu: !runOnGpu);
|
||||
var accelerator = gpu.Accelerator;
|
||||
var addKernel = GetAddSmallKernel(accelerator);
|
||||
var subKernel = GetSubSmallKernel(accelerator);
|
||||
|
||||
@@ -132,7 +132,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
return lease;
|
||||
}
|
||||
|
||||
return GpuContextPool.Rent();
|
||||
return GpuContextPool.RentPreferred(preferCpu: !_useGpuOrder);
|
||||
}
|
||||
|
||||
private void ReturnAccelerator(GpuContextPool.GpuContextLease lease) => _acceleratorPool.Add(lease);
|
||||
|
||||
Reference in new issue
Block a user