Preload divisor generator tables for GPU pool contexts
This commit is contained in:
1 parent
469c8057d3
commit
b7c34f4960
2 files changed
+148
No files matched your search
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,10 @@ public static class GpuContextPool
|
||||
Context = Context.CreateDefault();
|
||||
Accelerator = Context.GetPreferredDevice(preferCpu).CreateAccelerator(Context);
|
||||
IsCpu = Accelerator.AcceleratorType == AcceleratorType.CPU;
|
||||
if (!IsCpu)
|
||||
{
|
||||
DivisorGeneratorGpuCache.WarmUpByDivisorTables(Accelerator);
|
||||
}
|
||||
// NOTE: Avoid loading/compiling any kernel here to prevent implicit
|
||||
// CL stream/queue creation during accelerator construction.
|
||||
// Some OpenCL drivers are fragile when a queue is created immediately
|
||||
@@ -38,6 +42,7 @@ public static class GpuContextPool
|
||||
// Release PrimeTester GPU state for this accelerator (kernel/device primes)
|
||||
// Clear any per-accelerator cached resources to avoid releasing
|
||||
// after the accelerator is destroyed.
|
||||
DivisorGeneratorGpuCache.Clear(Accelerator);
|
||||
PrimeTester.ClearGpuCaches(Accelerator);
|
||||
|
||||
Accelerator.Dispose();
|
||||
@@ -117,6 +122,7 @@ public static class GpuContextPool
|
||||
gpu.Dispose();
|
||||
}
|
||||
|
||||
DivisorGeneratorGpuCache.DisposeAll();
|
||||
PrimeTester.DisposeGpuContexts();
|
||||
WarmedGpuContextCount = 0;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user