Merge pull request #168 from mlemanczyk/codex/remove-redundant-wrapper-methods-in-mersenne-path
Eliminate IDisposable dependencies
This commit is contained in:
28 files changed
+168
-126
No files matched your search
@@ -540,7 +540,7 @@ public class ProgramTests
|
||||
public void Main_displays_help_when_requested()
|
||||
{
|
||||
var main = typeof(Program).GetMethod("Main", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
using var writer = new StringWriter();
|
||||
var writer = new StringWriter();
|
||||
TextWriter original = Console.Out;
|
||||
Console.SetOut(writer);
|
||||
|
||||
@@ -554,6 +554,7 @@ public class ProgramTests
|
||||
}
|
||||
|
||||
string output = writer.ToString();
|
||||
writer.Dispose();
|
||||
output.Should().Contain("Usage:");
|
||||
output.Should().Contain("--mersenne-device=cpu|gpu");
|
||||
output.Should().Contain("--primes-device=cpu|gpu");
|
||||
|
||||
@@ -720,7 +720,21 @@ internal static class Program
|
||||
|
||||
if (_useByDivisorMode)
|
||||
{
|
||||
RunByDivisorMode(byDivisorCandidates, threadCount, _primeTestLimit);
|
||||
if (_byDivisorTester is null)
|
||||
{
|
||||
throw new InvalidOperationException("By-divisor tester has not been initialized.");
|
||||
}
|
||||
|
||||
MersenneNumberDivisorByDivisorTester.Run(
|
||||
byDivisorCandidates,
|
||||
_byDivisorTester,
|
||||
_byDivisorPreviousResults,
|
||||
_byDivisorStartPrime,
|
||||
static () => _lastCompositeP = true,
|
||||
static () => _lastCompositeP = false,
|
||||
PrintResult,
|
||||
threadCount,
|
||||
_primeTestLimit);
|
||||
FlushBuffer();
|
||||
StringBuilderPool.Return(_outputBuilder!);
|
||||
return;
|
||||
@@ -861,27 +875,6 @@ internal static class Program
|
||||
return candidates;
|
||||
}
|
||||
|
||||
private static void RunByDivisorMode(List<ulong> candidates, int threadCount, TimeSpan? primeTestLimit)
|
||||
{
|
||||
if (_byDivisorTester is null)
|
||||
{
|
||||
throw new InvalidOperationException("By-divisor tester has not been initialized.");
|
||||
}
|
||||
|
||||
// TODO: Inline this helper by invoking MersenneNumberDivisorByDivisorTester.Run directly at call sites so
|
||||
// the CLI avoids this extra frame during the hot composite-reporting path.
|
||||
MersenneNumberDivisorByDivisorTester.Run(
|
||||
candidates,
|
||||
_byDivisorTester,
|
||||
_byDivisorPreviousResults,
|
||||
_byDivisorStartPrime,
|
||||
static () => _lastCompositeP = true,
|
||||
static () => _lastCompositeP = false,
|
||||
PrintResult,
|
||||
threadCount,
|
||||
primeTestLimit);
|
||||
}
|
||||
|
||||
private static unsafe void LoadResultsFile(string resultsFileName, Action<ulong, bool, bool> lineProcessorAction)
|
||||
{
|
||||
using FileStream readStream = new(resultsFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
@@ -18,13 +18,14 @@ public class GpuPrimeLimiterTests
|
||||
var startedSecond = new TaskCompletionSource<bool>();
|
||||
var sw = new Stopwatch();
|
||||
|
||||
using var first = GpuPrimeWorkLimiter.Acquire();
|
||||
var first = GpuPrimeWorkLimiter.Acquire();
|
||||
sw.Start();
|
||||
|
||||
var secondTask = Task.Run(() =>
|
||||
{
|
||||
using var second = GpuPrimeWorkLimiter.Acquire();
|
||||
var second = GpuPrimeWorkLimiter.Acquire();
|
||||
startedSecond.TrySetResult(true);
|
||||
second.Dispose();
|
||||
});
|
||||
|
||||
// Give secondTask a moment to attempt acquire (it should block).
|
||||
|
||||
@@ -34,23 +34,19 @@ public class NttMontgomeryTests
|
||||
|
||||
// Device execution
|
||||
GpuContextPool.GpuContextLease context = GpuContextPool.Rent();
|
||||
try
|
||||
{
|
||||
var accelerator = context.Accelerator;
|
||||
using var buffer = accelerator.Allocate1D<GpuUInt128>(gpu.Length);
|
||||
buffer.View.CopyFromCPU(ref gpu[0], gpu.Length);
|
||||
NttGpuMath.SquareDevice(accelerator, buffer.View, modulus, primitiveRoot);
|
||||
buffer.View.CopyToCPU(ref gpu[0], gpu.Length);
|
||||
var accelerator = context.Accelerator;
|
||||
var buffer = accelerator.Allocate1D<GpuUInt128>(gpu.Length);
|
||||
buffer.View.CopyFromCPU(ref gpu[0], gpu.Length);
|
||||
NttGpuMath.SquareDevice(accelerator, buffer.View, modulus, primitiveRoot);
|
||||
buffer.View.CopyToCPU(ref gpu[0], gpu.Length);
|
||||
|
||||
for (int i = 0; i < cpu.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
for (int i = 0; i < cpu.Length; i++)
|
||||
{
|
||||
context.Dispose();
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
|
||||
buffer.Dispose();
|
||||
context.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -17,13 +17,14 @@ public class GpuWorkLimiterTests
|
||||
var startedSecond = new TaskCompletionSource<bool>();
|
||||
var sw = new Stopwatch();
|
||||
|
||||
using var first = GpuWorkLimiter.Acquire();
|
||||
var first = GpuWorkLimiter.Acquire();
|
||||
sw.Start();
|
||||
|
||||
var secondTask = Task.Run(() =>
|
||||
{
|
||||
using var second = GpuWorkLimiter.Acquire();
|
||||
var second = GpuWorkLimiter.Acquire();
|
||||
startedSecond.TrySetResult(true);
|
||||
second.Dispose();
|
||||
});
|
||||
|
||||
await Task.Delay(50);
|
||||
|
||||
@@ -14,8 +14,8 @@ public class MersenneDivisorCyclesGenerateGpuTests
|
||||
{
|
||||
MersenneDivisorCycles.GenerateGpu(path, maxDivisor: 50UL, batchSize: 16, skipCount: 0, nextPosition: 0);
|
||||
var pairs = new List<(ulong divisor, ulong cycle)>();
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
using var reader = new BinaryReader(stream);
|
||||
var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
var reader = new BinaryReader(stream);
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
ulong d = reader.ReadUInt64();
|
||||
@@ -41,6 +41,9 @@ public class MersenneDivisorCyclesGenerateGpuTests
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(pair.divisor);
|
||||
pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLength(pair.divisor, divisorData));
|
||||
}
|
||||
|
||||
reader.Dispose();
|
||||
stream.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -61,8 +64,8 @@ public class MersenneDivisorCyclesGenerateGpuTests
|
||||
MersenneDivisorCycles.Generate(path, maxDivisor: 50UL, threads: 4);
|
||||
|
||||
var pairs = new List<(ulong divisor, ulong cycle)>();
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
using var reader = new BinaryReader(stream);
|
||||
var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
var reader = new BinaryReader(stream);
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
ulong divisor = reader.ReadUInt64();
|
||||
@@ -95,6 +98,9 @@ public class MersenneDivisorCyclesGenerateGpuTests
|
||||
|
||||
var (_, completeCount) = MersenneDivisorCycles.FindLast(path);
|
||||
completeCount.Should().Be(expected.Count);
|
||||
|
||||
reader.Dispose();
|
||||
stream.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -46,7 +46,7 @@ public class MersenneNumberDivisorCpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(197UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
var session = tester.CreateDivisorSession();
|
||||
ulong[] exponents = { 59UL, 61UL, 67UL, 71UL, 73UL, 79UL, 117_652UL, 333_380UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
@@ -74,6 +74,8 @@ public class MersenneNumberDivisorCpuTesterTests
|
||||
}
|
||||
|
||||
hits.Should().Equal(expected2);
|
||||
|
||||
session.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -83,7 +85,7 @@ public class MersenneNumberDivisorCpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(197UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
var session = tester.CreateDivisorSession();
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(LargeDivisor1);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(LargeDivisor1, divisorData);
|
||||
|
||||
@@ -98,6 +100,8 @@ public class MersenneNumberDivisorCpuTesterTests
|
||||
}
|
||||
|
||||
hits.Should().Equal(expected);
|
||||
|
||||
session.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -107,7 +111,7 @@ public class MersenneNumberDivisorCpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(197UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
var session = tester.CreateDivisorSession();
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(LargeDivisor2);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(LargeDivisor2, divisorData);
|
||||
|
||||
@@ -122,5 +126,7 @@ public class MersenneNumberDivisorCpuTesterTests
|
||||
}
|
||||
|
||||
hits.Should().Equal(expected);
|
||||
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -157,7 +157,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(197UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
var session = tester.CreateDivisorSession();
|
||||
ulong[] exponents = { 59UL, 61UL, 67UL, 71UL, 73UL, 79UL, 117_652UL, 333_380UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
@@ -185,6 +185,8 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
}
|
||||
|
||||
hits.Should().Equal(expected2);
|
||||
|
||||
session.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -198,7 +200,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
|
||||
tester.ConfigureFromMaxPrime(197UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
var session = tester.CreateDivisorSession();
|
||||
ulong[] exponents = { 59UL, 61UL, 67UL, 71UL, 73UL, 79UL, 117_652UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
@@ -213,6 +215,8 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
}
|
||||
|
||||
hits.Should().Equal(expected);
|
||||
|
||||
session.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -225,7 +229,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
};
|
||||
tester.ConfigureFromMaxPrime(197UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
var session = tester.CreateDivisorSession();
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(4_000_169UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(4_000_169UL, divisorData);
|
||||
|
||||
@@ -252,6 +256,8 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
|
||||
expected.Should().Equal(direct);
|
||||
hits.Should().Equal(direct);
|
||||
|
||||
session.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -264,7 +270,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
};
|
||||
tester.ConfigureFromMaxPrime(197UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
var session = tester.CreateDivisorSession();
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(4_000_561UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(4_000_561UL, divisorData);
|
||||
|
||||
@@ -291,6 +297,8 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
|
||||
expected.Should().Equal(direct);
|
||||
hits.Should().Equal(direct);
|
||||
|
||||
session.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -300,7 +308,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(197UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
var session = tester.CreateDivisorSession();
|
||||
ulong[] exponents = { 59UL, 67UL, 117_652UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
@@ -316,6 +324,8 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
}
|
||||
|
||||
hits.Should().Equal(expected);
|
||||
|
||||
session.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -329,7 +339,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
|
||||
tester.ConfigureFromMaxPrime(197UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
var session = tester.CreateDivisorSession();
|
||||
ulong[] exponents = { 59UL, 61UL, 67UL, 71UL, 73UL, 79UL, 117_652UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
@@ -344,6 +354,8 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
}
|
||||
|
||||
hits.Should().Equal(expected);
|
||||
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,10 +76,10 @@ public readonly struct ResiduePrimeViews(
|
||||
public readonly ArrayView1D<ulong, Stride1D.Dense> LastSevenPow2 = lastSevenPow2;
|
||||
}
|
||||
|
||||
public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, KernelContainer kernels)
|
||||
public ref struct GpuKernelLease(GpuPrimeWorkLimiter.Lease limiter, GpuContextLease gpu, KernelContainer kernels)
|
||||
{
|
||||
private bool disposedValue;
|
||||
private IDisposable? _limiter = limiter;
|
||||
private GpuPrimeWorkLimiter.Lease? _limiter = limiter;
|
||||
private GpuContextLease? _gpu = gpu;
|
||||
private KernelContainer _kernels = kernels;
|
||||
private AcceleratorStream? _stream;
|
||||
|
||||
@@ -6,13 +6,13 @@ public static class GpuPrimeWorkLimiter
|
||||
private static readonly object _lock = new();
|
||||
private static int _currentLimit = 1;
|
||||
|
||||
public static IDisposable Acquire()
|
||||
public static Lease Acquire()
|
||||
{
|
||||
// TODO: Inline the pooled limiter guard from the GpuLimiterThroughputBenchmarks so prime-sieve
|
||||
// GPU jobs stop allocating Releaser instances on every acquisition.
|
||||
var sem = _semaphore;
|
||||
sem.Wait();
|
||||
return new Releaser(sem);
|
||||
return new Lease(sem);
|
||||
}
|
||||
|
||||
public static void SetLimit(int value)
|
||||
@@ -36,12 +36,12 @@ public static class GpuPrimeWorkLimiter
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class Releaser : IDisposable
|
||||
public sealed class Lease
|
||||
{
|
||||
private readonly SemaphoreSlim _sem;
|
||||
private bool _disposed;
|
||||
|
||||
public Releaser(SemaphoreSlim sem)
|
||||
public Lease(SemaphoreSlim sem)
|
||||
{
|
||||
_sem = sem;
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ public static class GpuWorkLimiter
|
||||
private static readonly object _lock = new();
|
||||
private static int _currentLimit = 1;
|
||||
|
||||
public static IDisposable Acquire()
|
||||
public static Lease Acquire()
|
||||
{
|
||||
// TODO: Replace the per-call Releaser allocation with the pooled struct-based guard from the
|
||||
// GpuLimiterThroughputBenchmarks so limiter acquisition does not allocate when we enter the
|
||||
// GPU scanning hot path.
|
||||
var sem = _semaphore;
|
||||
sem.Wait();
|
||||
return new Releaser(sem);
|
||||
return new Lease(sem);
|
||||
}
|
||||
|
||||
public static void SetLimit(int value)
|
||||
@@ -40,12 +40,12 @@ public static class GpuWorkLimiter
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class Releaser : IDisposable
|
||||
public sealed class Lease
|
||||
{
|
||||
private readonly SemaphoreSlim _sem;
|
||||
private bool _disposed;
|
||||
|
||||
public Releaser(SemaphoreSlim sem)
|
||||
public Lease(SemaphoreSlim sem)
|
||||
{
|
||||
_sem = sem;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Numerics;
|
||||
@@ -122,6 +123,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
resources.CycleCandidates,
|
||||
resources.CycleLengths,
|
||||
resources.CycleCapacity,
|
||||
resources,
|
||||
out lastProcessed,
|
||||
out coveredRange,
|
||||
out processedCount
|
||||
@@ -186,6 +188,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
ulong[] cycleCandidates,
|
||||
ulong[] cycleLengths,
|
||||
int cycleCapacity,
|
||||
BatchResources resources,
|
||||
out ulong lastProcessed,
|
||||
out bool coveredRange,
|
||||
out ulong processedCount)
|
||||
@@ -340,10 +343,10 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
|
||||
gapView.CopyFromCPU(ref MemoryMarshal.GetReference(gapSpan), chunkCount);
|
||||
|
||||
ComputeRemaindersOnGpu(chunkCount, remainder10, 10, gapView, deltaView, remainder10View, remainderDeltaKernel, remainderScanKernel);
|
||||
ComputeRemaindersOnGpu(chunkCount, remainder8, 8, gapView, deltaView, remainder8View, remainderDeltaKernel, remainderScanKernel);
|
||||
ComputeRemaindersOnGpu(chunkCount, remainder5, 5, gapView, deltaView, remainder5View, remainderDeltaKernel, remainderScanKernel);
|
||||
ComputeRemaindersOnGpu(chunkCount, remainder3, 3, gapView, deltaView, remainder3View, remainderDeltaKernel, remainderScanKernel);
|
||||
ComputeRemaindersOnGpu(chunkCount, remainder10, 10, gapView, deltaView, remainder10View, remainderDeltaKernel, remainderScanKernel, resources.GetRemainderScanConfig);
|
||||
ComputeRemaindersOnGpu(chunkCount, remainder8, 8, gapView, deltaView, remainder8View, remainderDeltaKernel, remainderScanKernel, resources.GetRemainderScanConfig);
|
||||
ComputeRemaindersOnGpu(chunkCount, remainder5, 5, gapView, deltaView, remainder5View, remainderDeltaKernel, remainderScanKernel, resources.GetRemainderScanConfig);
|
||||
ComputeRemaindersOnGpu(chunkCount, remainder3, 3, gapView, deltaView, remainder3View, remainderDeltaKernel, remainderScanKernel, resources.GetRemainderScanConfig);
|
||||
|
||||
BuildCandidateMaskOnGpu(chunkCount, remainder10View, remainder8View, remainder5View, remainder3View, lastIsSevenFlag, maskView, maskSpan, candidateMaskKernel);
|
||||
|
||||
@@ -461,7 +464,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
ArrayView1D<byte, Stride1D.Dense> deltaView,
|
||||
ArrayView1D<byte, Stride1D.Dense> remainderView,
|
||||
Action<Index1D, ArrayView<ulong>, ArrayView<byte>, byte> deltaKernel,
|
||||
Action<KernelConfig, Index1D, ArrayView<byte>, ArrayView<byte>, byte, byte> scanKernel)
|
||||
Action<KernelConfig, Index1D, ArrayView<byte>, ArrayView<byte>, byte, byte> scanKernel,
|
||||
Func<int, KernelConfig> configProvider)
|
||||
{
|
||||
// GPU batches always contain at least one divisor gap, so the empty-count guard remains commented
|
||||
// to eliminate redundant branching.
|
||||
@@ -471,10 +475,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
// }
|
||||
|
||||
deltaKernel(count, gapView, deltaView, modulus);
|
||||
SharedMemoryConfig sharedMemoryConfig = SharedMemoryConfig.RequestDynamic<int>(count);
|
||||
Index1D gridDim = new Index1D(1);
|
||||
Index1D groupDim = new Index1D(count);
|
||||
KernelConfig kernelConfig = new KernelConfig(gridDim, groupDim, sharedMemoryConfig);
|
||||
KernelConfig kernelConfig = configProvider(count);
|
||||
|
||||
scanKernel(kernelConfig, new Index1D(count), deltaView, remainderView, baseRemainder, modulus);
|
||||
}
|
||||
@@ -844,7 +845,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
|
||||
private void ReturnBatchResources(Accelerator accelerator, BatchResources resources) => _resourcePools.GetOrAdd(accelerator, static _ => []).Add(resources);
|
||||
|
||||
private sealed class BatchResources : IDisposable
|
||||
private sealed class BatchResources
|
||||
{
|
||||
internal BatchResources(MersenneNumberDivisorByDivisorGpuTester owner, Accelerator accelerator, int capacity)
|
||||
{
|
||||
@@ -906,10 +907,24 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
internal readonly Action<KernelConfig, Index1D, ArrayView<byte>, ArrayView<byte>, byte, byte> RemainderScanKernel;
|
||||
internal readonly Action<Index1D, ArrayView<byte>, ArrayView<byte>, ArrayView<byte>, ArrayView<byte>, byte, ArrayView<byte>> CandidateMaskKernel;
|
||||
|
||||
private readonly Dictionary<int, KernelConfig> _remainderScanConfigs = new();
|
||||
|
||||
internal readonly int Capacity;
|
||||
|
||||
internal readonly int CycleCapacity;
|
||||
|
||||
internal KernelConfig GetRemainderScanConfig(int count)
|
||||
{
|
||||
if (!_remainderScanConfigs.TryGetValue(count, out KernelConfig config))
|
||||
{
|
||||
SharedMemoryConfig sharedMemoryConfig = SharedMemoryConfig.RequestDynamic<int>(count);
|
||||
config = new KernelConfig(new Index1D(1), new Index1D(count), sharedMemoryConfig);
|
||||
_remainderScanConfigs[count] = config;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DivisorsBuffer.Dispose();
|
||||
@@ -948,7 +963,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
return ComputeAllowedMaxDivisor(prime, _divisorLimit);
|
||||
}
|
||||
|
||||
public DivisorScanSession CreateDivisorSession()
|
||||
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession()
|
||||
{
|
||||
// Divisor sessions are handed out only after configuration, so this guard would never trigger on the
|
||||
// production path.
|
||||
@@ -965,8 +980,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
|
||||
return new DivisorScanSession(this);
|
||||
}
|
||||
|
||||
IMersenneNumberDivisorByDivisorTester.IDivisorScanSession IMersenneNumberDivisorByDivisorTester.CreateDivisorSession() => CreateDivisorSession();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ using ILGPU.Runtime;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
internal sealed class MersenneNumberDivisorCandidateGpuEvaluator : IDisposable
|
||||
internal sealed class MersenneNumberDivisorCandidateGpuEvaluator
|
||||
{
|
||||
private const int RemainderCount = 6;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ using ILGPU.Runtime;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
internal sealed class MersenneNumberDivisorMontgomeryGpuBuilder : IDisposable
|
||||
internal sealed class MersenneNumberDivisorMontgomeryGpuBuilder
|
||||
{
|
||||
private readonly GpuContextPool.GpuContextLease _lease;
|
||||
private readonly Accelerator _accelerator;
|
||||
|
||||
@@ -6,7 +6,7 @@ using ILGPU.Runtime;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
internal sealed class MersenneNumberDivisorRemainderGpuStepper : IDisposable
|
||||
internal sealed class MersenneNumberDivisorRemainderGpuStepper
|
||||
{
|
||||
private const int RemainderCount = 6;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ using ILGPU.Runtime;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
internal sealed class MersenneNumberDivisorResidueGpuEvaluator : IDisposable
|
||||
internal sealed class MersenneNumberDivisorResidueGpuEvaluator
|
||||
{
|
||||
private readonly GpuContextPool.GpuContextLease _lease;
|
||||
private readonly Accelerator _accelerator;
|
||||
|
||||
@@ -327,8 +327,9 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
// TODO: Replace StreamWriter with the pooled TextFileWriter pipeline so persisting NTT
|
||||
// parameters reuses the zero-allocation buffered writes highlighted in the scanner I/O
|
||||
// benchmarks instead of allocating a new encoder per append.
|
||||
using var writer = new StreamWriter(ParameterFilePath, append: true);
|
||||
var writer = new StreamWriter(ParameterFilePath, append: true);
|
||||
writer.WriteLine($"{length} {modulus.High} {modulus.Low} {primitiveRoot.High} {primitiveRoot.Low}");
|
||||
writer.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,7 +339,7 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
ulong foundRoot = 0UL;
|
||||
bool success = false;
|
||||
var processorCount = Environment.ProcessorCount;
|
||||
using var cts = new System.Threading.CancellationTokenSource();
|
||||
var cts = new System.Threading.CancellationTokenSource();
|
||||
object sync = new();
|
||||
|
||||
// TODO: Move this Parallel.For to the shared low-overhead work scheduler once the NTT parameter
|
||||
@@ -381,11 +382,13 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
{
|
||||
modulus = new GpuUInt128(0UL, foundCandidate);
|
||||
primitiveRoot = new GpuUInt128(0UL, foundRoot);
|
||||
cts.Dispose();
|
||||
return true;
|
||||
}
|
||||
|
||||
modulus = new GpuUInt128(0UL, 18446744069414584321UL);
|
||||
primitiveRoot = new GpuUInt128(0UL, 7UL);
|
||||
cts.Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,31 +64,25 @@ internal static class MontgomeryOddPowerGpu
|
||||
var accelerator = lease.Accelerator;
|
||||
var stream = lease.Stream;
|
||||
|
||||
using var divisorBuffer = accelerator.Allocate1D<MontgomeryDivisorData>(divisorCount);
|
||||
using var countBuffer = accelerator.Allocate1D<int>(divisorCount);
|
||||
using var resultBuffer = accelerator.Allocate1D<ulong>(divisorCount * stride);
|
||||
var divisorBuffer = accelerator.Allocate1D<MontgomeryDivisorData>(divisorCount);
|
||||
var countBuffer = accelerator.Allocate1D<int>(divisorCount);
|
||||
var resultBuffer = accelerator.Allocate1D<ulong>(divisorCount * stride);
|
||||
|
||||
try
|
||||
{
|
||||
divisorBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(divisors), divisorCount);
|
||||
countBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(oddPowerCounts), divisorCount);
|
||||
resultBuffer.MemSetToZero();
|
||||
divisorBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(divisors), divisorCount);
|
||||
countBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(oddPowerCounts), divisorCount);
|
||||
resultBuffer.MemSetToZero();
|
||||
|
||||
var kernel = lease.MontgomeryOddPowerKernel;
|
||||
kernel(stream, divisorCount, divisorBuffer.View, countBuffer.View, resultBuffer.View, stride);
|
||||
stream.Synchronize();
|
||||
var kernel = lease.MontgomeryOddPowerKernel;
|
||||
kernel(stream, divisorCount, divisorBuffer.View, countBuffer.View, resultBuffer.View, stride);
|
||||
stream.Synchronize();
|
||||
|
||||
resultBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(results), divisorCount * stride);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
execution.Dispose();
|
||||
lease.Dispose();
|
||||
}
|
||||
resultBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(results), divisorCount * stride);
|
||||
|
||||
resultBuffer.Dispose();
|
||||
countBuffer.Dispose();
|
||||
divisorBuffer.Dispose();
|
||||
execution.Dispose();
|
||||
lease.Dispose();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ public static class NttGpuMath
|
||||
|
||||
private static readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, int>> BitReverseKernelCache = new(); // TODO: Precompute bit-reversal kernels during boot so we can remove the concurrent lookup overhead flagged in the benchmarks.
|
||||
|
||||
private sealed class SquareCacheEntry : IDisposable
|
||||
private sealed class SquareCacheEntry
|
||||
{
|
||||
public MemoryBuffer1D<GpuUInt128, Stride1D.Dense> Buffer { get; }
|
||||
public GpuUInt128 Root { get; }
|
||||
|
||||
@@ -1611,7 +1611,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
private static ulong CalculateByDoublingKernel(ulong prime)
|
||||
{
|
||||
ulong order = 1UL;
|
||||
ulong value = 2UL % prime;
|
||||
ulong value = 2UL; // Candidate primes exceed two, so the modulo would always return two.
|
||||
|
||||
while (value != 1UL)
|
||||
{
|
||||
|
||||
@@ -18,8 +18,10 @@ public interface IMersenneNumberDivisorByDivisorTester
|
||||
|
||||
void PrepareCandidates(ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues);
|
||||
|
||||
public interface IDivisorScanSession : IDisposable
|
||||
public interface IDivisorScanSession
|
||||
{
|
||||
void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, ReadOnlySpan<ulong> primes, Span<byte> hits);
|
||||
|
||||
void Dispose();
|
||||
}
|
||||
}
|
||||
@@ -196,7 +196,7 @@ public class MersenneDivisorCycles
|
||||
public static IEnumerable<(ulong divisor, ulong cycleLength)> EnumerateStream(Stream compressor)
|
||||
{
|
||||
// Binary pairs: (ulong divisor, ulong cycle)
|
||||
using var reader = new BinaryReader(compressor, Encoding.UTF8, leaveOpen: true);
|
||||
var reader = new BinaryReader(compressor, Encoding.UTF8, leaveOpen: true);
|
||||
while (true)
|
||||
{
|
||||
ulong d, c;
|
||||
@@ -207,6 +207,7 @@ public class MersenneDivisorCycles
|
||||
}
|
||||
catch (EndOfStreamException)
|
||||
{
|
||||
reader.Dispose();
|
||||
yield break;
|
||||
}
|
||||
|
||||
@@ -829,7 +830,7 @@ public class MersenneDivisorCycles
|
||||
// Start fresh to avoid mixing formats
|
||||
outputStream.SetLength(0L);
|
||||
outputStream.Position = 0L;
|
||||
using var writer = new BinaryWriter(outputStream, Encoding.UTF8, leaveOpen: false);
|
||||
var writer = new BinaryWriter(outputStream, Encoding.UTF8, leaveOpen: false);
|
||||
for (var taskIndex = 0; taskIndex < threads; taskIndex++)
|
||||
{
|
||||
ulong threadStart = start + (ulong)taskIndex * blockSize;
|
||||
@@ -911,6 +912,7 @@ public class MersenneDivisorCycles
|
||||
}
|
||||
|
||||
Task.WaitAll(tasks);
|
||||
writer.Dispose();
|
||||
}
|
||||
|
||||
public static void GenerateGpu(string path, ulong maxDivisor, int batchSize = 1_000_000, long skipCount = 0L, long nextPosition = 0L)
|
||||
@@ -947,7 +949,7 @@ public class MersenneDivisorCycles
|
||||
outputStream.Position = 0L;
|
||||
}
|
||||
|
||||
using var writer = new BinaryWriter(outputStream, Encoding.UTF8, leaveOpen: false);
|
||||
var writer = new BinaryWriter(outputStream, Encoding.UTF8, leaveOpen: false);
|
||||
try
|
||||
{
|
||||
while (skipCount > 0L && start <= maxDivisor)
|
||||
@@ -1022,6 +1024,7 @@ public class MersenneDivisorCycles
|
||||
pool.Return(outCycles, clearArray: false);
|
||||
}
|
||||
|
||||
writer.Dispose();
|
||||
stream.Dispose();
|
||||
lease.Dispose();
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Buffers;
|
||||
|
||||
namespace PerfectNumbers.Core
|
||||
{
|
||||
internal sealed class PrimeFactorPartialResult : IDisposable
|
||||
internal sealed class PrimeFactorPartialResult
|
||||
{
|
||||
[ThreadStatic]
|
||||
private static PrimeFactorPartialResult? s_poolHead;
|
||||
|
||||
@@ -176,7 +176,7 @@ public sealed class PrimeTester(bool useInternal = false)
|
||||
DevicePrimes.View.CopyFromCPU(primes);
|
||||
}
|
||||
|
||||
internal sealed class ScratchBuffers : IDisposable
|
||||
internal sealed class ScratchBuffers
|
||||
{
|
||||
public MemoryBuffer1D<ulong, Stride1D.Dense> Input { get; private set; }
|
||||
public MemoryBuffer1D<byte, Stride1D.Dense> Output { get; private set; }
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace PerfectNumbers.Core;
|
||||
/// <summary>
|
||||
/// Provides thread-safe file writing for both text and binary data.
|
||||
/// </summary>
|
||||
public sealed class TextFileWriter : IDisposable
|
||||
public sealed class TextFileWriter
|
||||
{
|
||||
private readonly FileStream _stream;
|
||||
private StreamWriter? _textWriter;
|
||||
|
||||
@@ -530,7 +530,9 @@ public static class ULongExtensions
|
||||
ulong modulus = divisor.Modulus;
|
||||
if (exponent == 0UL)
|
||||
{
|
||||
return keepMontgomery ? divisor.MontgomeryOne : 1UL % modulus;
|
||||
return keepMontgomery
|
||||
? divisor.MontgomeryOne
|
||||
: 1UL; // Montgomery moduli stay above one on the scanning path, so the modulo would always yield one.
|
||||
}
|
||||
|
||||
if (exponent <= Pow2WindowFallbackThreshold)
|
||||
@@ -622,7 +624,9 @@ public static class ULongExtensions
|
||||
ulong modulus = divisor.Modulus;
|
||||
if (exponent == 0UL)
|
||||
{
|
||||
return keepMontgomery ? divisor.MontgomeryOne : 1UL % modulus;
|
||||
return keepMontgomery
|
||||
? divisor.MontgomeryOne
|
||||
: 1UL; // Montgomery moduli stay above one on the scanning path, so the modulo would always yield one.
|
||||
}
|
||||
|
||||
if (exponent <= Pow2WindowFallbackThreshold)
|
||||
@@ -769,7 +773,7 @@ public static class ULongExtensions
|
||||
return;
|
||||
}
|
||||
|
||||
ulong baseValue = 2UL % modulus;
|
||||
ulong baseValue = 2UL; // Moduli arrive as odd primes greater than two, so the modulo would always return two.
|
||||
destination[0] = baseValue;
|
||||
if (oddPowerCount == 1)
|
||||
{
|
||||
|
||||
+7
-4
@@ -335,10 +335,13 @@ if (patchResults.Any(static r => !r.Success))
|
||||
|
||||
static List<PatchFile> ParsePatchFromFile(string patchFilePath)
|
||||
{
|
||||
using var stream = File.OpenRead(patchFilePath);
|
||||
using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 16_384, leaveOpen: false);
|
||||
using var lineReader = new PatchLineReader(reader);
|
||||
var stream = File.OpenRead(patchFilePath);
|
||||
var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 16_384, leaveOpen: false);
|
||||
var lineReader = new PatchLineReader(reader);
|
||||
var files = ParsePatch(lineReader);
|
||||
lineReader.Dispose();
|
||||
reader.Dispose();
|
||||
stream.Dispose();
|
||||
return files;
|
||||
}
|
||||
|
||||
@@ -500,7 +503,7 @@ static void FinalizePatchFile(
|
||||
files.Add(new PatchFile(originalPath, modifiedPath, hunks ?? new List<PatchHunk>(), type));
|
||||
}
|
||||
|
||||
private sealed class PatchLineReader : IDisposable
|
||||
private sealed class PatchLineReader
|
||||
{
|
||||
private const int ReadBufferSize = 4096;
|
||||
private const int InitialLineBufferSize = 256;
|
||||
|
||||
@@ -54,10 +54,11 @@ static ProbeResult ProbeBigInteger(Accelerator accelerator)
|
||||
try
|
||||
{
|
||||
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<int, Stride1D.Dense>>(BigIntegerKernel);
|
||||
using var buffer = accelerator.Allocate1D<int>(1);
|
||||
var buffer = accelerator.Allocate1D<int>(1);
|
||||
var extent = new Index1D((int)buffer.Length);
|
||||
kernel(extent, buffer.View);
|
||||
accelerator.Synchronize();
|
||||
buffer.Dispose();
|
||||
return ProbeResult.Success(
|
||||
"System.Numerics.BigInteger",
|
||||
"Addition, subtraction, multiplication, division, and modulo compile on the CPU accelerator.");
|
||||
@@ -73,9 +74,10 @@ static ProbeResult ProbeEInteger(Accelerator accelerator)
|
||||
try
|
||||
{
|
||||
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<int, Stride1D.Dense>>(EIntegerKernel);
|
||||
using var buffer = accelerator.Allocate1D<int>(1);
|
||||
var buffer = accelerator.Allocate1D<int>(1);
|
||||
kernel(new Index1D((int)buffer.Length), buffer.View);
|
||||
accelerator.Synchronize();
|
||||
buffer.Dispose();
|
||||
return ProbeResult.Success(
|
||||
"PeterO.Numbers.EInteger",
|
||||
"Addition, subtraction, multiplication, division, and remainder compile on the CPU accelerator.");
|
||||
@@ -93,7 +95,7 @@ static ProbeResult ProbeERational(Accelerator accelerator)
|
||||
try
|
||||
{
|
||||
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<int, Stride1D.Dense>>(ERationalKernel);
|
||||
using var buffer = accelerator.Allocate1D<int>(1);
|
||||
var buffer = accelerator.Allocate1D<int>(1);
|
||||
kernel(new Index1D((int)buffer.Length), buffer.View);
|
||||
accelerator.Synchronize();
|
||||
|
||||
@@ -101,6 +103,7 @@ static ProbeResult ProbeERational(Accelerator accelerator)
|
||||
details += hasRemainder
|
||||
? " ERational exposes a remainder API that requires further investigation."
|
||||
: " ERational does not expose a remainder/modulo API.";
|
||||
buffer.Dispose();
|
||||
return ProbeResult.Success("PeterO.Numbers.ERational", details);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -120,9 +123,10 @@ static ProbeResult ProbeOpenNumericPrimesIsPrime(Accelerator accelerator)
|
||||
try
|
||||
{
|
||||
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<int, Stride1D.Dense>>(OpenNumericPrimesIsPrimeKernel);
|
||||
using var buffer = accelerator.Allocate1D<int>(4);
|
||||
var buffer = accelerator.Allocate1D<int>(4);
|
||||
kernel(new Index1D((int)buffer.Length), buffer.View);
|
||||
accelerator.Synchronize();
|
||||
buffer.Dispose();
|
||||
return ProbeResult.Success(
|
||||
"Open.Numeric.Primes.Prime.Numbers.IsPrime",
|
||||
"IsPrime compiles and runs on the CPU accelerator kernel.");
|
||||
|
||||
Reference in new issue
Block a user