Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6027a73839 |
No files matched your search
@@ -1,3 +1,6 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
using Open.Numeric.Primes;
|
using Open.Numeric.Primes;
|
||||||
@@ -9,6 +12,7 @@ namespace EvenPerfectBitScanner.Benchmarks;
|
|||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class PrimeTesterBenchmarks
|
public class PrimeTesterBenchmarks
|
||||||
{
|
{
|
||||||
|
private static readonly HeuristicCombinedPrimeTester _heuristicCombinedTester = new();
|
||||||
private static readonly HeuristicPrimeTester _heuristicTester = new();
|
private static readonly HeuristicPrimeTester _heuristicTester = new();
|
||||||
private ulong[] _candidates = [];
|
private ulong[] _candidates = [];
|
||||||
|
|
||||||
@@ -30,7 +34,7 @@ public class PrimeTesterBenchmarks
|
|||||||
_ = Prime.Numbers.IsPrime(_candidates[0]);
|
_ = Prime.Numbers.IsPrime(_candidates[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark(Baseline = true)]
|
||||||
public int HeuristicCpu()
|
public int HeuristicCpu()
|
||||||
{
|
{
|
||||||
HeuristicPrimeTester tester = _heuristicTester;
|
HeuristicPrimeTester tester = _heuristicTester;
|
||||||
@@ -100,15 +104,18 @@ public class PrimeTesterBenchmarks
|
|||||||
return primeCount;
|
return primeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly PrimeTester _nonHeuristicTester = new();
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public int NonHeuristicGpu()
|
public int NonHeuristicGpu()
|
||||||
{
|
{
|
||||||
|
PrimeTester tester = _nonHeuristicTester;
|
||||||
ulong[] values = _candidates;
|
ulong[] values = _candidates;
|
||||||
int primeCount = 0;
|
int primeCount = 0;
|
||||||
|
|
||||||
for (int i = 0; i < values.Length; i++)
|
for (int i = 0; i < values.Length; i++)
|
||||||
{
|
{
|
||||||
if (PrimeTester.IsPrimeGpu(values[i]))
|
if (tester.IsPrimeGpu(values[i]))
|
||||||
{
|
{
|
||||||
primeCount++;
|
primeCount++;
|
||||||
}
|
}
|
||||||
@@ -117,7 +124,7 @@ public class PrimeTesterBenchmarks
|
|||||||
return primeCount;
|
return primeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark]
|
||||||
public int NonHeuristicCpu()
|
public int NonHeuristicCpu()
|
||||||
{
|
{
|
||||||
ulong[] values = _candidates;
|
ulong[] values = _candidates;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace EvenPerfectBitScanner;
|
|||||||
|
|
||||||
internal static class Program
|
internal static class Program
|
||||||
{
|
{
|
||||||
// private static ThreadLocal<PrimeTester> PrimeTesters = null!;
|
private static ThreadLocal<PrimeTester> PrimeTesters = null!;
|
||||||
private static ThreadLocal<MersenneNumberTester> MersenneTesters = null!;
|
private static ThreadLocal<MersenneNumberTester> MersenneTesters = null!;
|
||||||
private const ulong InitialP = PerfectNumberConstants.BiggestKnownEvenPerfectP;
|
private const ulong InitialP = PerfectNumberConstants.BiggestKnownEvenPerfectP;
|
||||||
private static MersenneNumberDivisorGpuTester? _divisorTester;
|
private static MersenneNumberDivisorGpuTester? _divisorTester;
|
||||||
@@ -58,15 +58,14 @@ internal static class Program
|
|||||||
bool startPrimeProvided = _cliArguments.StartPrimeProvided;
|
bool startPrimeProvided = _cliArguments.StartPrimeProvided;
|
||||||
int threadCount = Math.Max(1, _cliArguments.ThreadCount);
|
int threadCount = Math.Max(1, _cliArguments.ThreadCount);
|
||||||
int gpuPrimeBatch = Math.Max(1, _cliArguments.GpuPrimeBatch);
|
int gpuPrimeBatch = Math.Max(1, _cliArguments.GpuPrimeBatch);
|
||||||
int gpuPrimeThreads = Math.Max(1, _cliArguments.GpuPrimeThreads);
|
|
||||||
UnboundedTaskScheduler.ConfigureThreadCount(threadCount);
|
UnboundedTaskScheduler.ConfigureThreadCount(threadCount);
|
||||||
PrimeTester.GpuBatchSize = gpuPrimeBatch;
|
PrimeTester.GpuBatchSize = gpuPrimeBatch;
|
||||||
GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads);
|
|
||||||
GpuContextPool.WarmUpPool(threadCount);
|
GpuContextPool.WarmUpPool(threadCount);
|
||||||
PrimeTester.WarmUpGpuKernels(gpuPrimeThreads);
|
PrimeTester.WarmUpGpuKernels(threadCount);
|
||||||
Console.WriteLine("Starting up threads...");
|
Console.WriteLine("Starting up threads...");
|
||||||
_ = UnboundedTaskScheduler.Instance;
|
_ = UnboundedTaskScheduler.Instance;
|
||||||
int blockSize = Math.Max(1, _cliArguments.BlockSize);
|
int blockSize = Math.Max(1, _cliArguments.BlockSize);
|
||||||
|
int gpuPrimeThreads = Math.Max(1, _cliArguments.GpuPrimeThreads);
|
||||||
GpuKernelType kernelType = _cliArguments.KernelType;
|
GpuKernelType kernelType = _cliArguments.KernelType;
|
||||||
bool useBitTransform = _cliArguments.UseBitTransform;
|
bool useBitTransform = _cliArguments.UseBitTransform;
|
||||||
bool useOrder = _cliArguments.UseOrder;
|
bool useOrder = _cliArguments.UseOrder;
|
||||||
@@ -118,6 +117,9 @@ internal static class Program
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply GPU prime sieve runtime configuration
|
||||||
|
GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads);
|
||||||
|
PrimeTester.GpuBatchSize = gpuPrimeBatch;
|
||||||
|
|
||||||
MersenneDivisorCycles mersenneDivisorCycles = new();
|
MersenneDivisorCycles mersenneDivisorCycles = new();
|
||||||
if (!File.Exists(cyclesPath) || continueCyclesGeneration)
|
if (!File.Exists(cyclesPath) || continueCyclesGeneration)
|
||||||
@@ -176,7 +178,7 @@ internal static class Program
|
|||||||
blockSize = useByDivisor ? 1 : blockSize;
|
blockSize = useByDivisor ? 1 : blockSize;
|
||||||
|
|
||||||
CandidatesCalculator.Initialize(currentP);
|
CandidatesCalculator.Initialize(currentP);
|
||||||
// PrimeTesters = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
PrimeTesters = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
||||||
// Note: --primes-device controls default device for library kernels; p primality remains CPU here.
|
// Note: --primes-device controls default device for library kernels; p primality remains CPU here.
|
||||||
// Initialize per-thread p residue tracker (Identity model) at currentP
|
// Initialize per-thread p residue tracker (Identity model) at currentP
|
||||||
if (!useDivisor && !_cliArguments.UseByDivisor)
|
if (!useDivisor && !_cliArguments.UseByDivisor)
|
||||||
@@ -626,7 +628,7 @@ internal static class Program
|
|||||||
{
|
{
|
||||||
if (!(_runPrimesOnCpu
|
if (!(_runPrimesOnCpu
|
||||||
? PrimeTester.IsPrime(p)
|
? PrimeTester.IsPrime(p)
|
||||||
: PrimeTester.IsPrimeGpu(p)))
|
: PrimeTesters.Value!.IsPrimeGpu(p)))
|
||||||
{
|
{
|
||||||
_lastCompositeP = true;
|
_lastCompositeP = true;
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -18,19 +18,19 @@ public class GpuPrimeLimiterTests
|
|||||||
var startedSecond = new TaskCompletionSource<bool>();
|
var startedSecond = new TaskCompletionSource<bool>();
|
||||||
var sw = new Stopwatch();
|
var sw = new Stopwatch();
|
||||||
|
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
var first = GpuPrimeWorkLimiter.Acquire();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
|
|
||||||
var secondTask = Task.Run(() =>
|
var secondTask = Task.Run(() =>
|
||||||
{
|
{
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
var second = GpuPrimeWorkLimiter.Acquire();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
startedSecond.TrySetResult(true);
|
startedSecond.TrySetResult(true);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
GpuPrimeWorkLimiter.Release();
|
second.Dispose();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ public class GpuPrimeLimiterTests
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
// Release first after the initial wait so the second task can proceed.
|
// Release first after the initial wait so the second task can proceed.
|
||||||
GpuPrimeWorkLimiter.Release();
|
first.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
await secondTask.WaitAsync(TimeSpan.FromSeconds(1));
|
await secondTask.WaitAsync(TimeSpan.FromSeconds(1));
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class PrimeTesterGpuTests
|
|||||||
ulong[] primes = [31UL, 61UL, 89UL, 107UL, 127UL, 521UL];
|
ulong[] primes = [31UL, 61UL, 89UL, 107UL, 127UL, 521UL];
|
||||||
foreach (ulong prime in primes)
|
foreach (ulong prime in primes)
|
||||||
{
|
{
|
||||||
PrimeTester.IsPrimeGpu(prime).Should().BeTrue();
|
tester.IsPrimeGpu(prime).Should().BeTrue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -34,7 +34,7 @@ public class PrimeTesterGpuTests
|
|||||||
ulong[] composites = [33UL, 39UL, 51UL, 77UL, 91UL];
|
ulong[] composites = [33UL, 39UL, 51UL, 77UL, 91UL];
|
||||||
foreach (ulong composite in composites)
|
foreach (ulong composite in composites)
|
||||||
{
|
{
|
||||||
PrimeTester.IsPrimeGpu(composite).Should().BeFalse();
|
tester.IsPrimeGpu(composite).Should().BeFalse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class PrimeTesterIsPrimeTests
|
|||||||
{
|
{
|
||||||
var tester = new PrimeTester();
|
var tester = new PrimeTester();
|
||||||
|
|
||||||
PrimeTester.IsPrimeGpu(n).Should().Be(expected);
|
tester.IsPrimeGpu(n).Should().Be(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -64,7 +64,7 @@ public class PrimeTesterIsPrimeTests
|
|||||||
var testers = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
var testers = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
||||||
Parallel.For(0, primes.Length, i =>
|
Parallel.For(0, primes.Length, i =>
|
||||||
{
|
{
|
||||||
results[i] = PrimeTester.IsPrimeGpu(primes[i]);
|
results[i] = testers.Value!.IsPrimeGpu(primes[i]);
|
||||||
});
|
});
|
||||||
|
|
||||||
results.Should().AllBeEquivalentTo(true);
|
results.Should().AllBeEquivalentTo(true);
|
||||||
@@ -73,7 +73,7 @@ public class PrimeTesterIsPrimeTests
|
|||||||
{
|
{
|
||||||
foreach (ulong prime in primes)
|
foreach (ulong prime in primes)
|
||||||
{
|
{
|
||||||
PrimeTester.IsPrimeGpu(prime).Should().BeTrue();
|
testers.Value!.IsPrimeGpu(prime).Should().BeTrue();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
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,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
@@ -11,15 +12,14 @@ public static class GpuContextPool
|
|||||||
// Default device preference for generic GPU kernels (prime scans, NTT, etc.)
|
// Default device preference for generic GPU kernels (prime scans, NTT, etc.)
|
||||||
internal sealed class PooledContext
|
internal sealed class PooledContext
|
||||||
{
|
{
|
||||||
public readonly Context Context;
|
public Context Context { get; }
|
||||||
public readonly Accelerator Accelerator;
|
public Accelerator Accelerator { get; }
|
||||||
|
|
||||||
public PooledContext(KernelContainer kernels)
|
public PooledContext()
|
||||||
{
|
{
|
||||||
Context = SharedGpuContext.Context;
|
Context = Context.CreateDefault();
|
||||||
Accelerator = SharedGpuContext.Accelerator;
|
Accelerator = Context.GetPreferredDevice(false).CreateAccelerator(Context);
|
||||||
GpuStaticTableInitializer.EnsureStaticTables(kernels, Accelerator);
|
DivisorGeneratorGpuCache.WarmUpByDivisorTables(Accelerator);
|
||||||
kernels.Dispose();
|
|
||||||
// NOTE: Avoid loading/compiling any kernel here to prevent implicit
|
// NOTE: Avoid loading/compiling any kernel here to prevent implicit
|
||||||
// CL stream/queue creation during accelerator construction.
|
// CL stream/queue creation during accelerator construction.
|
||||||
// Some OpenCL drivers are fragile when a queue is created immediately
|
// Some OpenCL drivers are fragile when a queue is created immediately
|
||||||
@@ -33,11 +33,11 @@ public static class GpuContextPool
|
|||||||
// Release PrimeTester GPU state for this accelerator (kernel/device primes)
|
// Release PrimeTester GPU state for this accelerator (kernel/device primes)
|
||||||
// Clear any per-accelerator cached resources to avoid releasing
|
// Clear any per-accelerator cached resources to avoid releasing
|
||||||
// after the accelerator is destroyed.
|
// after the accelerator is destroyed.
|
||||||
|
DivisorGeneratorGpuCache.Clear(Accelerator);
|
||||||
PrimeTester.ClearGpuCaches(Accelerator);
|
PrimeTester.ClearGpuCaches(Accelerator);
|
||||||
|
|
||||||
// These resources are shared between GPU leases
|
Accelerator.Dispose();
|
||||||
// Accelerator.Dispose();
|
Context.Dispose();
|
||||||
// Context.Dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,10 +53,9 @@ public static class GpuContextPool
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a new accelerator when the pool does not have one available.
|
// Create a new accelerator when the pool does not have one available.
|
||||||
return new GpuContextLease(new PooledContext(GpuKernelPool.GetKernels()));
|
return new GpuContextLease(new PooledContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void WarmUpPool(int threadCount)
|
public static void WarmUpPool(int threadCount)
|
||||||
{
|
{
|
||||||
if (threadCount <= WarmedGpuContextCount)
|
if (threadCount <= WarmedGpuContextCount)
|
||||||
@@ -65,16 +64,15 @@ public static class GpuContextPool
|
|||||||
}
|
}
|
||||||
|
|
||||||
int toCreate = threadCount - WarmedGpuContextCount;
|
int toCreate = threadCount - WarmedGpuContextCount;
|
||||||
Console.WriteLine($"Warming up GPU context pool to {toCreate} contexts...");
|
|
||||||
var contexts = new PooledContext[toCreate];
|
var contexts = new PooledContext[toCreate];
|
||||||
var pool = GpuPool;
|
|
||||||
PooledContext context;
|
|
||||||
for (int i = 0; i < toCreate; i++)
|
for (int i = 0; i < toCreate; i++)
|
||||||
{
|
{
|
||||||
KernelContainer kernels = GpuKernelPool.GetKernels();
|
contexts[i] = new PooledContext();
|
||||||
context = new PooledContext(kernels);
|
}
|
||||||
contexts[i] = context;
|
|
||||||
pool.Enqueue(context);
|
for (int i = 0; i < toCreate; i++)
|
||||||
|
{
|
||||||
|
GpuPool.Enqueue(contexts[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
WarmedGpuContextCount = threadCount;
|
WarmedGpuContextCount = threadCount;
|
||||||
@@ -87,6 +85,7 @@ public static class GpuContextPool
|
|||||||
gpu.Dispose();
|
gpu.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DivisorGeneratorGpuCache.DisposeAll();
|
||||||
PrimeTester.DisposeGpuContexts();
|
PrimeTester.DisposeGpuContexts();
|
||||||
WarmedGpuContextCount = 0;
|
WarmedGpuContextCount = 0;
|
||||||
}
|
}
|
||||||
@@ -97,30 +96,21 @@ public static class GpuContextPool
|
|||||||
GpuPool.Enqueue(ctx);
|
GpuPool.Enqueue(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct GpuContextLease
|
public readonly struct GpuContextLease
|
||||||
{
|
{
|
||||||
private PooledContext? _ctx;
|
private readonly PooledContext _ctx;
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
internal GpuContextLease(PooledContext ctx)
|
internal GpuContextLease(PooledContext ctx)
|
||||||
{
|
{
|
||||||
_ctx = ctx;
|
_ctx = ctx;
|
||||||
_disposed = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Context Context => _ctx!.Context;
|
public readonly Context Context => _ctx.Context;
|
||||||
public Accelerator Accelerator => _ctx!.Accelerator;
|
public readonly Accelerator Accelerator => _ctx.Accelerator;
|
||||||
|
|
||||||
public void Dispose()
|
public readonly void Dispose()
|
||||||
{
|
{
|
||||||
if (_disposed)
|
Return(_ctx);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
Return(_ctx!);
|
|
||||||
_ctx = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using System;
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
using PerfectNumbers.Core;
|
|
||||||
using static PerfectNumbers.Core.Gpu.GpuContextPool;
|
using static PerfectNumbers.Core.Gpu.GpuContextPool;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
@@ -11,24 +10,25 @@ public sealed class GpuKernelLease
|
|||||||
{
|
{
|
||||||
private static readonly ConcurrentQueue<GpuKernelLease> Pool = new();
|
private static readonly ConcurrentQueue<GpuKernelLease> Pool = new();
|
||||||
|
|
||||||
|
private Action? _releaseLimiter;
|
||||||
private GpuContextLease? _gpu;
|
private GpuContextLease? _gpu;
|
||||||
public KernelContainer Kernels;
|
private KernelContainer? _kernels;
|
||||||
private AcceleratorStream? _stream;
|
private AcceleratorStream? _stream;
|
||||||
|
|
||||||
private GpuKernelLease(KernelContainer kernels)
|
private GpuKernelLease()
|
||||||
{
|
{
|
||||||
Kernels = kernels;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static GpuKernelLease Rent(GpuContextLease gpu, KernelContainer kernels)
|
internal static GpuKernelLease Rent(GpuPrimeWorkLimiter.Lease limiter, GpuContextLease gpu, KernelContainer kernels)
|
||||||
{
|
{
|
||||||
if (!Pool.TryDequeue(out var lease))
|
if (!Pool.TryDequeue(out var lease))
|
||||||
{
|
{
|
||||||
lease = new GpuKernelLease(kernels);
|
lease = new GpuKernelLease();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lease._releaseLimiter = limiter.Dispose;
|
||||||
lease._gpu = gpu;
|
lease._gpu = gpu;
|
||||||
lease.Kernels = kernels;
|
lease._kernels = kernels;
|
||||||
lease._stream = null;
|
lease._stream = null;
|
||||||
return lease;
|
return lease;
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ public sealed class GpuKernelLease
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
var accel = Accelerator; // avoid capturing 'this' in lambda
|
var accel = Accelerator; // avoid capturing 'this' in lambda
|
||||||
return KernelContainer.InitOnce(ref Kernels!.Order, () =>
|
return KernelContainer.InitOnce(ref _kernels!.Order, () =>
|
||||||
{
|
{
|
||||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>(OrderKernels.OrderKernelScan);
|
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>(OrderKernels.OrderKernelScan);
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
@@ -70,7 +70,7 @@ public sealed class GpuKernelLease
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
var accel = Accelerator;
|
var accel = Accelerator;
|
||||||
return KernelContainer.InitOnce(ref Kernels!.Pow2Mod, () =>
|
return KernelContainer.InitOnce(ref _kernels!.Pow2Mod, () =>
|
||||||
{
|
{
|
||||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernels.Pow2ModKernelScan);
|
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernels.Pow2ModKernelScan);
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
@@ -84,7 +84,7 @@ public sealed class GpuKernelLease
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
var accel = Accelerator;
|
var accel = Accelerator;
|
||||||
return KernelContainer.InitOnce(ref Kernels!.Incremental, () =>
|
return KernelContainer.InitOnce(ref _kernels!.Incremental, () =>
|
||||||
{
|
{
|
||||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalKernelScan);
|
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalKernelScan);
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
@@ -98,7 +98,7 @@ public sealed class GpuKernelLease
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
var accel = Accelerator;
|
var accel = Accelerator;
|
||||||
return KernelContainer.InitOnce(ref Kernels!.IncrementalOrder, () =>
|
return KernelContainer.InitOnce(ref _kernels!.IncrementalOrder, () =>
|
||||||
{
|
{
|
||||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalOrderKernelScan);
|
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalOrderKernelScan);
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
@@ -112,7 +112,7 @@ public sealed class GpuKernelLease
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
var accel = Accelerator;
|
var accel = Accelerator;
|
||||||
return KernelContainer.InitOnce(ref Kernels!.Pow2ModOrder, () =>
|
return KernelContainer.InitOnce(ref _kernels!.Pow2ModOrder, () =>
|
||||||
{
|
{
|
||||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernels.Pow2ModOrderKernelScan);
|
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernels.Pow2ModOrderKernelScan);
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
@@ -121,26 +121,12 @@ public sealed class GpuKernelLease
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ushort, Stride1D.Dense>> SpecialMaxKernel
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var accel = Accelerator;
|
|
||||||
return KernelContainer.InitOnce(ref Kernels!.SpecialMax, () =>
|
|
||||||
{
|
|
||||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ushort, Stride1D.Dense>>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel);
|
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
|
||||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ushort, Stride1D.Dense>>>();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SmallPrimeFactorKernel
|
public Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SmallPrimeFactorKernel
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var accel = Accelerator;
|
var accel = Accelerator;
|
||||||
return KernelContainer.InitOnce(ref Kernels!.SmallPrimeFactor, () =>
|
return KernelContainer.InitOnce(ref _kernels!.SmallPrimeFactor, () =>
|
||||||
{
|
{
|
||||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(SmallPrimeFactorKernels.SmallPrimeFactorKernelScan);
|
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(SmallPrimeFactorKernels.SmallPrimeFactorKernelScan);
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
@@ -157,8 +143,10 @@ public sealed class GpuKernelLease
|
|||||||
_gpu?.Dispose();
|
_gpu?.Dispose();
|
||||||
_gpu = null;
|
_gpu = null;
|
||||||
|
|
||||||
Kernels?.Dispose();
|
_releaseLimiter?.Invoke();
|
||||||
GpuPrimeWorkLimiter.Release();
|
_releaseLimiter = null;
|
||||||
|
|
||||||
|
_kernels = null;
|
||||||
Pool.Enqueue(this);
|
Pool.Enqueue(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using PerfectNumbers.Core;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
@@ -38,7 +37,6 @@ public sealed class KernelContainer
|
|||||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
||||||
ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2ModOrder;
|
ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2ModOrder;
|
||||||
public Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>? SmallPrimeFactor;
|
public Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>? SmallPrimeFactor;
|
||||||
public Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ushort, Stride1D.Dense>>? SpecialMax;
|
|
||||||
|
|
||||||
// Optional device buffer with small divisor cycles (<= 4M). Index = divisor, value = cycle length.
|
// Optional device buffer with small divisor cycles (<= 4M). Index = divisor, value = cycle length.
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallCycles;
|
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallCycles;
|
||||||
@@ -48,9 +46,6 @@ public sealed class KernelContainer
|
|||||||
public MemoryBuffer1D<int, Stride1D.Dense>? SmallPrimeFactorExponentSlots;
|
public MemoryBuffer1D<int, Stride1D.Dense>? SmallPrimeFactorExponentSlots;
|
||||||
public MemoryBuffer1D<int, Stride1D.Dense>? SmallPrimeFactorCountSlot;
|
public MemoryBuffer1D<int, Stride1D.Dense>? SmallPrimeFactorCountSlot;
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimeFactorRemainingSlot;
|
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimeFactorRemainingSlot;
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense>? SpecialMaxFactors;
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense>? SpecialMaxCandidates;
|
|
||||||
public MemoryBuffer1D<ushort, Stride1D.Dense>? SpecialMaxResult;
|
|
||||||
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastOne;
|
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastOne;
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2LastOne;
|
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2LastOne;
|
||||||
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastSeven;
|
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastSeven;
|
||||||
@@ -72,8 +67,6 @@ public sealed class KernelContainer
|
|||||||
}
|
}
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() => GpuKernelPool.Return(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly struct ResiduePrimeViews(
|
public readonly struct ResiduePrimeViews(
|
||||||
@@ -88,29 +81,44 @@ public readonly struct ResiduePrimeViews(
|
|||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> LastSevenPow2 = lastSevenPow2;
|
public readonly ArrayView1D<ulong, Stride1D.Dense> LastSevenPow2 = lastSevenPow2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly struct SmallPrimeFactorTables(
|
public readonly struct SmallPrimeFactorTables
|
||||||
|
{
|
||||||
|
public readonly MemoryBuffer1D<uint, Stride1D.Dense> Primes;
|
||||||
|
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> Squares;
|
||||||
|
public readonly int Count;
|
||||||
|
|
||||||
|
public SmallPrimeFactorTables(
|
||||||
MemoryBuffer1D<uint, Stride1D.Dense> primes,
|
MemoryBuffer1D<uint, Stride1D.Dense> primes,
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense> squares,
|
MemoryBuffer1D<ulong, Stride1D.Dense> squares,
|
||||||
int count)
|
int count)
|
||||||
{
|
{
|
||||||
public readonly MemoryBuffer1D<uint, Stride1D.Dense> Primes = primes;
|
Primes = primes;
|
||||||
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> Squares = squares;
|
Squares = squares;
|
||||||
public readonly int Count = count;
|
Count = count;
|
||||||
|
}
|
||||||
|
|
||||||
public ArrayView1D<uint, Stride1D.Dense> PrimesView => Primes.View;
|
public ArrayView1D<uint, Stride1D.Dense> PrimesView => Primes.View;
|
||||||
public ArrayView1D<ulong, Stride1D.Dense> SquaresView => Squares.View;
|
public ArrayView1D<ulong, Stride1D.Dense> SquaresView => Squares.View;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly struct SmallPrimeFactorScratch(
|
public readonly struct SmallPrimeFactorScratch
|
||||||
|
{
|
||||||
|
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> PrimeSlots;
|
||||||
|
public readonly MemoryBuffer1D<int, Stride1D.Dense> ExponentSlots;
|
||||||
|
public readonly MemoryBuffer1D<int, Stride1D.Dense> CountSlot;
|
||||||
|
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> RemainingSlot;
|
||||||
|
|
||||||
|
public SmallPrimeFactorScratch(
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense> primeSlots,
|
MemoryBuffer1D<ulong, Stride1D.Dense> primeSlots,
|
||||||
MemoryBuffer1D<int, Stride1D.Dense> exponentSlots,
|
MemoryBuffer1D<int, Stride1D.Dense> exponentSlots,
|
||||||
MemoryBuffer1D<int, Stride1D.Dense> countSlot,
|
MemoryBuffer1D<int, Stride1D.Dense> countSlot,
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense> remainingSlot)
|
MemoryBuffer1D<ulong, Stride1D.Dense> remainingSlot)
|
||||||
{
|
{
|
||||||
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> PrimeSlots = primeSlots;
|
PrimeSlots = primeSlots;
|
||||||
public readonly MemoryBuffer1D<int, Stride1D.Dense> ExponentSlots = exponentSlots;
|
ExponentSlots = exponentSlots;
|
||||||
public readonly MemoryBuffer1D<int, Stride1D.Dense> CountSlot = countSlot;
|
CountSlot = countSlot;
|
||||||
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> RemainingSlot = remainingSlot;
|
RemainingSlot = remainingSlot;
|
||||||
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
@@ -121,55 +129,44 @@ public readonly struct SmallPrimeFactorScratch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly struct SpecialMaxScratch(
|
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense> factorValues,
|
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense> candidateValues,
|
|
||||||
MemoryBuffer1D<ushort, Stride1D.Dense> resultSlot)
|
|
||||||
{
|
|
||||||
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> FactorValues = factorValues;
|
|
||||||
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> CandidateValues = candidateValues;
|
|
||||||
public readonly MemoryBuffer1D<ushort, Stride1D.Dense> ResultSlot = resultSlot;
|
|
||||||
|
|
||||||
public ArrayView1D<ulong, Stride1D.Dense> FactorsView => FactorValues.View;
|
|
||||||
public ArrayView1D<ulong, Stride1D.Dense> CandidatesView => CandidateValues.View;
|
|
||||||
public ArrayView1D<ushort, Stride1D.Dense> ResultView => ResultSlot.View;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GpuKernelPool
|
public class GpuKernelPool
|
||||||
{
|
{
|
||||||
private static readonly ConcurrentQueue<KernelContainer> KernelCache = new(); // TODO: Replace this concurrent map with a simple accelerator-indexed lookup once kernel launchers are prewarmed during startup so we can drop the thread-safe wrapper entirely.
|
private static readonly ConcurrentDictionary<Accelerator, KernelContainer> KernelCache = new(); // TODO: Replace this concurrent map with a simple accelerator-indexed lookup once kernel launchers are prewarmed during startup so we can drop the thread-safe wrapper entirely.
|
||||||
|
|
||||||
public static KernelContainer GetKernels() => KernelCache.TryDequeue(out var container)
|
private static KernelContainer GetKernels(Accelerator accelerator)
|
||||||
? container
|
|
||||||
: new KernelContainer();
|
|
||||||
|
|
||||||
public static void Return(KernelContainer container) => KernelCache.Enqueue(container);
|
|
||||||
|
|
||||||
internal static void PreloadStaticTables(KernelContainer kernels, Accelerator accelerator)
|
|
||||||
{
|
{
|
||||||
EnsureSmallCyclesOnDevice(kernels, accelerator);
|
return KernelCache.GetOrAdd(accelerator, _ => new KernelContainer());
|
||||||
EnsureSmallPrimesOnDevice(kernels, accelerator);
|
|
||||||
EnsureSmallPrimeFactorTables(kernels, accelerator);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensures the small cycles table is uploaded to the device for the given accelerator.
|
// Ensures the small cycles table is uploaded to the device for the given accelerator.
|
||||||
// Returns the ArrayView to pass into kernels (when kernels are extended to accept it).
|
// Returns the ArrayView to pass into kernels (when kernels are extended to accept it).
|
||||||
public static ArrayView1D<ulong, Stride1D.Dense> EnsureSmallCyclesOnDevice(KernelContainer kernels, Accelerator accelerator)
|
public static ArrayView1D<ulong, Stride1D.Dense> EnsureSmallCyclesOnDevice(Accelerator accelerator)
|
||||||
{
|
{
|
||||||
|
var kernels = GetKernels(accelerator);
|
||||||
if (kernels.SmallCycles is { } buffer)
|
if (kernels.SmallCycles is { } buffer)
|
||||||
{
|
{
|
||||||
return buffer.View;
|
return buffer.View;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure single upload per accelerator even if multiple threads race here.
|
||||||
|
lock (kernels) // TODO: Remove this lock by pre-uploading the immutable small-cycle snapshot during initialization; once no mutation happens at runtime, the pool must expose a simple reference without synchronization.
|
||||||
|
{
|
||||||
|
if (kernels.SmallCycles is { } existing)
|
||||||
|
{
|
||||||
|
return existing.View;
|
||||||
|
}
|
||||||
|
|
||||||
var host = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot(); // TODO: Preload this device buffer during startup and keep it immutable so we can delete the lock above in favor of the preloaded snapshot.
|
var host = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot(); // TODO: Preload this device buffer during startup and keep it immutable so we can delete the lock above in favor of the preloaded snapshot.
|
||||||
var device = accelerator.Allocate1D<ulong>(host.Length);
|
var device = accelerator.Allocate1D<ulong>(host.Length);
|
||||||
device.View.CopyFromCPU(host);
|
device.View.CopyFromCPU(host);
|
||||||
kernels.SmallCycles = device;
|
kernels.SmallCycles = device;
|
||||||
return device.View;
|
return device.View;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static ResiduePrimeViews EnsureSmallPrimesOnDevice(KernelContainer kernels, Accelerator accelerator)
|
public static ResiduePrimeViews EnsureSmallPrimesOnDevice(Accelerator accelerator)
|
||||||
{
|
{
|
||||||
|
var kernels = GetKernels(accelerator);
|
||||||
if (kernels.SmallPrimesLastOne is { } lastOne &&
|
if (kernels.SmallPrimesLastOne is { } lastOne &&
|
||||||
kernels.SmallPrimesLastSeven is { } lastSeven &&
|
kernels.SmallPrimesLastSeven is { } lastSeven &&
|
||||||
kernels.SmallPrimesPow2LastOne is { } lastOnePow2 &&
|
kernels.SmallPrimesPow2LastOne is { } lastOnePow2 &&
|
||||||
@@ -178,6 +175,8 @@ public class GpuKernelPool
|
|||||||
return new ResiduePrimeViews(lastOne.View, lastSeven.View, lastOnePow2.View, lastSevenPow2.View);
|
return new ResiduePrimeViews(lastOne.View, lastSeven.View, lastOnePow2.View, lastSevenPow2.View);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock (kernels) // TODO: Inline these small-prime uploads into startup initialization alongside the small-cycle snapshot so we can drop runtime locking and keep the GPU pool free of synchronization.
|
||||||
|
{
|
||||||
if (kernels.SmallPrimesLastOne is { } existingLastOne &&
|
if (kernels.SmallPrimesLastOne is { } existingLastOne &&
|
||||||
kernels.SmallPrimesLastSeven is { } existingLastSeven &&
|
kernels.SmallPrimesLastSeven is { } existingLastSeven &&
|
||||||
kernels.SmallPrimesPow2LastOne is { } existingLastOnePow2 &&
|
kernels.SmallPrimesPow2LastOne is { } existingLastOnePow2 &&
|
||||||
@@ -207,14 +206,18 @@ public class GpuKernelPool
|
|||||||
|
|
||||||
return new ResiduePrimeViews(deviceLastOne.View, deviceLastSeven.View, deviceLastOnePow2.View, deviceLastSevenPow2.View);
|
return new ResiduePrimeViews(deviceLastOne.View, deviceLastSeven.View, deviceLastOnePow2.View, deviceLastSevenPow2.View);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static SmallPrimeFactorTables EnsureSmallPrimeFactorTables(KernelContainer kernels, Accelerator accelerator)
|
public static SmallPrimeFactorTables EnsureSmallPrimeFactorTables(Accelerator accelerator)
|
||||||
{
|
{
|
||||||
|
var kernels = GetKernels(accelerator);
|
||||||
if (kernels.SmallPrimeFactorsPrimes is { } primeBuffer && kernels.SmallPrimeFactorsSquares is { } squareBuffer)
|
if (kernels.SmallPrimeFactorsPrimes is { } primeBuffer && kernels.SmallPrimeFactorsSquares is { } squareBuffer)
|
||||||
{
|
{
|
||||||
return new SmallPrimeFactorTables(primeBuffer, squareBuffer, (int)primeBuffer.Length);
|
return new SmallPrimeFactorTables(primeBuffer, squareBuffer, (int)primeBuffer.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock (kernels)
|
||||||
|
{
|
||||||
if (kernels.SmallPrimeFactorsPrimes is { } existingPrimes && kernels.SmallPrimeFactorsSquares is { } existingSquares)
|
if (kernels.SmallPrimeFactorsPrimes is { } existingPrimes && kernels.SmallPrimeFactorsSquares is { } existingSquares)
|
||||||
{
|
{
|
||||||
return new SmallPrimeFactorTables(existingPrimes, existingSquares, (int)existingPrimes.Length);
|
return new SmallPrimeFactorTables(existingPrimes, existingSquares, (int)existingPrimes.Length);
|
||||||
@@ -233,8 +236,12 @@ public class GpuKernelPool
|
|||||||
|
|
||||||
return new SmallPrimeFactorTables(devicePrimes, deviceSquares, hostPrimes.Length);
|
return new SmallPrimeFactorTables(devicePrimes, deviceSquares, hostPrimes.Length);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static SmallPrimeFactorScratch EnsureSmallPrimeFactorScratch(KernelContainer kernels, Accelerator accelerator, int slotCount)
|
public static SmallPrimeFactorScratch EnsureSmallPrimeFactorScratch(Accelerator accelerator, int slotCount)
|
||||||
|
{
|
||||||
|
var kernels = GetKernels(accelerator);
|
||||||
|
lock (kernels)
|
||||||
{
|
{
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense>? primeSlots = kernels.SmallPrimeFactorPrimeSlots;
|
MemoryBuffer1D<ulong, Stride1D.Dense>? primeSlots = kernels.SmallPrimeFactorPrimeSlots;
|
||||||
if (primeSlots is null || primeSlots.Length < slotCount)
|
if (primeSlots is null || primeSlots.Length < slotCount)
|
||||||
@@ -268,41 +275,15 @@ public class GpuKernelPool
|
|||||||
|
|
||||||
return new SmallPrimeFactorScratch(primeSlots!, exponentSlots!, countSlot!, remainingSlot!);
|
return new SmallPrimeFactorScratch(primeSlots!, exponentSlots!, countSlot!, remainingSlot!);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SpecialMaxScratch EnsureSpecialMaxScratch(KernelContainer kernels, Accelerator accelerator, int factorCapacity)
|
|
||||||
{
|
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense>? factorValues = kernels.SpecialMaxFactors;
|
|
||||||
if (factorValues is null || factorValues.Length < factorCapacity)
|
|
||||||
{
|
|
||||||
factorValues?.Dispose();
|
|
||||||
factorValues = accelerator.Allocate1D<ulong>(factorCapacity);
|
|
||||||
kernels.SpecialMaxFactors = factorValues;
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense>? candidateValues = kernels.SpecialMaxCandidates;
|
|
||||||
if (candidateValues is null || candidateValues.Length < factorCapacity)
|
|
||||||
{
|
|
||||||
candidateValues?.Dispose();
|
|
||||||
candidateValues = accelerator.Allocate1D<ulong>(factorCapacity);
|
|
||||||
kernels.SpecialMaxCandidates = candidateValues;
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryBuffer1D<ushort, Stride1D.Dense>? resultSlot = kernels.SpecialMaxResult;
|
|
||||||
if (resultSlot is null)
|
|
||||||
{
|
|
||||||
resultSlot = accelerator.Allocate1D<ushort>(1);
|
|
||||||
kernels.SpecialMaxResult = resultSlot;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SpecialMaxScratch(factorValues!, candidateValues!, resultSlot!);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GpuKernelLease GetKernel(bool useGpuOrder)
|
public static GpuKernelLease GetKernel(bool useGpuOrder)
|
||||||
{
|
{
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
var limiter = GpuPrimeWorkLimiter.Acquire();
|
||||||
var gpu = Rent();
|
var gpu = Rent();
|
||||||
var kernels = GetKernels();
|
var accelerator = gpu.Accelerator;
|
||||||
return GpuKernelLease.Rent(gpu, kernels);
|
var kernels = GetKernels(accelerator);
|
||||||
|
return GpuKernelLease.Rent(limiter, gpu, kernels);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
public static class GpuPrimeWorkLimiter
|
public static class GpuPrimeWorkLimiter
|
||||||
@@ -8,16 +5,17 @@ public static class GpuPrimeWorkLimiter
|
|||||||
private static SemaphoreSlim _semaphore = new(1, int.MaxValue);
|
private static SemaphoreSlim _semaphore = new(1, int.MaxValue);
|
||||||
private static int _currentLimit = 1;
|
private static int _currentLimit = 1;
|
||||||
|
|
||||||
public static void Acquire() => _semaphore.Wait();
|
public static Lease Acquire()
|
||||||
public static void Release() => _semaphore.Release();
|
{
|
||||||
|
// TODO: Inline the pooled limiter guard from the GpuLimiterThroughputBenchmarks so prime-sieve
|
||||||
|
// GPU jobs stop allocating Lease instances on every acquisition.
|
||||||
|
var sem = _semaphore;
|
||||||
|
// sem.Wait();
|
||||||
|
return new Lease(sem);
|
||||||
|
}
|
||||||
|
|
||||||
public static void SetLimit(int value)
|
public static void SetLimit(int value)
|
||||||
{
|
{
|
||||||
if (value <= 0)
|
|
||||||
{
|
|
||||||
value = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value == _currentLimit)
|
if (value == _currentLimit)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -25,9 +23,23 @@ public static class GpuPrimeWorkLimiter
|
|||||||
|
|
||||||
// TODO: Switch to a shared limiter implementation with GpuWorkLimiter so we can coordinate CPU/GPU prime work using
|
// TODO: Switch to a shared limiter implementation with GpuWorkLimiter so we can coordinate CPU/GPU prime work using
|
||||||
// the same semaphore pool without reallocating per adjustment.
|
// the same semaphore pool without reallocating per adjustment.
|
||||||
var previous = _semaphore;
|
|
||||||
_semaphore = new SemaphoreSlim(value, value);
|
_semaphore = new SemaphoreSlim(value, value);
|
||||||
_currentLimit = value;
|
_currentLimit = value;
|
||||||
previous.Dispose();
|
}
|
||||||
|
|
||||||
|
public sealed class Lease
|
||||||
|
{
|
||||||
|
private readonly SemaphoreSlim _sem;
|
||||||
|
|
||||||
|
internal Lease(SemaphoreSlim sem)
|
||||||
|
{
|
||||||
|
_sem = sem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// _sem.Release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using ILGPU;
|
|
||||||
using ILGPU.Runtime;
|
|
||||||
using PerfectNumbers.Core;
|
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
|
||||||
|
|
||||||
internal static class GpuStaticTableInitializer
|
|
||||||
{
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, byte> Initialized = new(AcceleratorReferenceComparer.Instance);
|
|
||||||
|
|
||||||
internal static void EnsureStaticTables(KernelContainer kernels, Accelerator accelerator)
|
|
||||||
{
|
|
||||||
Initialized.GetOrAdd(accelerator, acc =>
|
|
||||||
{
|
|
||||||
PrimeTester.PrimeTesterGpuContextPool.EnsureStaticTables(kernels, acc);
|
|
||||||
GpuKernelPool.PreloadStaticTables(kernels, acc);
|
|
||||||
PrimeOrderGpuHeuristics.PreloadStaticTables( acc);
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class AcceleratorReferenceComparer : IEqualityComparer<Accelerator>
|
|
||||||
{
|
|
||||||
internal 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,83 +8,21 @@ internal readonly struct HeuristicGpuDivisorTables
|
|||||||
{
|
{
|
||||||
public static readonly HeuristicGpuDivisorTables Shared = default;
|
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> GroupADivisors;
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupADivisorSquares;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding1;
|
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> GroupBDivisorsEnding7;
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding7;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding9;
|
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding9;
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding9;
|
|
||||||
|
|
||||||
public HeuristicGpuDivisorTables(
|
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> groupADivisors,
|
||||||
ArrayView1D<ulong, Stride1D.Dense> groupADivisorSquares,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding1,
|
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding1,
|
||||||
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding1,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding7,
|
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding7,
|
||||||
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding7,
|
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding9)
|
||||||
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;
|
GroupADivisors = groupADivisors;
|
||||||
GroupADivisorSquares = groupADivisorSquares;
|
|
||||||
GroupBDivisorsEnding1 = groupBDivisorsEnding1;
|
GroupBDivisorsEnding1 = groupBDivisorsEnding1;
|
||||||
GroupBDivisorSquaresEnding1 = groupBDivisorSquaresEnding1;
|
|
||||||
GroupBDivisorsEnding7 = groupBDivisorsEnding7;
|
GroupBDivisorsEnding7 = groupBDivisorsEnding7;
|
||||||
GroupBDivisorSquaresEnding7 = groupBDivisorSquaresEnding7;
|
|
||||||
GroupBDivisorsEnding9 = groupBDivisorsEnding9;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -99,18 +37,6 @@ internal readonly struct HeuristicGpuDivisorTables
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public ArrayView1D<ulong, Stride1D.Dense> SelectDivisors(HeuristicGpuDivisorTableKind kind, byte ending)
|
public ArrayView1D<ulong, Stride1D.Dense> SelectDivisors(HeuristicGpuDivisorTableKind kind, byte ending)
|
||||||
{
|
{
|
||||||
@@ -120,21 +46,6 @@ internal readonly struct HeuristicGpuDivisorTables
|
|||||||
HeuristicGpuDivisorTableKind.GroupBEnding1 => GroupBDivisorsEnding1,
|
HeuristicGpuDivisorTableKind.GroupBEnding1 => GroupBDivisorsEnding1,
|
||||||
HeuristicGpuDivisorTableKind.GroupBEnding7 => GroupBDivisorsEnding7,
|
HeuristicGpuDivisorTableKind.GroupBEnding7 => GroupBDivisorsEnding7,
|
||||||
HeuristicGpuDivisorTableKind.GroupBEnding9 => GroupBDivisorsEnding9,
|
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,
|
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -144,3 +55,43 @@ internal readonly struct HeuristicGpuDivisorTables
|
|||||||
Unsafe.AsRef(in Shared) = tables;
|
Unsafe.AsRef(in Shared) = tables;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal readonly struct HeuristicGpuCombinedDivisorTables
|
||||||
|
{
|
||||||
|
public static readonly HeuristicGpuCombinedDivisorTables 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 HeuristicGpuCombinedDivisorTables(
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding1,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding3,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding7,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding9)
|
||||||
|
{
|
||||||
|
CombinedDivisorsEnding1 = combinedDivisorsEnding1;
|
||||||
|
CombinedDivisorsEnding3 = combinedDivisorsEnding3;
|
||||||
|
CombinedDivisorsEnding7 = combinedDivisorsEnding7;
|
||||||
|
CombinedDivisorsEnding9 = combinedDivisorsEnding9;
|
||||||
|
}
|
||||||
|
|
||||||
|
[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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void InitializeShared(in HeuristicGpuCombinedDivisorTables tables)
|
||||||
|
{
|
||||||
|
Unsafe.AsRef(in Shared) = tables;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
using ILGPU;
|
|
||||||
using ILGPU.Runtime;
|
|
||||||
using PerfectNumbers.Core;
|
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
|
||||||
|
|
||||||
internal static partial class PrimeOrderGpuHeuristics
|
|
||||||
{
|
|
||||||
internal static void EvaluateSpecialMaxCandidatesKernel(
|
|
||||||
Index1D index,
|
|
||||||
ulong phi,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> factors,
|
|
||||||
int factorCount,
|
|
||||||
MontgomeryDivisorData divisor,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> candidates,
|
|
||||||
ArrayView1D<ushort, Stride1D.Dense> resultOut)
|
|
||||||
{
|
|
||||||
if (index != 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int actual = 0;
|
|
||||||
for (int i = 0; i < factorCount; i++)
|
|
||||||
{
|
|
||||||
ulong factor = factors[i];
|
|
||||||
if (factor <= 1UL)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong reduced = phi / factor;
|
|
||||||
if (reduced == 0UL)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
candidates[actual] = reduced;
|
|
||||||
actual++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actual == 0)
|
|
||||||
{
|
|
||||||
resultOut[0] = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 1; i < actual; i++)
|
|
||||||
{
|
|
||||||
ulong current = candidates[i];
|
|
||||||
int insert = i - 1;
|
|
||||||
while (insert >= 0 && candidates[insert] > current)
|
|
||||||
{
|
|
||||||
candidates[insert + 1] = candidates[insert];
|
|
||||||
insert--;
|
|
||||||
}
|
|
||||||
|
|
||||||
candidates[insert + 1] = current;
|
|
||||||
}
|
|
||||||
|
|
||||||
var divisorPartial = new GpuDivisorPartialData(divisor.Modulus);
|
|
||||||
var stepper = new ExponentRemainderStepperGpu(divisorPartial);
|
|
||||||
if (stepper.InitializeIsUnityGpu(candidates[0]))
|
|
||||||
{
|
|
||||||
resultOut[0] = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 1; i < actual; i++)
|
|
||||||
{
|
|
||||||
if (stepper.ComputeNextIsUnityGpu(candidates[i]))
|
|
||||||
{
|
|
||||||
resultOut[0] = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resultOut[0] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -82,30 +82,25 @@ internal static class PrimeTesterKernels
|
|||||||
Index1D index,
|
Index1D index,
|
||||||
ArrayView<int> resultFlag,
|
ArrayView<int> resultFlag,
|
||||||
ulong n,
|
ulong n,
|
||||||
ulong maxDivisorSquare,
|
ulong maxDivisor,
|
||||||
HeuristicGpuDivisorTableKind tableKind,
|
HeuristicGpuDivisorTableKind tableKind,
|
||||||
HeuristicGpuDivisorTables tables)
|
HeuristicGpuDivisorTables tables,
|
||||||
|
HeuristicGpuCombinedDivisorTables combinedTables)
|
||||||
{
|
{
|
||||||
byte nMod10 = (byte)(n % 10UL);
|
byte nMod10 = (byte)(n % 10UL);
|
||||||
ArrayView1D<ulong, Stride1D.Dense> divisors = tables.SelectDivisors(tableKind, nMod10);
|
ArrayView1D<ulong, Stride1D.Dense> divisors = tableKind == HeuristicGpuDivisorTableKind.Combined
|
||||||
ArrayView1D<ulong, Stride1D.Dense> divisorSquares = tables.SelectDivisorSquares(tableKind, nMod10);
|
? combinedTables.SelectCombined(nMod10)
|
||||||
|
: tables.SelectDivisors(tableKind, nMod10);
|
||||||
|
|
||||||
int divisorLength = (int)divisorSquares.Length;
|
int divisorLength = (int)divisors.Length;
|
||||||
int threadIndex = index;
|
int threadIndex = index;
|
||||||
if (threadIndex >= divisorLength)
|
bool skipThread = threadIndex >= divisorLength;
|
||||||
{
|
ulong divisor = skipThread ? 1UL : divisors[threadIndex];
|
||||||
return;
|
skipThread = skipThread | (divisor > maxDivisor);
|
||||||
}
|
|
||||||
|
|
||||||
ulong divisorSquare = divisorSquares[threadIndex];
|
ulong remainder = skipThread ? 1UL : n % divisor;
|
||||||
if (divisorSquare > maxDivisorSquare)
|
bool hasFactor = !skipThread & (remainder == 0UL);
|
||||||
{
|
if (hasFactor)
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong divisor = divisors[threadIndex];
|
|
||||||
divisor = n % divisor;
|
|
||||||
if (divisor == 0UL)
|
|
||||||
{
|
{
|
||||||
resultFlag[0] = 1;
|
resultFlag[0] = 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
|
|||||||
ulong step3 = ((exponent % 3UL) << 1) % 3UL;
|
ulong step3 = ((exponent % 3UL) << 1) % 3UL;
|
||||||
ulong step5 = ((exponent % 5UL) << 1) % 5UL;
|
ulong step5 = ((exponent % 5UL) << 1) % 5UL;
|
||||||
GpuUInt128 twoPGpu = (GpuUInt128)twoP;
|
GpuUInt128 twoPGpu = (GpuUInt128)twoP;
|
||||||
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(gpuLease.Kernels, accelerator);
|
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
|
||||||
ResiduePrimeViews primeViews = default;
|
ResiduePrimeViews primeViews = default;
|
||||||
if (_kernelType == GpuKernelType.Pow2Mod)
|
if (_kernelType == GpuKernelType.Pow2Mod)
|
||||||
{
|
{
|
||||||
primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(gpuLease.Kernels, accelerator);
|
primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(accelerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
var orderBuffer = accelerator.Allocate1D<ulong>(batchSize);
|
var orderBuffer = accelerator.Allocate1D<ulong>(batchSize);
|
||||||
|
|||||||
@@ -54,13 +54,13 @@ public class MersenneNumberLucasLehmerGpuTester
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
var limiter = 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();
|
limiter.Dispose();
|
||||||
throw new NotSupportedException("NTT parameters for the given exponent are not available.");
|
throw new NotSupportedException("NTT parameters for the given exponent are not available.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ public class MersenneNumberLucasLehmerGpuTester
|
|||||||
gpu.Dispose();
|
gpu.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
GpuPrimeWorkLimiter.Release();
|
limiter.Dispose();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
|
|||||||
UInt128 remaining;
|
UInt128 remaining;
|
||||||
int currentSize;
|
int currentSize;
|
||||||
int found = 0;
|
int found = 0;
|
||||||
|
|
||||||
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(gpuLease.Kernels, accelerator);
|
|
||||||
while (kStart <= maxK && Volatile.Read(ref isPrime))
|
while (kStart <= maxK && Volatile.Read(ref isPrime))
|
||||||
{
|
{
|
||||||
remaining = maxK - kStart + 1UL;
|
remaining = maxK - kStart + 1UL;
|
||||||
@@ -46,6 +44,7 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
|
|||||||
var kernelArgs = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
|
var kernelArgs = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
|
||||||
|
|
||||||
// Ensure device has small cycles table for early rejections in order kernels
|
// Ensure device has small cycles table for early rejections in order kernels
|
||||||
|
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
|
||||||
kernel(stream, currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, divMul,
|
kernel(stream, currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, divMul,
|
||||||
kernelArgs, foundBuffer.View, smallCyclesView);
|
kernelArgs, foundBuffer.View, smallCyclesView);
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
|||||||
// GPU residue variant: check 2^p % q == 1 for q = 2*p*k + 1.
|
// GPU residue variant: check 2^p % q == 1 for q = 2*p*k + 1.
|
||||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||||
{
|
{
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
var limiter = GpuPrimeWorkLimiter.Acquire();
|
||||||
var gpuLease = RentAccelerator();
|
var gpuLease = RentAccelerator();
|
||||||
var accelerator = gpuLease.Accelerator;
|
var accelerator = gpuLease.Accelerator;
|
||||||
|
|
||||||
@@ -35,15 +35,14 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
|||||||
var stream = accelerator.CreateStream();
|
var stream = accelerator.CreateStream();
|
||||||
var kernel = GetPow2ModKernel(accelerator);
|
var kernel = GetPow2ModKernel(accelerator);
|
||||||
var resources = RentResources(accelerator, GpuConstants.ScanBatchSize);
|
var resources = RentResources(accelerator, GpuConstants.ScanBatchSize);
|
||||||
var gpuKernels = GpuKernelPool.GetKernels();
|
|
||||||
|
|
||||||
var orderBuffer = resources.OrderBuffer;
|
var orderBuffer = resources.OrderBuffer;
|
||||||
ulong[] orderArray = resources.OrderArray;
|
ulong[] orderArray = resources.OrderArray;
|
||||||
int batchSize = resources.Capacity;
|
int batchSize = resources.Capacity;
|
||||||
|
|
||||||
// Ensure device has small cycles and primes tables for in-kernel lookup
|
// Ensure device has small cycles and primes tables for in-kernel lookup
|
||||||
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(gpuKernels, accelerator);
|
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
|
||||||
ResiduePrimeViews primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(gpuKernels, accelerator);
|
ResiduePrimeViews primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(accelerator);
|
||||||
|
|
||||||
UInt128 kStart = 1UL;
|
UInt128 kStart = 1UL;
|
||||||
UInt128 limit = maxK + UInt128.One;
|
UInt128 limit = maxK + UInt128.One;
|
||||||
@@ -110,9 +109,8 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
|||||||
ReturnResources(accelerator, resources);
|
ReturnResources(accelerator, resources);
|
||||||
stream.Dispose();
|
stream.Dispose();
|
||||||
// Monitor.Exit(gpuLease.ExecutionLock);
|
// Monitor.Exit(gpuLease.ExecutionLock);
|
||||||
gpuKernels.Dispose();
|
|
||||||
ReturnAccelerator(gpuLease);
|
ReturnAccelerator(gpuLease);
|
||||||
GpuPrimeWorkLimiter.Release();
|
limiter.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> GetPow2ModKernel(Accelerator accelerator) =>
|
private Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> GetPow2ModKernel(Accelerator accelerator) =>
|
||||||
|
|||||||
@@ -18,11 +18,6 @@ internal enum GpuPow2ModStatus
|
|||||||
|
|
||||||
internal static partial class PrimeOrderGpuHeuristics
|
internal static partial class PrimeOrderGpuHeuristics
|
||||||
{
|
{
|
||||||
internal static void PreloadStaticTables(Accelerator accelerator)
|
|
||||||
{
|
|
||||||
_ = GetSmallPrimeDeviceCache(accelerator);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<ulong, byte> OverflowedPrimes = new();
|
private static readonly ConcurrentDictionary<ulong, byte> OverflowedPrimes = new();
|
||||||
private static readonly ConcurrentDictionary<UInt128, byte> OverflowedPrimesWide = new();
|
private static readonly ConcurrentDictionary<UInt128, byte> OverflowedPrimesWide = new();
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>> Pow2ModKernelCache = new();
|
private static readonly ConcurrentDictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>> Pow2ModKernelCache = new();
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using ILGPU;
|
|
||||||
using ILGPU.Runtime;
|
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
|
||||||
|
|
||||||
internal static class SharedGpuContext
|
|
||||||
{
|
|
||||||
private static readonly Lazy<Context> s_context = new(() => ILGPU.Context.CreateDefault(), LazyThreadSafetyMode.ExecutionAndPublication);
|
|
||||||
private static readonly Lazy<Device> s_device = new(() => s_context.Value.GetPreferredDevice(false), LazyThreadSafetyMode.ExecutionAndPublication);
|
|
||||||
private static readonly Lazy<Accelerator> s_accelerator = new(() => s_device.Value.CreateAccelerator(s_context.Value), LazyThreadSafetyMode.ExecutionAndPublication);
|
|
||||||
|
|
||||||
internal static Context Context => s_context.Value;
|
|
||||||
|
|
||||||
internal static Device Device => s_device.Value;
|
|
||||||
|
|
||||||
internal static Accelerator Accelerator => s_accelerator.Value;
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Open.Numeric.Primes;
|
using Open.Numeric.Primes;
|
||||||
|
using System;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
@@ -224,13 +225,14 @@ 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 limiter = GpuPrimeWorkLimiter.Acquire();
|
||||||
var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(1);
|
var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(nMod10, 1, PrimeTester.PrimeTesterGpuContextPool.HeuristicGpuTableScope.GroupABWithCombined);
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = gpu.Accelerator;
|
||||||
var kernel = PrimeTester.PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.HeuristicTrialDivisionKernel;
|
var kernel = gpu.HeuristicTrialDivisionKernel;
|
||||||
var flagView1D = gpu.HeuristicFlag.View;
|
var flagView1D = gpu.HeuristicFlag.View;
|
||||||
var flagView = flagView1D.AsContiguous();
|
var flagView = flagView1D.AsContiguous();
|
||||||
|
|
||||||
|
ulong maxDivisor = ComputeMaxDivisorLimit(maxDivisorSquare);
|
||||||
bool compositeDetected = false;
|
bool compositeDetected = false;
|
||||||
int divisorLength = GetCombinedDivisors(nMod10).Length;
|
int divisorLength = GetCombinedDivisors(nMod10).Length;
|
||||||
|
|
||||||
@@ -240,20 +242,38 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
divisorLength,
|
divisorLength,
|
||||||
flagView,
|
flagView,
|
||||||
n,
|
n,
|
||||||
maxDivisorSquare,
|
maxDivisor,
|
||||||
HeuristicGpuDivisorTableKind.Combined,
|
HeuristicGpuDivisorTableKind.Combined,
|
||||||
gpu.HeuristicGpuTables);
|
gpu.HeuristicGpuTables,
|
||||||
|
gpu.HeuristicCombinedTables);
|
||||||
accelerator.Synchronize();
|
accelerator.Synchronize();
|
||||||
flagView1D.CopyToCPU(ref compositeFlag, 1);
|
flagView1D.CopyToCPU(ref compositeFlag, 1);
|
||||||
compositeDetected = compositeFlag != 0;
|
compositeDetected = compositeFlag != 0;
|
||||||
|
|
||||||
gpu.Dispose();
|
gpu.Dispose();
|
||||||
GpuPrimeWorkLimiter.Release();
|
limiter.Dispose();
|
||||||
return compositeDetected;
|
return compositeDetected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|
||||||
|
private static ulong ComputeMaxDivisorLimit(ulong maxDivisorSquare)
|
||||||
|
{
|
||||||
|
ulong maxDivisor = (ulong)Math.Sqrt(maxDivisorSquare);
|
||||||
|
|
||||||
|
while ((maxDivisor + 1) != 0 && (maxDivisor + 1) <= maxDivisorSquare / (maxDivisor + 1))
|
||||||
|
{
|
||||||
|
maxDivisor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (maxDivisor != 0 && maxDivisor > maxDivisorSquare / maxDivisor)
|
||||||
|
{
|
||||||
|
maxDivisor--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxDivisor;
|
||||||
|
}
|
||||||
private static bool EvaluateWithOpenNumericFallback(ulong n)
|
private static bool EvaluateWithOpenNumericFallback(ulong n)
|
||||||
{
|
{
|
||||||
return Prime.Numbers.IsPrime(n);
|
return Prime.Numbers.IsPrime(n);
|
||||||
|
|||||||
@@ -240,13 +240,14 @@ public sealed class HeuristicPrimeTester
|
|||||||
|
|
||||||
private bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10)
|
private bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10)
|
||||||
{
|
{
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
var limiter = GpuPrimeWorkLimiter.Acquire();
|
||||||
var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(1);
|
var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(nMod10, 1, PrimeTester.PrimeTesterGpuContextPool.HeuristicGpuTableScope.GroupAB);
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = gpu.Accelerator;
|
||||||
var kernel = PrimeTester.PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.HeuristicTrialDivisionKernel;
|
var kernel = gpu.HeuristicTrialDivisionKernel;
|
||||||
var flagView1D = gpu.HeuristicFlag.View;
|
var flagView1D = gpu.HeuristicFlag.View;
|
||||||
var flagView = flagView1D.AsContiguous();
|
var flagView = flagView1D.AsContiguous();
|
||||||
|
|
||||||
|
ulong maxDivisor = ComputeMaxDivisorLimit(maxDivisorSquare);
|
||||||
bool compositeDetected = false;
|
bool compositeDetected = false;
|
||||||
int compositeFlag = 0;
|
int compositeFlag = 0;
|
||||||
|
|
||||||
@@ -257,9 +258,10 @@ public sealed class HeuristicPrimeTester
|
|||||||
groupALength,
|
groupALength,
|
||||||
flagView,
|
flagView,
|
||||||
n,
|
n,
|
||||||
maxDivisorSquare,
|
maxDivisor,
|
||||||
HeuristicGpuDivisorTableKind.GroupA,
|
HeuristicGpuDivisorTableKind.GroupA,
|
||||||
gpu.HeuristicGpuTables);
|
gpu.HeuristicGpuTables,
|
||||||
|
gpu.HeuristicCombinedTables);
|
||||||
accelerator.Synchronize();
|
accelerator.Synchronize();
|
||||||
flagView1D.CopyToCPU(ref compositeFlag, 1);
|
flagView1D.CopyToCPU(ref compositeFlag, 1);
|
||||||
compositeDetected = compositeFlag != 0;
|
compositeDetected = compositeFlag != 0;
|
||||||
@@ -291,9 +293,10 @@ public sealed class HeuristicPrimeTester
|
|||||||
divisorLength,
|
divisorLength,
|
||||||
flagView,
|
flagView,
|
||||||
n,
|
n,
|
||||||
maxDivisorSquare,
|
maxDivisor,
|
||||||
tableKind,
|
tableKind,
|
||||||
gpu.HeuristicGpuTables);
|
gpu.HeuristicGpuTables,
|
||||||
|
gpu.HeuristicCombinedTables);
|
||||||
accelerator.Synchronize();
|
accelerator.Synchronize();
|
||||||
flagView1D.CopyToCPU(ref compositeFlag, 1);
|
flagView1D.CopyToCPU(ref compositeFlag, 1);
|
||||||
compositeDetected = compositeFlag != 0;
|
compositeDetected = compositeFlag != 0;
|
||||||
@@ -301,7 +304,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
}
|
}
|
||||||
|
|
||||||
gpu.Dispose();
|
gpu.Dispose();
|
||||||
GpuPrimeWorkLimiter.Release();
|
limiter.Dispose();
|
||||||
return compositeDetected;
|
return compositeDetected;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,6 +315,23 @@ public sealed class HeuristicPrimeTester
|
|||||||
{
|
{
|
||||||
return Prime.Numbers.IsPrime(n);
|
return Prime.Numbers.IsPrime(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ulong ComputeMaxDivisorLimit(ulong maxDivisorSquare)
|
||||||
|
{
|
||||||
|
ulong maxDivisor = (ulong)Math.Sqrt(maxDivisorSquare);
|
||||||
|
|
||||||
|
while ((maxDivisor + 1) != 0 && (maxDivisor + 1) <= maxDivisorSquare / (maxDivisor + 1))
|
||||||
|
{
|
||||||
|
maxDivisor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (maxDivisor != 0 && maxDivisor > maxDivisorSquare / maxDivisor)
|
||||||
|
{
|
||||||
|
maxDivisor--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxDivisor;
|
||||||
|
}
|
||||||
internal static ulong ComputeHeuristicDivisorSquareLimit(ulong n)
|
internal static ulong ComputeHeuristicDivisorSquareLimit(ulong n)
|
||||||
{
|
{
|
||||||
return n;
|
return n;
|
||||||
|
|||||||
@@ -412,7 +412,7 @@ public class MersenneDivisorCycles
|
|||||||
// Console.WriteLine($"MersenneDivisorCycles.TryFactorIntoCountsInternal hits {Volatile.Read(ref _tryFactorIntoCountsInternalHits)}");
|
// Console.WriteLine($"MersenneDivisorCycles.TryFactorIntoCountsInternal hits {Volatile.Read(ref _tryFactorIntoCountsInternalHits)}");
|
||||||
|
|
||||||
// HeuristicPrimeTester primeTester = _primeTester ??= new();
|
// HeuristicPrimeTester primeTester = _primeTester ??= new();
|
||||||
if (PrimeTester.IsPrimeGpu(remaining))
|
if (PrimeTester.IsPrime(remaining))
|
||||||
// if (PrimeTester.IsPrimeCpu(remaining, CancellationToken.None))
|
// if (PrimeTester.IsPrimeCpu(remaining, CancellationToken.None))
|
||||||
{
|
{
|
||||||
AddFactor(counts, remaining);
|
AddFactor(counts, remaining);
|
||||||
@@ -943,7 +943,7 @@ public class MersenneDivisorCycles
|
|||||||
// HeuristicPrimeTester primeTester = _primeTester ??= new();
|
// HeuristicPrimeTester primeTester = _primeTester ??= new();
|
||||||
// if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.Exclusive.IsPrimeCpu(divisor, CancellationToken.None))
|
// if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.Exclusive.IsPrimeCpu(divisor, CancellationToken.None))
|
||||||
// if (!skipPrimeOrderHeuristic && primeTester.IsPrimeGpu(divisor))
|
// if (!skipPrimeOrderHeuristic && primeTester.IsPrimeGpu(divisor))
|
||||||
if (!skipPrimeOrderHeuristic && PrimeTester.IsPrimeGpu(divisor))
|
if (!skipPrimeOrderHeuristic && PrimeTester.IsPrime(divisor))
|
||||||
{
|
{
|
||||||
ulong computedOrder = PrimeOrderCalculator.Calculate(
|
ulong computedOrder = PrimeOrderCalculator.Calculate(
|
||||||
divisor,
|
divisor,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
@@ -107,7 +108,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return candidateOrder;
|
return candidateOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private static ulong specialMaxHits;
|
|
||||||
private static bool TrySpecialMaxCpu(ulong phi, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData)
|
private static bool TrySpecialMaxCpu(ulong phi, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData)
|
||||||
{
|
{
|
||||||
int length = factors.Count;
|
int length = factors.Count;
|
||||||
@@ -120,16 +121,16 @@ internal static partial class PrimeOrderCalculator
|
|||||||
|
|
||||||
ReadOnlySpan<FactorEntry> factorSpan = new(factors.Factors, 0, length);
|
ReadOnlySpan<FactorEntry> factorSpan = new(factors.Factors, 0, length);
|
||||||
|
|
||||||
Span<ulong> stackBuffer = stackalloc ulong[length];
|
if (length <= 32)
|
||||||
|
|
||||||
if (length <= 7)
|
|
||||||
{
|
{
|
||||||
|
Span<ulong> stackBuffer = stackalloc ulong[length];
|
||||||
return EvaluateSpecialMaxCandidates(stackBuffer, factorSpan, phi, prime, divisorData);
|
return EvaluateSpecialMaxCandidates(stackBuffer, factorSpan, phi, prime, divisorData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Atomic.Add(ref specialMaxHits, 1);
|
ulong[] rented = ThreadStaticPools.UlongPool.Rent(length);
|
||||||
// Console.WriteLine($"Special max GPU hit {specialMaxHits} ({length})");
|
bool result = EvaluateSpecialMaxCandidates(rented.AsSpan(0, length), factorSpan, phi, prime, divisorData);
|
||||||
return EvaluateSpecialMaxCandidatesGpu(stackBuffer, factorSpan, phi, prime, divisorData);
|
ThreadStaticPools.UlongPool.Return(rented, clearArray: false);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool EvaluateSpecialMaxCandidates(Span<ulong> buffer, ReadOnlySpan<FactorEntry> factors, ulong phi, ulong prime, in MontgomeryDivisorData divisorData)
|
private static bool EvaluateSpecialMaxCandidates(Span<ulong> buffer, ReadOnlySpan<FactorEntry> factors, ulong phi, ulong prime, in MontgomeryDivisorData divisorData)
|
||||||
@@ -1193,7 +1194,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// Console.WriteLine($"Partial factor pending hits {Volatile.Read(ref _partialFactorPendingHits)}");
|
// Console.WriteLine($"Partial factor pending hits {Volatile.Read(ref _partialFactorPendingHits)}");
|
||||||
|
|
||||||
// bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None);
|
// bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None);
|
||||||
bool isPrime = PrimeTester.IsPrimeGpu(composite);
|
bool isPrime = PrimeTester.IsPrime(composite);
|
||||||
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
|
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
|
||||||
|
|
||||||
entry = entry.WithPrimality(isPrime);
|
entry = entry.WithPrimality(isPrime);
|
||||||
@@ -1230,7 +1231,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// Atomic.Add(ref _partialFactorCofactorHits, 1UL);
|
// Atomic.Add(ref _partialFactorCofactorHits, 1UL);
|
||||||
// Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}");
|
// Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}");
|
||||||
|
|
||||||
cofactorIsPrime = PrimeTester.IsPrimeGpu(cofactor);
|
cofactorIsPrime = PrimeTester.IsPrime(cofactor);
|
||||||
// cofactorIsPrime = PrimeTester.IsPrimeGpu(cofactor);
|
// cofactorIsPrime = PrimeTester.IsPrimeGpu(cofactor);
|
||||||
// cofactorIsPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(cofactor);
|
// cofactorIsPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(cofactor);
|
||||||
}
|
}
|
||||||
@@ -1700,7 +1701,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// HeuristicPrimeTester tester = _tester ??= new();
|
// HeuristicPrimeTester tester = _tester ??= new();
|
||||||
|
|
||||||
// bool isPrime = primeTester.HeuristicIsPrimeGpu(value);
|
// bool isPrime = primeTester.HeuristicIsPrimeGpu(value);
|
||||||
bool isPrime = PrimeTester.IsPrimeGpu(value);
|
bool isPrime = PrimeTester.IsPrime(value);
|
||||||
// bool isPrime = PrimeTester.IsPrimeGpu(value);
|
// bool isPrime = PrimeTester.IsPrimeGpu(value);
|
||||||
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(value);
|
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(value);
|
||||||
if (!knownComposite && isPrime)
|
if (!knownComposite && isPrime)
|
||||||
@@ -1716,7 +1717,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// Console.WriteLine($"Factor completely after PollardRho hits {Volatile.Read(ref _factorCompletelyPollardRhoHits)}");
|
// Console.WriteLine($"Factor completely after PollardRho hits {Volatile.Read(ref _factorCompletelyPollardRhoHits)}");
|
||||||
|
|
||||||
// isPrime = primeTester.HeuristicIsPrimeGpu(factor);
|
// isPrime = primeTester.HeuristicIsPrimeGpu(factor);
|
||||||
isPrime = PrimeTester.IsPrimeGpu(factor);
|
isPrime = PrimeTester.IsPrime(factor);
|
||||||
// isPrime = PrimeTester.IsPrimeGpu(factor);
|
// isPrime = PrimeTester.IsPrimeGpu(factor);
|
||||||
// isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(factor);
|
// isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(factor);
|
||||||
if (isPrime)
|
if (isPrime)
|
||||||
@@ -1753,7 +1754,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// Atomic.Add(ref _partialFactorCofactorHits, 1UL);
|
// Atomic.Add(ref _partialFactorCofactorHits, 1UL);
|
||||||
// Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}");
|
// Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}");
|
||||||
|
|
||||||
isPrime = PrimeTester.IsPrimeGpu(quotient);
|
isPrime = PrimeTester.IsPrime(quotient);
|
||||||
// isPrime = PrimeTester.IsPrimeGpu(quotient);
|
// isPrime = PrimeTester.IsPrimeGpu(quotient);
|
||||||
// isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(quotient);
|
// isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(quotient);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ internal static partial class PrimeOrderCalculator
|
|||||||
Accelerator accelerator = lease.Accelerator;
|
Accelerator accelerator = lease.Accelerator;
|
||||||
AcceleratorStream stream = lease.Stream;
|
AcceleratorStream stream = lease.Stream;
|
||||||
|
|
||||||
SmallPrimeFactorTables tables = GpuKernelPool.EnsureSmallPrimeFactorTables(lease.Kernels, accelerator);
|
SmallPrimeFactorTables tables = GpuKernelPool.EnsureSmallPrimeFactorTables(accelerator);
|
||||||
SmallPrimeFactorScratch scratch = GpuKernelPool.EnsureSmallPrimeFactorScratch(lease.Kernels, accelerator, GpuSmallPrimeFactorSlots);
|
SmallPrimeFactorScratch scratch = GpuKernelPool.EnsureSmallPrimeFactorScratch(accelerator, GpuSmallPrimeFactorSlots);
|
||||||
|
|
||||||
var kernel = lease.SmallPrimeFactorKernel;
|
var kernel = lease.SmallPrimeFactorKernel;
|
||||||
kernel(
|
kernel(
|
||||||
@@ -68,52 +68,4 @@ internal static partial class PrimeOrderCalculator
|
|||||||
ThreadStaticPools.IntPool.Return(exponentBufferArray);
|
ThreadStaticPools.IntPool.Return(exponentBufferArray);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool EvaluateSpecialMaxCandidatesGpu(
|
|
||||||
Span<ulong> buffer,
|
|
||||||
ReadOnlySpan<FactorEntry> factors,
|
|
||||||
ulong phi,
|
|
||||||
ulong prime,
|
|
||||||
in MontgomeryDivisorData divisorData)
|
|
||||||
{
|
|
||||||
if (factors.Length == 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int factorCount = factors.Length;
|
|
||||||
Span<ulong> factorSpan = buffer[..factorCount];
|
|
||||||
for (int i = 0; i < factorCount; i++)
|
|
||||||
{
|
|
||||||
factorSpan[i] = factors[i].Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
|
||||||
Accelerator accelerator = lease.Accelerator;
|
|
||||||
AcceleratorStream stream = lease.Stream;
|
|
||||||
|
|
||||||
SpecialMaxScratch scratch = GpuKernelPool.EnsureSpecialMaxScratch(lease.Kernels, accelerator, factorCapacity: factorCount);
|
|
||||||
|
|
||||||
scratch.FactorsView.SubView(0, factorCount).CopyFromCPU(ref MemoryMarshal.GetReference(factorSpan), factorCount);
|
|
||||||
|
|
||||||
var kernel = lease.SpecialMaxKernel;
|
|
||||||
kernel(
|
|
||||||
stream,
|
|
||||||
1,
|
|
||||||
phi,
|
|
||||||
scratch.FactorsView,
|
|
||||||
factorCount,
|
|
||||||
divisorData,
|
|
||||||
scratch.CandidatesView,
|
|
||||||
scratch.ResultView);
|
|
||||||
|
|
||||||
stream.Synchronize();
|
|
||||||
|
|
||||||
ushort result = 0;
|
|
||||||
scratch.ResultView.CopyToCPU(ref result, 1);
|
|
||||||
lease.Dispose();
|
|
||||||
|
|
||||||
return result != 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+410
-287
@@ -93,43 +93,50 @@ public sealed class PrimeTester
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool IsPrimeGpu(ulong n)
|
public bool IsPrimeGpu(ulong n)
|
||||||
{
|
{
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
// bool belowGpuRange = n < 31UL;
|
||||||
var gpu = PrimeTesterGpuContextPool.Rent(1);
|
|
||||||
var accelerator = gpu.Accelerator;
|
|
||||||
var kernel = PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.Kernel;
|
|
||||||
|
|
||||||
ulong value = n;
|
// if (belowGpuRange)
|
||||||
byte flag = 0;
|
// {
|
||||||
|
return IsPrime(n);
|
||||||
|
// }
|
||||||
|
|
||||||
var input = gpu.Input;
|
// var limiter = GpuPrimeWorkLimiter.Acquire();
|
||||||
var output = gpu.Output;
|
// var gpu = PrimeTesterGpuContextPool.Rent(1);
|
||||||
var inputView = input.View;
|
// var accelerator = gpu.Accelerator;
|
||||||
var outputView = output.View;
|
// var kernel = gpu.Kernel;
|
||||||
|
|
||||||
inputView.CopyFromCPU(ref value, 1);
|
// ulong value = n;
|
||||||
kernel(
|
// byte flag = 0;
|
||||||
1,
|
|
||||||
inputView,
|
|
||||||
gpu.DevicePrimesDefault.View,
|
|
||||||
gpu.DevicePrimesLastOne.View,
|
|
||||||
gpu.DevicePrimesLastSeven.View,
|
|
||||||
gpu.DevicePrimesLastThree.View,
|
|
||||||
gpu.DevicePrimesLastNine.View,
|
|
||||||
gpu.DevicePrimesPow2Default.View,
|
|
||||||
gpu.DevicePrimesPow2LastOne.View,
|
|
||||||
gpu.DevicePrimesPow2LastSeven.View,
|
|
||||||
gpu.DevicePrimesPow2LastThree.View,
|
|
||||||
gpu.DevicePrimesPow2LastNine.View,
|
|
||||||
outputView);
|
|
||||||
accelerator.Synchronize();
|
|
||||||
outputView.CopyToCPU(ref flag, 1);
|
|
||||||
|
|
||||||
gpu.Dispose();
|
// var input = gpu.Input;
|
||||||
GpuPrimeWorkLimiter.Release();
|
// var output = gpu.Output;
|
||||||
|
// var inputView = input.View;
|
||||||
|
// var outputView = output.View;
|
||||||
|
|
||||||
return flag != 0;
|
// inputView.CopyFromCPU(ref value, 1);
|
||||||
|
// kernel(
|
||||||
|
// 1,
|
||||||
|
// inputView,
|
||||||
|
// gpu.DevicePrimesDefault.View,
|
||||||
|
// gpu.DevicePrimesLastOne.View,
|
||||||
|
// gpu.DevicePrimesLastSeven.View,
|
||||||
|
// gpu.DevicePrimesLastThree.View,
|
||||||
|
// gpu.DevicePrimesLastNine.View,
|
||||||
|
// gpu.DevicePrimesPow2Default.View,
|
||||||
|
// gpu.DevicePrimesPow2LastOne.View,
|
||||||
|
// gpu.DevicePrimesPow2LastSeven.View,
|
||||||
|
// gpu.DevicePrimesPow2LastThree.View,
|
||||||
|
// gpu.DevicePrimesPow2LastNine.View,
|
||||||
|
// outputView);
|
||||||
|
// accelerator.Synchronize();
|
||||||
|
// outputView.CopyToCPU(ref flag, 1);
|
||||||
|
|
||||||
|
// gpu.Dispose();
|
||||||
|
// limiter.Dispose();
|
||||||
|
|
||||||
|
// return flag != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GpuBatchSize { get; set; } = 262_144;
|
public static int GpuBatchSize { get; set; } = 262_144;
|
||||||
@@ -152,7 +159,26 @@ public sealed class PrimeTester
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PrimeTesterGpuContextPool.WarmUp(target, GpuBatchSize);
|
int toWarm = target - WarmedGpuLeaseCount;
|
||||||
|
var leases = new PrimeTesterGpuContextPool.PrimeTesterGpuContextLease[toWarm];
|
||||||
|
ReadOnlySpan<byte> supportedDigits = PrimeTesterGpuContextPool.SupportedDigits;
|
||||||
|
int digitIndex = 0;
|
||||||
|
for (int i = 0; i < toWarm; i++)
|
||||||
|
{
|
||||||
|
byte trailingDigit = supportedDigits[digitIndex];
|
||||||
|
leases[i] = PrimeTesterGpuContextPool.Rent(trailingDigit, GpuBatchSize);
|
||||||
|
digitIndex++;
|
||||||
|
if (digitIndex == supportedDigits.Length)
|
||||||
|
{
|
||||||
|
digitIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < toWarm; i++)
|
||||||
|
{
|
||||||
|
leases[i].Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
WarmedGpuLeaseCount = target;
|
WarmedGpuLeaseCount = target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,25 +186,45 @@ public sealed class PrimeTester
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void IsPrimeBatchGpu(ReadOnlySpan<ulong> values, Span<byte> results)
|
public static void IsPrimeBatchGpu(ReadOnlySpan<ulong> values, Span<byte> results)
|
||||||
{
|
{
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
var limiter = GpuPrimeWorkLimiter.Acquire();
|
||||||
var gpu = PrimeTesterGpuContextPool.Rent(GpuBatchSize);
|
|
||||||
var accelerator = gpu.Accelerator;
|
|
||||||
var kernel = PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.Kernel;
|
|
||||||
int totalLength = values.Length;
|
int totalLength = values.Length;
|
||||||
int batchSize = GpuBatchSize;
|
int batchSize = GpuBatchSize;
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
while (pos < totalLength)
|
||||||
|
{
|
||||||
|
ulong currentValue = values[pos];
|
||||||
|
byte trailingDigit = (byte)(currentValue % 10UL);
|
||||||
|
var gpu = PrimeTesterGpuContextPool.Rent(trailingDigit, GpuBatchSize);
|
||||||
|
var accelerator = gpu.Accelerator;
|
||||||
|
var kernel = gpu.Kernel;
|
||||||
var input = gpu.Input;
|
var input = gpu.Input;
|
||||||
var output = gpu.Output;
|
var output = gpu.Output;
|
||||||
var inputView = input.View;
|
var inputView = input.View;
|
||||||
var outputView = output.View;
|
var outputView = output.View;
|
||||||
|
|
||||||
int pos = 0;
|
int blockStart = pos;
|
||||||
while (pos < totalLength)
|
while (blockStart < totalLength)
|
||||||
{
|
{
|
||||||
int remaining = totalLength - pos;
|
byte blockDigit = (byte)(values[blockStart] % 10UL);
|
||||||
int count = remaining > batchSize ? batchSize : remaining;
|
if (blockDigit != trailingDigit)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
var valueSlice = values.Slice(pos, count);
|
int limit = Math.Min(batchSize, totalLength - blockStart);
|
||||||
|
int count = 0;
|
||||||
|
while (count < limit && (byte)(values[blockStart + count] % 10UL) == trailingDigit)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var valueSlice = values.Slice(blockStart, count);
|
||||||
ref ulong valueRef = ref MemoryMarshal.GetReference(valueSlice);
|
ref ulong valueRef = ref MemoryMarshal.GetReference(valueSlice);
|
||||||
inputView.CopyFromCPU(ref valueRef, count);
|
inputView.CopyFromCPU(ref valueRef, count);
|
||||||
|
|
||||||
@@ -197,94 +243,77 @@ public sealed class PrimeTester
|
|||||||
gpu.DevicePrimesPow2LastNine.View,
|
gpu.DevicePrimesPow2LastNine.View,
|
||||||
outputView);
|
outputView);
|
||||||
accelerator.Synchronize();
|
accelerator.Synchronize();
|
||||||
var resultSlice = results.Slice(pos, count);
|
var resultSlice = results.Slice(blockStart, count);
|
||||||
ref byte resultRef = ref MemoryMarshal.GetReference(resultSlice);
|
ref byte resultRef = ref MemoryMarshal.GetReference(resultSlice);
|
||||||
outputView.CopyToCPU(ref resultRef, count);
|
outputView.CopyToCPU(ref resultRef, count);
|
||||||
|
|
||||||
pos += count;
|
blockStart += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu.Dispose();
|
gpu.Dispose();
|
||||||
GpuPrimeWorkLimiter.Release();
|
pos = blockStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
limiter.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static class PrimeTesterGpuContextPool
|
internal static class PrimeTesterGpuContextPool
|
||||||
{
|
{
|
||||||
private static readonly ConcurrentQueue<PrimeTesterGpuContextLease> Pool = new();
|
internal enum HeuristicGpuTableScope : byte
|
||||||
private static readonly object WarmUpLock = new();
|
{
|
||||||
private static int WarmedLeaseCount;
|
GroupAB = 0,
|
||||||
|
GroupABWithCombined = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly byte[] SupportedTrailingDigits = [1, 3, 7, 9];
|
||||||
|
private const int ScopeCount = 2;
|
||||||
|
private static readonly ConcurrentQueue<PrimeTesterGpuContextLease>[,] Pools;
|
||||||
private static readonly object SharedTablesLock = new();
|
private static readonly object SharedTablesLock = new();
|
||||||
private static readonly Dictionary<Accelerator, SharedHeuristicGpuTables> SharedTables = new(AcceleratorReferenceComparer.Instance);
|
private static readonly Dictionary<string, SharedKernelTables> SharedTables = new();
|
||||||
|
private static readonly Dictionary<(string Key, byte Digit, HeuristicGpuTableScope Scope), TrailingDigitTables> DigitTables = new();
|
||||||
|
|
||||||
private static SharedHeuristicGpuTables RentSharedTables(Accelerator accelerator)
|
static PrimeTesterGpuContextPool()
|
||||||
{
|
{
|
||||||
lock (SharedTablesLock)
|
Pools = new ConcurrentQueue<PrimeTesterGpuContextLease>[SupportedTrailingDigits.Length, ScopeCount];
|
||||||
|
for (int digitIndex = 0; digitIndex < SupportedTrailingDigits.Length; digitIndex++)
|
||||||
{
|
{
|
||||||
if (!SharedTables.TryGetValue(accelerator, out var tables))
|
for (int scopeIndex = 0; scopeIndex < ScopeCount; scopeIndex++)
|
||||||
{
|
{
|
||||||
tables = new SharedHeuristicGpuTables(accelerator);
|
Pools[digitIndex, scopeIndex] = new ConcurrentQueue<PrimeTesterGpuContextLease>();
|
||||||
SharedTables.Add(accelerator, tables);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tables.AddReference();
|
|
||||||
return tables;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReleaseSharedTables(SharedHeuristicGpuTables tables)
|
internal static ReadOnlySpan<byte> SupportedDigits => SupportedTrailingDigits;
|
||||||
|
|
||||||
|
private static int GetPoolIndex(byte trailingDigit)
|
||||||
{
|
{
|
||||||
if (tables is null)
|
return trailingDigit switch
|
||||||
{
|
{
|
||||||
return;
|
1 => 0,
|
||||||
|
3 => 1,
|
||||||
|
7 => 2,
|
||||||
|
9 => 3,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(trailingDigit), trailingDigit, "Only trailing digits 1, 3, 7, and 9 are supported."),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (SharedTablesLock)
|
private static int GetScopeIndex(HeuristicGpuTableScope scope)
|
||||||
{
|
{
|
||||||
tables.Release();
|
return scope switch
|
||||||
}
|
{
|
||||||
|
HeuristicGpuTableScope.GroupAB => 0,
|
||||||
|
HeuristicGpuTableScope.GroupABWithCombined => 1,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(scope), scope, "Unsupported heuristic table scope."),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void EnsureStaticTables(KernelContainer kernels, Accelerator accelerator)
|
internal static PrimeTesterGpuContextLease Rent(byte trailingDigit, int minBufferCapacity = 1, HeuristicGpuTableScope tableScope = HeuristicGpuTableScope.GroupAB)
|
||||||
{
|
{
|
||||||
var tables = RentSharedTables(accelerator);
|
var pool = Pools[GetPoolIndex(trailingDigit), GetScopeIndex(tableScope)];
|
||||||
var heuristicTables = tables.CreateHeuristicDivisorTables();
|
if (!pool.TryDequeue(out var lease))
|
||||||
HeuristicGpuDivisorTables.InitializeShared(in heuristicTables);
|
|
||||||
ReleaseSharedTables(tables);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void WarmUp(int target, int minBufferCapacity)
|
|
||||||
{
|
{
|
||||||
if (target <= 0)
|
lease = new PrimeTesterGpuContextLease(trailingDigit, minBufferCapacity, tableScope);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (WarmUpLock)
|
|
||||||
{
|
|
||||||
if (target <= WarmedLeaseCount)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KernelContainer kernels = GpuKernelPool.GetKernels();
|
|
||||||
GpuStaticTableInitializer.EnsureStaticTables(kernels, SharedGpuContext.Accelerator);
|
|
||||||
|
|
||||||
int toCreate = target - WarmedLeaseCount;
|
|
||||||
for (int i = 0; i < toCreate; i++)
|
|
||||||
{
|
|
||||||
Pool.Enqueue(new PrimeTesterGpuContextLease(minBufferCapacity));
|
|
||||||
}
|
|
||||||
|
|
||||||
kernels.Dispose();
|
|
||||||
WarmedLeaseCount = target;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static PrimeTesterGpuContextLease Rent(int minBufferCapacity = 1)
|
|
||||||
{
|
|
||||||
if (!Pool.TryDequeue(out var lease))
|
|
||||||
{
|
|
||||||
lease = new PrimeTesterGpuContextLease(minBufferCapacity);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -295,33 +324,42 @@ public sealed class PrimeTester
|
|||||||
return lease;
|
return lease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal static void Return(PrimeTesterGpuContextLease lease)
|
internal static void Return(PrimeTesterGpuContextLease lease)
|
||||||
{
|
{
|
||||||
lease.Accelerator.Synchronize();
|
lease.Accelerator.Synchronize();
|
||||||
Pool.Enqueue(lease);
|
Pools[GetPoolIndex(lease.TrailingDigit), GetScopeIndex(lease.TableScope)].Enqueue(lease);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void DisposeAll()
|
internal static void DisposeAll()
|
||||||
{
|
{
|
||||||
while (Pool.TryDequeue(out var lease))
|
for (int digitIndex = 0; digitIndex < SupportedTrailingDigits.Length; digitIndex++)
|
||||||
|
{
|
||||||
|
for (int scopeIndex = 0; scopeIndex < ScopeCount; scopeIndex++)
|
||||||
|
{
|
||||||
|
var pool = Pools[digitIndex, scopeIndex];
|
||||||
|
while (pool.TryDequeue(out var lease))
|
||||||
{
|
{
|
||||||
lease.DisposeResources();
|
lease.DisposeResources();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lock (SharedTablesLock)
|
lock (SharedTablesLock)
|
||||||
{
|
{
|
||||||
|
foreach (var entry in DigitTables.Values)
|
||||||
|
{
|
||||||
|
entry.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
DigitTables.Clear();
|
||||||
|
|
||||||
foreach (var entry in SharedTables.Values)
|
foreach (var entry in SharedTables.Values)
|
||||||
{
|
{
|
||||||
entry.Dispose();
|
entry.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedTables.Clear();
|
SharedTables.Clear();
|
||||||
var emptyTables = default(HeuristicGpuDivisorTables);
|
|
||||||
HeuristicGpuDivisorTables.InitializeShared(in emptyTables);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WarmedLeaseCount = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Clear(Accelerator accelerator)
|
internal static void Clear(Accelerator accelerator)
|
||||||
@@ -331,10 +369,13 @@ public sealed class PrimeTester
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (WarmUpLock)
|
|
||||||
{
|
|
||||||
var retained = new List<PrimeTesterGpuContextLease>();
|
var retained = new List<PrimeTesterGpuContextLease>();
|
||||||
while (Pool.TryDequeue(out var lease))
|
for (int digitIndex = 0; digitIndex < SupportedTrailingDigits.Length; digitIndex++)
|
||||||
|
{
|
||||||
|
for (int scopeIndex = 0; scopeIndex < ScopeCount; scopeIndex++)
|
||||||
|
{
|
||||||
|
var pool = Pools[digitIndex, scopeIndex];
|
||||||
|
while (pool.TryDequeue(out var lease))
|
||||||
{
|
{
|
||||||
if (lease.Accelerator == accelerator)
|
if (lease.Accelerator == accelerator)
|
||||||
{
|
{
|
||||||
@@ -349,46 +390,172 @@ public sealed class PrimeTester
|
|||||||
int retainedCount = retained.Count;
|
int retainedCount = retained.Count;
|
||||||
for (int i = 0; i < retainedCount; i++)
|
for (int i = 0; i < retainedCount; i++)
|
||||||
{
|
{
|
||||||
Pool.Enqueue(retained[i]);
|
pool.Enqueue(retained[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
WarmedLeaseCount = Pool.Count;
|
retained.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (SharedTablesLock)
|
lock (SharedTablesLock)
|
||||||
{
|
{
|
||||||
if (SharedTables.Remove(accelerator, out var tables))
|
string key = CreateAcceleratorKey(accelerator);
|
||||||
|
|
||||||
|
if (SharedTables.Remove(key, out var shared))
|
||||||
|
{
|
||||||
|
shared.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DigitTables.Count > 0)
|
||||||
|
{
|
||||||
|
var toRemove = new List<(string Key, byte Digit, HeuristicGpuTableScope Scope)>();
|
||||||
|
foreach (var entry in DigitTables)
|
||||||
|
{
|
||||||
|
if (entry.Key.Key == key)
|
||||||
|
{
|
||||||
|
entry.Value.Dispose();
|
||||||
|
toRemove.Add(entry.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < toRemove.Count; i++)
|
||||||
|
{
|
||||||
|
DigitTables.Remove(toRemove[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CreateAcceleratorKey(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
return accelerator.AcceleratorType + ":" + accelerator.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SharedKernelTables RentSharedTables(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
string key = CreateAcceleratorKey(accelerator);
|
||||||
|
|
||||||
|
lock (SharedTablesLock)
|
||||||
|
{
|
||||||
|
if (!SharedTables.TryGetValue(key, out var tables))
|
||||||
|
{
|
||||||
|
tables = new SharedKernelTables(accelerator, key);
|
||||||
|
SharedTables.Add(key, tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
tables.AddReference();
|
||||||
|
return tables;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReleaseSharedTables(SharedKernelTables tables)
|
||||||
|
{
|
||||||
|
if (tables is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (SharedTablesLock)
|
||||||
|
{
|
||||||
|
if (tables.Release() && SharedTables.Remove(tables.Key, out _))
|
||||||
{
|
{
|
||||||
tables.Dispose();
|
tables.Dispose();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (SharedTables.Count == 0)
|
private static TrailingDigitTables RentTrailingDigitTables(Accelerator accelerator, byte trailingDigit, HeuristicGpuTableScope scope)
|
||||||
{
|
{
|
||||||
var emptyTables = default(HeuristicGpuDivisorTables);
|
string key = CreateAcceleratorKey(accelerator);
|
||||||
HeuristicGpuDivisorTables.InitializeShared(in emptyTables);
|
var compositeKey = (key, trailingDigit, scope);
|
||||||
|
|
||||||
|
lock (SharedTablesLock)
|
||||||
|
{
|
||||||
|
if (!DigitTables.TryGetValue(compositeKey, out var tables))
|
||||||
|
{
|
||||||
|
tables = new TrailingDigitTables(accelerator, key, trailingDigit, scope);
|
||||||
|
DigitTables.Add(compositeKey, tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
tables.AddReference();
|
||||||
|
return tables;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReleaseTrailingDigitTables(TrailingDigitTables tables)
|
||||||
|
{
|
||||||
|
if (tables is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (SharedTablesLock)
|
||||||
|
{
|
||||||
|
var compositeKey = (tables.Key, tables.TrailingDigit, tables.Scope);
|
||||||
|
if (tables.Release() && DigitTables.Remove(compositeKey))
|
||||||
|
{
|
||||||
|
tables.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class PrimeTesterGpuContextLease : IDisposable
|
internal sealed class PrimeTesterGpuContextLease : IDisposable
|
||||||
{
|
{
|
||||||
|
|
||||||
private bool _returned;
|
private bool _returned;
|
||||||
private readonly SharedHeuristicGpuTables _sharedTables;
|
private readonly SharedKernelTables _sharedTables;
|
||||||
|
private readonly TrailingDigitTables _digitTables;
|
||||||
private readonly HeuristicGpuDivisorTables _heuristicDivisorTables;
|
private readonly HeuristicGpuDivisorTables _heuristicDivisorTables;
|
||||||
|
private readonly HeuristicGpuCombinedDivisorTables _heuristicCombinedTables;
|
||||||
|
|
||||||
internal readonly Context AcceleratorContext;
|
internal readonly Context AcceleratorContext;
|
||||||
public readonly Accelerator Accelerator;
|
public readonly Accelerator Accelerator;
|
||||||
|
public readonly byte TrailingDigit;
|
||||||
public static readonly Action<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> Kernel = SharedGpuContext.Accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel);
|
public readonly HeuristicGpuTableScope TableScope;
|
||||||
|
public readonly Action<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> Kernel;
|
||||||
public static readonly Action<Index1D, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables> HeuristicTrialDivisionKernel = SharedGpuContext.Accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables>(PrimeTesterKernels.HeuristicTrialDivisionKernel);
|
public readonly Action<Index1D, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables, HeuristicGpuCombinedDivisorTables> HeuristicTrialDivisionKernel;
|
||||||
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense> Input = null!;
|
public MemoryBuffer1D<ulong, Stride1D.Dense> Input = null!;
|
||||||
public MemoryBuffer1D<byte, Stride1D.Dense> Output = null!;
|
public MemoryBuffer1D<byte, Stride1D.Dense> Output = null!;
|
||||||
public MemoryBuffer1D<int, Stride1D.Dense> HeuristicFlag = null!;
|
public MemoryBuffer1D<int, Stride1D.Dense> HeuristicFlag = null!;
|
||||||
public int BufferCapacity;
|
public int BufferCapacity;
|
||||||
|
|
||||||
|
internal PrimeTesterGpuContextLease(byte trailingDigit, int minBufferCapacity, HeuristicGpuTableScope tableScope)
|
||||||
|
{
|
||||||
|
TrailingDigit = trailingDigit;
|
||||||
|
TableScope = tableScope;
|
||||||
|
|
||||||
|
AcceleratorContext = Context.CreateDefault();
|
||||||
|
Accelerator = AcceleratorContext.GetPreferredDevice(false).CreateAccelerator(AcceleratorContext);
|
||||||
|
Kernel = Accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel);
|
||||||
|
HeuristicTrialDivisionKernel = Accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables, HeuristicGpuCombinedDivisorTables>(PrimeTesterKernels.HeuristicTrialDivisionKernel);
|
||||||
|
|
||||||
|
_sharedTables = RentSharedTables(Accelerator);
|
||||||
|
_digitTables = RentTrailingDigitTables(Accelerator, trailingDigit, tableScope);
|
||||||
|
|
||||||
|
_heuristicDivisorTables = new HeuristicGpuDivisorTables(
|
||||||
|
_sharedTables.HeuristicGroupADivisors.View,
|
||||||
|
_digitTables.GroupBDivisorsEnding1View,
|
||||||
|
_digitTables.GroupBDivisorsEnding7View,
|
||||||
|
_digitTables.GroupBDivisorsEnding9View);
|
||||||
|
HeuristicGpuDivisorTables.InitializeShared(in _heuristicDivisorTables);
|
||||||
|
|
||||||
|
if (_digitTables.HasCombinedTables)
|
||||||
|
{
|
||||||
|
_heuristicCombinedTables = new HeuristicGpuCombinedDivisorTables(
|
||||||
|
_digitTables.CombinedDivisorsEnding1View,
|
||||||
|
_digitTables.CombinedDivisorsEnding3View,
|
||||||
|
_digitTables.CombinedDivisorsEnding7View,
|
||||||
|
_digitTables.CombinedDivisorsEnding9View);
|
||||||
|
HeuristicGpuCombinedDivisorTables.InitializeShared(in _heuristicCombinedTables);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_heuristicCombinedTables = HeuristicGpuCombinedDivisorTables.Shared;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureCapacity(minBufferCapacity);
|
||||||
|
_returned = false;
|
||||||
|
}
|
||||||
|
|
||||||
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesDefault => _sharedTables.DevicePrimesDefault;
|
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesDefault => _sharedTables.DevicePrimesDefault;
|
||||||
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastOne => _sharedTables.DevicePrimesLastOne;
|
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastOne => _sharedTables.DevicePrimesLastOne;
|
||||||
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastSeven => _sharedTables.DevicePrimesLastSeven;
|
public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastSeven => _sharedTables.DevicePrimesLastSeven;
|
||||||
@@ -399,43 +566,11 @@ public sealed class PrimeTester
|
|||||||
public MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastSeven => _sharedTables.DevicePrimesPow2LastSeven;
|
public MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastSeven => _sharedTables.DevicePrimesPow2LastSeven;
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastThree => _sharedTables.DevicePrimesPow2LastThree;
|
public MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastThree => _sharedTables.DevicePrimesPow2LastThree;
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastNine => _sharedTables.DevicePrimesPow2LastNine;
|
public MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastNine => _sharedTables.DevicePrimesPow2LastNine;
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupADivisors => _sharedTables.HeuristicGroupADivisors;
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupADivisorSquares => _sharedTables.HeuristicGroupADivisorSquares;
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding1 => _sharedTables.HeuristicGroupBDivisorsEnding1;
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding1 => _sharedTables.HeuristicGroupBDivisorSquaresEnding1;
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding7 => _sharedTables.HeuristicGroupBDivisorsEnding7;
|
|
||||||
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;
|
public HeuristicGpuDivisorTables HeuristicGpuTables => _heuristicDivisorTables;
|
||||||
|
public HeuristicGpuCombinedDivisorTables HeuristicCombinedTables => _heuristicCombinedTables;
|
||||||
|
|
||||||
internal PrimeTesterGpuContextLease(int minBufferCapacity)
|
internal void EnsureCapacity(int minCapacity)
|
||||||
{
|
|
||||||
AcceleratorContext = SharedGpuContext.Context;
|
|
||||||
Accelerator = SharedGpuContext.Accelerator;
|
|
||||||
// GpuStaticTableInitializer.EnsureStaticTables(Accelerator);
|
|
||||||
// Kernel = Accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel);
|
|
||||||
// HeuristicTrialDivisionKernel = Accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables>(PrimeTesterKernels.HeuristicTrialDivisionKernel);
|
|
||||||
|
|
||||||
_sharedTables = RentSharedTables(Accelerator);
|
|
||||||
|
|
||||||
_heuristicDivisorTables = _sharedTables.CreateHeuristicDivisorTables();
|
|
||||||
HeuristicGpuDivisorTables.InitializeShared(in _heuristicDivisorTables);
|
|
||||||
|
|
||||||
EnsureCapacity(minBufferCapacity);
|
|
||||||
_returned = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EnsureCapacity(int minCapacity)
|
|
||||||
{
|
{
|
||||||
if (minCapacity <= BufferCapacity)
|
if (minCapacity <= BufferCapacity)
|
||||||
{
|
{
|
||||||
@@ -464,7 +599,7 @@ public sealed class PrimeTester
|
|||||||
}
|
}
|
||||||
|
|
||||||
_returned = true;
|
_returned = true;
|
||||||
Return(this);
|
PrimeTesterGpuContextPool.Return(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void DisposeResources()
|
internal void DisposeResources()
|
||||||
@@ -472,34 +607,19 @@ public sealed class PrimeTester
|
|||||||
Input?.Dispose();
|
Input?.Dispose();
|
||||||
Output?.Dispose();
|
Output?.Dispose();
|
||||||
HeuristicFlag?.Dispose();
|
HeuristicFlag?.Dispose();
|
||||||
|
ReleaseTrailingDigitTables(_digitTables);
|
||||||
ReleaseSharedTables(_sharedTables);
|
ReleaseSharedTables(_sharedTables);
|
||||||
// These resources are shared between GPU leases
|
Accelerator.Dispose();
|
||||||
// Accelerator.Dispose();
|
AcceleratorContext.Dispose();
|
||||||
// AcceleratorContext.Dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private sealed class SharedKernelTables
|
||||||
private sealed class AcceleratorReferenceComparer : IEqualityComparer<Accelerator>
|
|
||||||
{
|
|
||||||
internal 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 SharedHeuristicGpuTables
|
|
||||||
{
|
{
|
||||||
private int _referenceCount;
|
private int _referenceCount;
|
||||||
|
|
||||||
internal SharedHeuristicGpuTables(Accelerator accelerator)
|
internal SharedKernelTables(Accelerator accelerator, string key)
|
||||||
{
|
{
|
||||||
|
Key = key;
|
||||||
|
|
||||||
var primesDefault = DivisorGenerator.SmallPrimes;
|
var primesDefault = DivisorGenerator.SmallPrimes;
|
||||||
DevicePrimesDefault = accelerator.Allocate1D<uint>(primesDefault.Length);
|
DevicePrimesDefault = accelerator.Allocate1D<uint>(primesDefault.Length);
|
||||||
@@ -542,44 +662,10 @@ public sealed class PrimeTester
|
|||||||
DevicePrimesPow2LastNine.View.CopyFromCPU(primesPow2LastNine);
|
DevicePrimesPow2LastNine.View.CopyFromCPU(primesPow2LastNine);
|
||||||
|
|
||||||
HeuristicPrimeSieves.EnsureInitialized();
|
HeuristicPrimeSieves.EnsureInitialized();
|
||||||
HeuristicCombinedPrimeTester.EnsureInitialized();
|
HeuristicGroupADivisors = CopySpanToDevice(accelerator, HeuristicPrimeSieves.GroupADivisors);
|
||||||
|
|
||||||
var heuristicGroupA = HeuristicPrimeSieves.GroupADivisors;
|
|
||||||
HeuristicGroupADivisors = CopySpanToDevice(accelerator, heuristicGroupA);
|
|
||||||
|
|
||||||
var heuristicGroupASquares = HeuristicPrimeSieves.GroupADivisorSquares;
|
|
||||||
HeuristicGroupADivisorSquares = CopySpanToDevice(accelerator, heuristicGroupASquares);
|
|
||||||
|
|
||||||
HeuristicGroupBDivisorsEnding1 = CopyUintSpanToDevice(accelerator, DivisorGenerator.SmallPrimesLastOneWithoutLastThree);
|
|
||||||
HeuristicGroupBDivisorSquaresEnding1 = CopySpanToDevice(accelerator, DivisorGenerator.SmallPrimesPow2LastOneWithoutLastThree);
|
|
||||||
|
|
||||||
HeuristicGroupBDivisorsEnding7 = CopyUintSpanToDevice(accelerator, DivisorGenerator.SmallPrimesLastSevenWithoutLastThree);
|
|
||||||
HeuristicGroupBDivisorSquaresEnding7 = CopySpanToDevice(accelerator, DivisorGenerator.SmallPrimesPow2LastSevenWithoutLastThree);
|
|
||||||
|
|
||||||
HeuristicGroupBDivisorsEnding9 = CopyUintSpanToDevice(accelerator, DivisorGenerator.SmallPrimesLastNineWithoutLastThree);
|
|
||||||
HeuristicGroupBDivisorSquaresEnding9 = CopySpanToDevice(accelerator, DivisorGenerator.SmallPrimesPow2LastNineWithoutLastThree);
|
|
||||||
|
|
||||||
var combinedEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1Span;
|
|
||||||
HeuristicCombinedDivisorsEnding1 = CopySpanToDevice(accelerator, combinedEnding1);
|
|
||||||
var combinedSquaresEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1SquaresSpan;
|
|
||||||
HeuristicCombinedDivisorSquaresEnding1 = CopySpanToDevice(accelerator, combinedSquaresEnding1);
|
|
||||||
|
|
||||||
var combinedEnding3 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding3Span;
|
|
||||||
HeuristicCombinedDivisorsEnding3 = CopySpanToDevice(accelerator, combinedEnding3);
|
|
||||||
var combinedSquaresEnding3 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding3SquaresSpan;
|
|
||||||
HeuristicCombinedDivisorSquaresEnding3 = CopySpanToDevice(accelerator, combinedSquaresEnding3);
|
|
||||||
|
|
||||||
var combinedEnding7 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding7Span;
|
|
||||||
HeuristicCombinedDivisorsEnding7 = CopySpanToDevice(accelerator, combinedEnding7);
|
|
||||||
var combinedSquaresEnding7 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding7SquaresSpan;
|
|
||||||
HeuristicCombinedDivisorSquaresEnding7 = CopySpanToDevice(accelerator, combinedSquaresEnding7);
|
|
||||||
|
|
||||||
var combinedEnding9 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding9Span;
|
|
||||||
HeuristicCombinedDivisorsEnding9 = CopySpanToDevice(accelerator, combinedEnding9);
|
|
||||||
var combinedSquaresEnding9 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding9SquaresSpan;
|
|
||||||
HeuristicCombinedDivisorSquaresEnding9 = CopySpanToDevice(accelerator, combinedSquaresEnding9);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal string Key { get; }
|
||||||
internal MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesDefault { get; }
|
internal MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesDefault { get; }
|
||||||
internal MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastOne { get; }
|
internal MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastOne { get; }
|
||||||
internal MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastSeven { get; }
|
internal MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastSeven { get; }
|
||||||
@@ -591,54 +677,122 @@ public sealed class PrimeTester
|
|||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastThree { get; }
|
internal MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastThree { get; }
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastNine { get; }
|
internal MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastNine { get; }
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupADivisors { get; }
|
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupADivisors { get; }
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupADivisorSquares { get; }
|
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding1 { get; }
|
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding1 { get; }
|
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding7 { get; }
|
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding7 { get; }
|
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding9 { get; }
|
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding9 { get; }
|
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding1 { get; }
|
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding1 { get; }
|
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding3 { get; }
|
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding3 { get; }
|
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding7 { get; }
|
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding7 { get; }
|
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding9 { get; }
|
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding9 { get; }
|
|
||||||
|
|
||||||
internal HeuristicGpuDivisorTables CreateHeuristicDivisorTables()
|
|
||||||
{
|
|
||||||
return new HeuristicGpuDivisorTables(
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void AddReference()
|
internal void AddReference()
|
||||||
{
|
{
|
||||||
_referenceCount++;
|
_referenceCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Release()
|
internal bool Release()
|
||||||
{
|
{
|
||||||
if (_referenceCount > 0)
|
if (_referenceCount > 0)
|
||||||
{
|
{
|
||||||
_referenceCount--;
|
_referenceCount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return _referenceCount == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Dispose()
|
||||||
|
{
|
||||||
|
DevicePrimesDefault.Dispose();
|
||||||
|
DevicePrimesLastOne.Dispose();
|
||||||
|
DevicePrimesLastSeven.Dispose();
|
||||||
|
DevicePrimesLastThree.Dispose();
|
||||||
|
DevicePrimesLastNine.Dispose();
|
||||||
|
DevicePrimesPow2Default.Dispose();
|
||||||
|
DevicePrimesPow2LastOne.Dispose();
|
||||||
|
DevicePrimesPow2LastSeven.Dispose();
|
||||||
|
DevicePrimesPow2LastThree.Dispose();
|
||||||
|
DevicePrimesPow2LastNine.Dispose();
|
||||||
|
HeuristicGroupADivisors.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class TrailingDigitTables
|
||||||
|
{
|
||||||
|
private int _referenceCount;
|
||||||
|
|
||||||
|
internal TrailingDigitTables(Accelerator accelerator, string key, byte trailingDigit, HeuristicGpuTableScope scope)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
TrailingDigit = trailingDigit;
|
||||||
|
Scope = scope;
|
||||||
|
|
||||||
|
HeuristicPrimeSieves.EnsureInitialized();
|
||||||
|
HeuristicCombinedPrimeTester.EnsureInitialized();
|
||||||
|
|
||||||
|
if (trailingDigit == 1 || trailingDigit == 7 || trailingDigit == 9)
|
||||||
|
{
|
||||||
|
GroupBDivisorsEnding1 = CopyUintSpanToDevice(accelerator, DivisorGenerator.SmallPrimesLastOneWithoutLastThree);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trailingDigit == 3 || trailingDigit == 7 || trailingDigit == 9)
|
||||||
|
{
|
||||||
|
GroupBDivisorsEnding7 = CopyUintSpanToDevice(accelerator, DivisorGenerator.SmallPrimesLastSevenWithoutLastThree);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trailingDigit == 1 || trailingDigit == 3 || trailingDigit == 9)
|
||||||
|
{
|
||||||
|
GroupBDivisorsEnding9 = CopyUintSpanToDevice(accelerator, DivisorGenerator.SmallPrimesLastNineWithoutLastThree);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scope == HeuristicGpuTableScope.GroupABWithCombined)
|
||||||
|
{
|
||||||
|
CombinedDivisorsEnding1 = trailingDigit == 1 ? CopySpanToDevice(accelerator, HeuristicCombinedPrimeTester.CombinedDivisorsEnding1Span) : null;
|
||||||
|
CombinedDivisorsEnding3 = trailingDigit == 3 ? CopySpanToDevice(accelerator, HeuristicCombinedPrimeTester.CombinedDivisorsEnding3Span) : null;
|
||||||
|
CombinedDivisorsEnding7 = trailingDigit == 7 ? CopySpanToDevice(accelerator, HeuristicCombinedPrimeTester.CombinedDivisorsEnding7Span) : null;
|
||||||
|
CombinedDivisorsEnding9 = trailingDigit == 9 ? CopySpanToDevice(accelerator, HeuristicCombinedPrimeTester.CombinedDivisorsEnding9Span) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string Key { get; }
|
||||||
|
internal byte TrailingDigit { get; }
|
||||||
|
internal HeuristicGpuTableScope Scope { get; }
|
||||||
|
|
||||||
|
internal bool HasCombinedTables => Scope == HeuristicGpuTableScope.GroupABWithCombined;
|
||||||
|
|
||||||
|
internal MemoryBuffer1D<ulong, Stride1D.Dense>? CombinedDivisorsEnding1 { get; }
|
||||||
|
internal MemoryBuffer1D<ulong, Stride1D.Dense>? CombinedDivisorsEnding3 { get; }
|
||||||
|
internal MemoryBuffer1D<ulong, Stride1D.Dense>? CombinedDivisorsEnding7 { get; }
|
||||||
|
internal MemoryBuffer1D<ulong, Stride1D.Dense>? CombinedDivisorsEnding9 { get; }
|
||||||
|
internal MemoryBuffer1D<ulong, Stride1D.Dense>? GroupBDivisorsEnding1 { get; }
|
||||||
|
internal MemoryBuffer1D<ulong, Stride1D.Dense>? GroupBDivisorsEnding7 { get; }
|
||||||
|
internal MemoryBuffer1D<ulong, Stride1D.Dense>? GroupBDivisorsEnding9 { get; }
|
||||||
|
|
||||||
|
internal ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding1View => CombinedDivisorsEnding1?.View ?? ArrayView1D<ulong, Stride1D.Dense>.Empty;
|
||||||
|
internal ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding3View => CombinedDivisorsEnding3?.View ?? ArrayView1D<ulong, Stride1D.Dense>.Empty;
|
||||||
|
internal ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding7View => CombinedDivisorsEnding7?.View ?? ArrayView1D<ulong, Stride1D.Dense>.Empty;
|
||||||
|
internal ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding9View => CombinedDivisorsEnding9?.View ?? ArrayView1D<ulong, Stride1D.Dense>.Empty;
|
||||||
|
internal ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding1View => GroupBDivisorsEnding1?.View ?? ArrayView1D<ulong, Stride1D.Dense>.Empty;
|
||||||
|
internal ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding7View => GroupBDivisorsEnding7?.View ?? ArrayView1D<ulong, Stride1D.Dense>.Empty;
|
||||||
|
internal ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding9View => GroupBDivisorsEnding9?.View ?? ArrayView1D<ulong, Stride1D.Dense>.Empty;
|
||||||
|
|
||||||
|
internal void AddReference()
|
||||||
|
{
|
||||||
|
_referenceCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool Release()
|
||||||
|
{
|
||||||
|
if (_referenceCount > 0)
|
||||||
|
{
|
||||||
|
_referenceCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _referenceCount == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Dispose()
|
||||||
|
{
|
||||||
|
CombinedDivisorsEnding1?.Dispose();
|
||||||
|
CombinedDivisorsEnding3?.Dispose();
|
||||||
|
CombinedDivisorsEnding7?.Dispose();
|
||||||
|
CombinedDivisorsEnding9?.Dispose();
|
||||||
|
GroupBDivisorsEnding1?.Dispose();
|
||||||
|
GroupBDivisorsEnding7?.Dispose();
|
||||||
|
GroupBDivisorsEnding9?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MemoryBuffer1D<ulong, Stride1D.Dense> CopySpanToDevice(Accelerator accelerator, ReadOnlySpan<ulong> span)
|
private static MemoryBuffer1D<ulong, Stride1D.Dense> CopySpanToDevice(Accelerator accelerator, ReadOnlySpan<ulong> span)
|
||||||
@@ -646,8 +800,8 @@ public sealed class PrimeTester
|
|||||||
var buffer = accelerator.Allocate1D<ulong>(span.Length);
|
var buffer = accelerator.Allocate1D<ulong>(span.Length);
|
||||||
if (!span.IsEmpty)
|
if (!span.IsEmpty)
|
||||||
{
|
{
|
||||||
ref ulong sourceRef = ref MemoryMarshal.GetReference(span);
|
ref ulong source = ref MemoryMarshal.GetReference(span);
|
||||||
buffer.View.CopyFromCPU(ref sourceRef, span.Length);
|
buffer.View.CopyFromCPU(ref source, span.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
@@ -669,37 +823,6 @@ public sealed class PrimeTester
|
|||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Dispose()
|
|
||||||
{
|
|
||||||
DevicePrimesDefault.Dispose();
|
|
||||||
DevicePrimesLastOne.Dispose();
|
|
||||||
DevicePrimesLastSeven.Dispose();
|
|
||||||
DevicePrimesLastThree.Dispose();
|
|
||||||
DevicePrimesLastNine.Dispose();
|
|
||||||
DevicePrimesPow2Default.Dispose();
|
|
||||||
DevicePrimesPow2LastOne.Dispose();
|
|
||||||
DevicePrimesPow2LastSeven.Dispose();
|
|
||||||
DevicePrimesPow2LastThree.Dispose();
|
|
||||||
DevicePrimesPow2LastNine.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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expose cache clearing for accelerator disposal coordination
|
// Expose cache clearing for accelerator disposal coordination
|
||||||
@@ -732,7 +855,7 @@ public sealed class PrimeTester
|
|||||||
// TODO: Route this batch helper through the shared GPU kernel pool from
|
// TODO: Route this batch helper through the shared GPU kernel pool from
|
||||||
// GpuUInt128BinaryGcdBenchmarks so we reuse cached kernels, pinned host buffers,
|
// GpuUInt128BinaryGcdBenchmarks so we reuse cached kernels, pinned host buffers,
|
||||||
// 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 = PrimeTesterGpuContextPool.Rent();
|
var gpu = PrimeTesterGpuContextPool.Rent(1);
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = gpu.Accelerator;
|
||||||
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SharesFactorKernel);
|
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SharesFactorKernel);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# GPU context pooling and heuristic divisor updates
|
||||||
|
|
||||||
|
- [x] Review existing GPU context pooling in `PerfectNumbers.Core/PrimeTester.cs` and determine shared table allocations that can be scoped per trailing digit.
|
||||||
|
- [x] Split `PrimeTesterGpuContextPool` into trailing-digit specific pools so each lease only uploads the divisor tables required for that digit.
|
||||||
|
- [x] Update GPU warm-up and rental code paths to pick pools based on `n % 10` and ensure thread-safe reuse.
|
||||||
|
- [x] Replace square-table usage in GPU heuristic trial division with square-root limits so divisor tables no longer need squared values on the accelerator.
|
||||||
|
- [x] Remove GPU square buffers and adjust kernels, table types, and callers accordingly.
|
||||||
|
- [x] Build solution to confirm the new pooling and divisor logic compiles; tests documented in summary.
|
||||||
Reference in new issue
Block a user