Merge pull request #168 from mlemanczyk/codex/remove-redundant-wrapper-methods-in-mersenne-path

Eliminate IDisposable dependencies
This commit is contained in:
Marek Lemańczyk authored and GitHub committed 2025-10-13 22:16:12 +02:00
commit 7ea7cf527e
28 files changed
+168 -126

No files matched your search

+2 -1
View File
@@ -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");
+15 -22
View File
@@ -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();
}
}
+2 -2
View File
@@ -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;
}
+4 -4
View File
@@ -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;
}
}
+1 -1
View File
@@ -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();
}
}
+6 -3
View File
@@ -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;
+1 -1
View File
@@ -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; }
+1 -1
View File
@@ -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;
+7 -3
View File
@@ -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
View File
@@ -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;
+8 -4
View File
@@ -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.");