Merge pull request #240 from mlemanczyk/codex/analyze-mersenne=bydivisor-loops-for-early-breaks-kynlfe

Remove temporary by-divisor review documents
This commit is contained in:
Marek Lemańczyk authored and GitHub committed 2025-10-28 02:01:52 +01:00
commit c39a99a4fd
27 files changed
+179 -281

No files matched your search

+2 -12
View File
@@ -104,14 +104,7 @@ internal static class Program
testPrimeCandidateLimit = Math.Max(1, threadCount) * 3; testPrimeCandidateLimit = Math.Max(1, threadCount) * 3;
} }
if (useByDivisor) _byDivisorStartPrime = useByDivisor && startPrimeProvided ? currentP : 0UL;
{
_byDivisorStartPrime = startPrimeProvided ? currentP : 0UL;
}
else
{
_byDivisorStartPrime = 0UL;
}
if (useByDivisor && !testMode && string.IsNullOrEmpty(filterFile)) if (useByDivisor && !testMode && string.IsNullOrEmpty(filterFile))
{ {
@@ -177,10 +170,7 @@ internal static class Program
Console.WriteLine("Divisor cycles are ready"); Console.WriteLine("Divisor cycles are ready");
if (useByDivisor) blockSize = useByDivisor ? 1 : blockSize;
{
blockSize = 1;
}
CandidatesCalculator.Initialize(currentP); CandidatesCalculator.Initialize(currentP);
PrimeTesters = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true); PrimeTesters = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
@@ -18,22 +18,33 @@ public class GpuPrimeLimiterTests
var startedSecond = new TaskCompletionSource<bool>(); var startedSecond = new TaskCompletionSource<bool>();
var sw = new Stopwatch(); var sw = new Stopwatch();
using var first = GpuPrimeWorkLimiter.Acquire(); var first = GpuPrimeWorkLimiter.Acquire();
sw.Start(); sw.Start();
var secondTask = Task.Run(() => var secondTask = Task.Run(() =>
{ {
using var second = GpuPrimeWorkLimiter.Acquire(); var second = GpuPrimeWorkLimiter.Acquire();
try
{
startedSecond.TrySetResult(true); startedSecond.TrySetResult(true);
}
finally
{
second.Dispose();
}
}); });
try
{
// Give secondTask a moment to attempt acquire (it should block). // Give secondTask a moment to attempt acquire (it should block).
await Task.Delay(50); await Task.Delay(50);
startedSecond.Task.IsCompleted.Should().BeFalse(); startedSecond.Task.IsCompleted.Should().BeFalse();
}
// Release first after short delay, allowing second to continue. finally
// (Dispose at scope end, but we want to end now.) {
// Release first after the initial wait so the second task can proceed.
first.Dispose(); first.Dispose();
}
await secondTask.WaitAsync(TimeSpan.FromSeconds(1)); await secondTask.WaitAsync(TimeSpan.FromSeconds(1));
sw.Stop(); sw.Stop();
@@ -17,19 +17,31 @@ public class GpuWorkLimiterTests
var startedSecond = new TaskCompletionSource<bool>(); var startedSecond = new TaskCompletionSource<bool>();
var sw = new Stopwatch(); var sw = new Stopwatch();
using var first = GpuWorkLimiter.Acquire(); var first = GpuWorkLimiter.Acquire();
sw.Start(); sw.Start();
var secondTask = Task.Run(() => var secondTask = Task.Run(() =>
{ {
using var second = GpuWorkLimiter.Acquire(); var second = GpuWorkLimiter.Acquire();
try
{
startedSecond.TrySetResult(true); startedSecond.TrySetResult(true);
}
finally
{
second.Dispose();
}
}); });
try
{
await Task.Delay(50); await Task.Delay(50);
startedSecond.Task.IsCompleted.Should().BeFalse(); startedSecond.Task.IsCompleted.Should().BeFalse();
}
finally
{
first.Dispose(); first.Dispose();
}
await secondTask.WaitAsync(TimeSpan.FromSeconds(1)); await secondTask.WaitAsync(TimeSpan.FromSeconds(1));
sw.Stop(); sw.Stop();
@@ -30,7 +30,9 @@ public class MersenneNumberDivisorCpuTesterTests
var tester = new MersenneNumberDivisorByDivisorCpuTester(); var tester = new MersenneNumberDivisorByDivisorCpuTester();
tester.ConfigureFromMaxPrime(13UL); tester.ConfigureFromMaxPrime(13UL);
using var session = tester.CreateDivisorSession(); var session = tester.CreateDivisorSession();
try
{
ulong[] primes = { 5UL, 7UL, 11UL, 13UL }; ulong[] primes = { 5UL, 7UL, 11UL, 13UL };
byte[] hits = new byte[primes.Length]; byte[] hits = new byte[primes.Length];
@@ -43,6 +45,11 @@ public class MersenneNumberDivisorCpuTesterTests
session.CheckDivisor(31UL, MontgomeryDivisorData.FromModulus(31UL), cycle31, primes, hits); session.CheckDivisor(31UL, MontgomeryDivisorData.FromModulus(31UL), cycle31, primes, hits);
hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 }); hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 });
} }
finally
{
session.Dispose();
}
}
[Fact] [Fact]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
@@ -51,7 +58,9 @@ public class MersenneNumberDivisorCpuTesterTests
var tester = new MersenneNumberDivisorByDivisorCpuTester(); var tester = new MersenneNumberDivisorByDivisorCpuTester();
tester.ConfigureFromMaxPrime(43UL); tester.ConfigureFromMaxPrime(43UL);
using var session = tester.CreateDivisorSession(); var session = tester.CreateDivisorSession();
try
{
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL }; ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
byte[] hits = new byte[exponents.Length]; byte[] hits = new byte[exponents.Length];
@@ -61,6 +70,11 @@ public class MersenneNumberDivisorCpuTesterTests
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 }); hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
} }
finally
{
session.Dispose();
}
}
[Fact] [Fact]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
@@ -69,7 +83,9 @@ public class MersenneNumberDivisorCpuTesterTests
var tester = new MersenneNumberDivisorByDivisorCpuTester(); var tester = new MersenneNumberDivisorByDivisorCpuTester();
tester.ConfigureFromMaxPrime(61UL); tester.ConfigureFromMaxPrime(61UL);
using var session = tester.CreateDivisorSession(); var session = tester.CreateDivisorSession();
try
{
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL }; ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
byte[] hits = new byte[exponents.Length]; byte[] hits = new byte[exponents.Length];
@@ -79,5 +95,10 @@ public class MersenneNumberDivisorCpuTesterTests
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 }); hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
} }
finally
{
session.Dispose();
}
}
} }
@@ -149,7 +149,9 @@ public class MersenneNumberDivisorGpuTesterTests
var tester = new MersenneNumberDivisorByDivisorGpuTester(); var tester = new MersenneNumberDivisorByDivisorGpuTester();
tester.ConfigureFromMaxPrime(43UL); tester.ConfigureFromMaxPrime(43UL);
using var session = tester.CreateDivisorSession(); var session = tester.CreateDivisorSession();
try
{
ulong[] primes = { 31UL, 37UL, 41UL }; ulong[] primes = { 31UL, 37UL, 41UL };
byte[] hits = new byte[primes.Length]; byte[] hits = new byte[primes.Length];
@@ -157,6 +159,11 @@ public class MersenneNumberDivisorGpuTesterTests
hits.Should().ContainInOrder(new byte[] { 0, 1, 0 }); hits.Should().ContainInOrder(new byte[] { 0, 1, 0 });
} }
finally
{
session.Dispose();
}
}
[Fact] [Fact]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
@@ -165,7 +172,9 @@ public class MersenneNumberDivisorGpuTesterTests
var tester = new MersenneNumberDivisorByDivisorGpuTester(); var tester = new MersenneNumberDivisorByDivisorGpuTester();
tester.ConfigureFromMaxPrime(43UL); tester.ConfigureFromMaxPrime(43UL);
using var session = tester.CreateDivisorSession(); var session = tester.CreateDivisorSession();
try
{
ulong[] primes = { 31UL, 37UL, 41UL, 43UL }; ulong[] primes = { 31UL, 37UL, 41UL, 43UL };
byte[] hits = new byte[primes.Length]; byte[] hits = new byte[primes.Length];
@@ -178,6 +187,11 @@ public class MersenneNumberDivisorGpuTesterTests
session.CheckDivisor(13367UL, MontgomeryDivisorData.FromModulus(13367UL), cycle13367, primes, hits); session.CheckDivisor(13367UL, MontgomeryDivisorData.FromModulus(13367UL), cycle13367, primes, hits);
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 }); hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
} }
finally
{
session.Dispose();
}
}
[Fact] [Fact]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
@@ -190,7 +204,9 @@ public class MersenneNumberDivisorGpuTesterTests
tester.ConfigureFromMaxPrime(47UL); tester.ConfigureFromMaxPrime(47UL);
using var session = tester.CreateDivisorSession(); var session = tester.CreateDivisorSession();
try
{
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL }; ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
byte[] hits = new byte[primes.Length]; byte[] hits = new byte[primes.Length];
@@ -199,6 +215,11 @@ public class MersenneNumberDivisorGpuTesterTests
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 }); hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 });
} }
finally
{
session.Dispose();
}
}
[Fact] [Fact]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
@@ -210,7 +231,9 @@ public class MersenneNumberDivisorGpuTesterTests
}; };
tester.ConfigureFromMaxPrime(43UL); tester.ConfigureFromMaxPrime(43UL);
using var session = tester.CreateDivisorSession(); var session = tester.CreateDivisorSession();
try
{
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL }; ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
byte[] hits = new byte[exponents.Length]; byte[] hits = new byte[exponents.Length];
@@ -221,6 +244,11 @@ public class MersenneNumberDivisorGpuTesterTests
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 }); hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
} }
finally
{
session.Dispose();
}
}
[Fact] [Fact]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
@@ -232,7 +260,9 @@ public class MersenneNumberDivisorGpuTesterTests
}; };
tester.ConfigureFromMaxPrime(61UL); tester.ConfigureFromMaxPrime(61UL);
using var session = tester.CreateDivisorSession(); var session = tester.CreateDivisorSession();
try
{
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL }; ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
byte[] hits = new byte[exponents.Length]; byte[] hits = new byte[exponents.Length];
@@ -243,6 +273,11 @@ public class MersenneNumberDivisorGpuTesterTests
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 }); hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
} }
finally
{
session.Dispose();
}
}
[Fact] [Fact]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
@@ -297,7 +332,9 @@ public class MersenneNumberDivisorGpuTesterTests
tester.ConfigureFromMaxPrime(47UL); tester.ConfigureFromMaxPrime(47UL);
using var session = tester.CreateDivisorSession(); var session = tester.CreateDivisorSession();
try
{
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL }; ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
byte[] hits = new byte[primes.Length]; byte[] hits = new byte[primes.Length];
@@ -306,5 +343,10 @@ public class MersenneNumberDivisorGpuTesterTests
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 }); hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 });
} }
finally
{
session.Dispose();
}
}
} }
@@ -2,11 +2,8 @@ namespace PerfectNumbers.Core.Cpu;
internal sealed class MersenneCpuDivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession internal sealed class MersenneCpuDivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
{ {
private bool _disposed;
public void Reset() public void Reset()
{ {
_disposed = false;
} }
public void CheckDivisor( public void CheckDivisor(
@@ -67,12 +64,6 @@ internal sealed class MersenneCpuDivisorScanSession : IMersenneNumberDivisorByDi
public void Dispose() public void Dispose()
{ {
if (_disposed)
{
throw new ObjectDisposedException(nameof(MersenneCpuDivisorScanSession), "Divisor scan session disposed twice.");
}
_disposed = true;
ThreadStaticPools.ReturnMersenneCpuDivisorSession(this); ThreadStaticPools.ReturnMersenneCpuDivisorSession(this);
} }
} }
@@ -145,14 +145,12 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
byte step10; byte step10;
byte step8; byte step8;
byte step5;
byte step3; byte step3;
byte step7; byte step7;
byte step11; byte step11;
byte remainder10; byte remainder10;
byte remainder8; byte remainder8;
byte remainder5;
byte remainder3; byte remainder3;
byte remainder7; byte remainder7;
byte remainder11; byte remainder11;
@@ -161,14 +159,12 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
{ {
step10 = (byte)(stepLow % 10UL); step10 = (byte)(stepLow % 10UL);
step8 = (byte)(stepLow & 7UL); step8 = (byte)(stepLow & 7UL);
step5 = (byte)(stepLow % 5UL);
step3 = (byte)(stepLow % 3UL); step3 = (byte)(stepLow % 3UL);
step7 = (byte)(stepLow % 7UL); step7 = (byte)(stepLow % 7UL);
step11 = (byte)(stepLow % 11UL); step11 = (byte)(stepLow % 11UL);
remainder10 = (byte)(divisorLow % 10UL); remainder10 = (byte)(divisorLow % 10UL);
remainder8 = (byte)(divisorLow & 7UL); remainder8 = (byte)(divisorLow & 7UL);
remainder5 = (byte)(divisorLow % 5UL);
remainder3 = (byte)(divisorLow % 3UL); remainder3 = (byte)(divisorLow % 3UL);
remainder7 = (byte)(divisorLow % 7UL); remainder7 = (byte)(divisorLow % 7UL);
remainder11 = (byte)(divisorLow % 11UL); remainder11 = (byte)(divisorLow % 11UL);
@@ -181,13 +177,11 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
decimalMask, decimalMask,
step10, step10,
step8, step8,
step5,
step3, step3,
step7, step7,
step11, step11,
remainder10, remainder10,
remainder8, remainder8,
remainder5,
remainder3, remainder3,
remainder7, remainder7,
remainder11, remainder11,
@@ -197,21 +191,19 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
ulong divisorHigh = divisor.High; ulong divisorHigh = divisor.High;
step10 = (byte)((((stepHigh % 10UL) * 6UL) + (stepLow % 10UL)) % 10UL); step10 = (byte)((((stepHigh % 10UL) * 6UL) + (stepLow % 10UL)) % 10UL);
step8 = (byte)(stepLow % 8UL); step8 = (byte)(stepLow % 8UL);
step5 = (byte)(((stepHigh % 5UL) + (stepLow % 5UL)) % 5UL);
step3 = (byte)(((stepHigh % 3UL) + (stepLow % 3UL)) % 3UL); step3 = (byte)(((stepHigh % 3UL) + (stepLow % 3UL)) % 3UL);
step7 = (byte)((((stepHigh % 7UL) * 2UL) + (stepLow % 7UL)) % 7UL); step7 = (byte)((((stepHigh % 7UL) * 2UL) + (stepLow % 7UL)) % 7UL);
step11 = (byte)((((stepHigh % 11UL) * 5UL) + (stepLow % 11UL)) % 11UL); step11 = (byte)((((stepHigh % 11UL) * 5UL) + (stepLow % 11UL)) % 11UL);
remainder10 = (byte)((((divisorHigh % 10UL) * 6UL) + (divisorLow % 10UL)) % 10UL); remainder10 = (byte)((((divisorHigh % 10UL) * 6UL) + (divisorLow % 10UL)) % 10UL);
remainder8 = (byte)(divisorLow % 8UL); remainder8 = (byte)(divisorLow % 8UL);
remainder5 = (byte)(((divisorHigh % 5UL) + (divisorLow % 5UL)) % 5UL);
remainder3 = (byte)(((divisorHigh % 3UL) + (divisorLow % 3UL)) % 3UL); remainder3 = (byte)(((divisorHigh % 3UL) + (divisorLow % 3UL)) % 3UL);
remainder7 = (byte)((((divisorHigh % 7UL) * 2UL) + (divisorLow % 7UL)) % 7UL); remainder7 = (byte)((((divisorHigh % 7UL) * 2UL) + (divisorLow % 7UL)) % 7UL);
remainder11 = (byte)((((divisorHigh % 11UL) * 5UL) + (divisorLow % 11UL)) % 11UL); remainder11 = (byte)((((divisorHigh % 11UL) * 5UL) + (divisorLow % 11UL)) % 11UL);
while (divisor.CompareTo(limit) <= 0) while (divisor.CompareTo(limit) <= 0)
{ {
bool passesSmallModuli = remainder3 != 0 && remainder5 != 0 && remainder7 != 0 && remainder11 != 0; bool passesSmallModuli = remainder3 != 0 && remainder7 != 0 && remainder11 != 0;
if (passesSmallModuli && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0) if (passesSmallModuli && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0)
{ {
ulong candidate = divisor.Low; ulong candidate = divisor.Low;
@@ -261,7 +253,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
divisor.Add(step); divisor.Add(step);
remainder10 = AddMod10(remainder10, step10); remainder10 = AddMod10(remainder10, step10);
remainder8 = AddMod8(remainder8, step8); remainder8 = AddMod8(remainder8, step8);
remainder5 = AddMod5(remainder5, step5);
remainder3 = AddMod3(remainder3, step3); remainder3 = AddMod3(remainder3, step3);
remainder7 = AddMod7(remainder7, step7); remainder7 = AddMod7(remainder7, step7);
remainder11 = AddMod11(remainder11, step11); remainder11 = AddMod11(remainder11, step11);
@@ -278,13 +269,11 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
ushort decimalMask, ushort decimalMask,
byte step10, byte step10,
byte step8, byte step8,
byte step5,
byte step3, byte step3,
byte step7, byte step7,
byte step11, byte step11,
byte remainder10, byte remainder10,
byte remainder8, byte remainder8,
byte remainder5,
byte remainder3, byte remainder3,
byte remainder7, byte remainder7,
byte remainder11, byte remainder11,
@@ -297,7 +286,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
{ {
if (divisor <= limit) if (divisor <= limit)
{ {
if (remainder3 != 0 && remainder5 != 0 && remainder7 != 0 && remainder11 != 0 && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0) if (remainder3 != 0 && remainder7 != 0 && remainder11 != 0 && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0)
{ {
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
ulong divisorCycle; ulong divisorCycle;
@@ -345,7 +334,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
while (true) while (true)
{ {
bool passesSmallModuli = remainder3 != 0 && remainder5 != 0 && remainder7 != 0 && remainder11 != 0; bool passesSmallModuli = remainder3 != 0 && remainder7 != 0 && remainder11 != 0;
if (passesSmallModuli && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0) if (passesSmallModuli && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0)
{ {
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
@@ -389,7 +378,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
divisor += step; divisor += step;
remainder10 = AddMod10(remainder10, step10); remainder10 = AddMod10(remainder10, step10);
remainder8 = AddMod8(remainder8, step8); remainder8 = AddMod8(remainder8, step8);
remainder5 = AddMod5(remainder5, step5);
remainder3 = AddMod3(remainder3, step3); remainder3 = AddMod3(remainder3, step3);
remainder7 = AddMod7(remainder7, step7); remainder7 = AddMod7(remainder7, step7);
remainder11 = AddMod11(remainder11, step11); remainder11 = AddMod11(remainder11, step11);
@@ -416,19 +404,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
return (byte)sum; return (byte)sum;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte AddMod5(byte value, byte delta)
{
const int Modulus = 5;
int sum = value + delta;
if (sum >= Modulus)
{
sum -= Modulus;
}
return (byte)sum;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte AddMod7(byte value, byte delta) private static byte AddMod7(byte value, byte delta)
-13
View File
@@ -14,8 +14,6 @@ public static class GpuContextPool
internal sealed class PooledContext internal sealed class PooledContext
{ {
private bool disposed;
public Context Context { get; } public Context Context { get; }
public Accelerator Accelerator { get; } public Accelerator Accelerator { get; }
public bool IsCpu { get; } public bool IsCpu { get; }
@@ -34,10 +32,6 @@ public static class GpuContextPool
public void Dispose() public void Dispose()
{ {
if (disposed)
{
return;
}
// Ensure all cached GPU buffers for this accelerator are released. // Ensure all cached GPU buffers for this accelerator are released.
NttGpuMath.ClearCaches(Accelerator); NttGpuMath.ClearCaches(Accelerator);
@@ -48,7 +42,6 @@ public static class GpuContextPool
Accelerator.Dispose(); Accelerator.Dispose();
Context.Dispose(); Context.Dispose();
disposed = true;
} }
} }
@@ -131,7 +124,6 @@ public static class GpuContextPool
public struct GpuContextLease public struct GpuContextLease
{ {
private readonly PooledContext _ctx; private readonly PooledContext _ctx;
private bool disposed;
internal GpuContextLease(PooledContext ctx, object kernelInitLock) internal GpuContextLease(PooledContext ctx, object kernelInitLock)
{ {
@@ -150,13 +142,8 @@ public static class GpuContextPool
public void Dispose() public void Dispose()
{ {
if (disposed)
{
return;
}
Return(_ctx); Return(_ctx);
disposed = true;
} }
} }
} }
+7 -22
View File
@@ -1,3 +1,4 @@
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using ILGPU; using ILGPU;
using ILGPU.Runtime; using ILGPU.Runtime;
@@ -9,30 +10,28 @@ public sealed class GpuKernelLease
{ {
private static readonly ConcurrentQueue<GpuKernelLease> Pool = new(); private static readonly ConcurrentQueue<GpuKernelLease> Pool = new();
private IDisposable? _limiter; private Action? _releaseLimiter;
private GpuContextLease? _gpu; private GpuContextLease? _gpu;
private KernelContainer? _kernels; private KernelContainer? _kernels;
private AcceleratorStream? _stream; private AcceleratorStream? _stream;
private object? _executionLock; private object? _executionLock;
private bool _disposed;
private GpuKernelLease() private GpuKernelLease()
{ {
} }
internal static GpuKernelLease Rent(IDisposable limiter, 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(); lease = new GpuKernelLease();
} }
lease._limiter = limiter; lease._releaseLimiter = limiter.Dispose;
lease._gpu = gpu; lease._gpu = gpu;
lease._kernels = kernels; lease._kernels = kernels;
lease._stream = null; lease._stream = null;
lease._executionLock = gpu.ExecutionLock; lease._executionLock = gpu.ExecutionLock;
lease._disposed = false;
return lease; return lease;
} }
@@ -158,14 +157,7 @@ public sealed class GpuKernelLease
} }
} }
private void Dispose(bool disposing) public void Dispose()
{
if (_disposed)
{
return;
}
if (disposing)
{ {
_stream?.Dispose(); _stream?.Dispose();
_stream = null; _stream = null;
@@ -175,20 +167,13 @@ public sealed class GpuKernelLease
_executionLock = null; _executionLock = null;
_limiter?.Dispose(); _releaseLimiter?.Invoke();
_limiter = null; _releaseLimiter = null;
}
_kernels = null; _kernels = null;
_disposed = true;
Pool.Enqueue(this); Pool.Enqueue(this);
} }
public void Dispose()
{
Dispose(disposing: false);
}
public readonly struct ExecutionScope public readonly struct ExecutionScope
{ {
private readonly object? _lock; private readonly object? _lock;
+5 -12
View File
@@ -6,13 +6,13 @@ public static class GpuPrimeWorkLimiter
private static readonly object _lock = new(); private static readonly object _lock = new();
private static int _currentLimit = 1; 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 // TODO: Inline the pooled limiter guard from the GpuLimiterThroughputBenchmarks so prime-sieve
// GPU jobs stop allocating Releaser instances on every acquisition. // GPU jobs stop allocating Lease instances on every acquisition.
var sem = _semaphore; var sem = _semaphore;
sem.Wait(); sem.Wait();
return new Releaser(sem); return new Lease(sem);
} }
public static void SetLimit(int value) public static void SetLimit(int value)
@@ -36,25 +36,18 @@ public static class GpuPrimeWorkLimiter
} }
} }
private sealed class Releaser : IDisposable public sealed class Lease
{ {
private readonly SemaphoreSlim _sem; private readonly SemaphoreSlim _sem;
private bool _disposed;
public Releaser(SemaphoreSlim sem) internal Lease(SemaphoreSlim sem)
{ {
_sem = sem; _sem = sem;
} }
public void Dispose() public void Dispose()
{ {
if (_disposed)
{
return;
}
_sem.Release(); _sem.Release();
_disposed = true;
} }
} }
} }
+6 -13
View File
@@ -6,14 +6,14 @@ public static class GpuWorkLimiter
private static readonly object _lock = new(); private static readonly object _lock = new();
private static int _currentLimit = 1; 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 // TODO: Replace the per-call Lease allocation with the pooled struct-based guard from the
// GpuLimiterThroughputBenchmarks so limiter acquisition does not allocate when we enter the // GpuLimiterThroughputBenchmarks so limiter acquisition does not allocate when we enter the
// GPU scanning hot path. // GPU scanning hot path.
var sem = _semaphore; var sem = _semaphore;
sem.Wait(); sem.Wait();
return new Releaser(sem); return new Lease(sem);
} }
public static void SetLimit(int value) public static void SetLimit(int value)
@@ -32,7 +32,7 @@ public static class GpuWorkLimiter
// Swap to a new semaphore for the new limit. We intentionally // Swap to a new semaphore for the new limit. We intentionally
// do not dispose the old semaphore to avoid releasing handles // do not dispose the old semaphore to avoid releasing handles
// that may still be in use by existing Releaser instances. // that may still be in use by existing Lease instances.
// TODO: Consolidate with GpuPrimeWorkLimiter so both limiters share a pooled SemaphoreSlim and avoid rebuilding the // TODO: Consolidate with GpuPrimeWorkLimiter so both limiters share a pooled SemaphoreSlim and avoid rebuilding the
// limiter state whenever limits are adjusted from the CLI. // limiter state whenever limits are adjusted from the CLI.
_semaphore = new SemaphoreSlim(value, value); _semaphore = new SemaphoreSlim(value, value);
@@ -40,25 +40,18 @@ public static class GpuWorkLimiter
} }
} }
private sealed class Releaser : IDisposable public sealed class Lease
{ {
private readonly SemaphoreSlim _sem; private readonly SemaphoreSlim _sem;
private bool _disposed;
public Releaser(SemaphoreSlim sem) internal Lease(SemaphoreSlim sem)
{ {
_sem = sem; _sem = sem;
} }
public void Dispose() public void Dispose()
{ {
if (_disposed)
{
return;
}
_sem.Release(); _sem.Release();
_disposed = true;
} }
} }
} }
@@ -220,7 +220,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
ulong stepValue = (ulong)twoP128; ulong stepValue = (ulong)twoP128;
byte step10 = (byte)(stepValue % 10UL); byte step10 = (byte)(stepValue % 10UL);
byte step8 = (byte)(stepValue & 7UL); byte step8 = (byte)(stepValue & 7UL);
byte step5 = (byte)(stepValue % 5UL);
byte step3 = (byte)(stepValue % 3UL); byte step3 = (byte)(stepValue % 3UL);
byte step7 = (byte)(stepValue % 7UL); byte step7 = (byte)(stepValue % 7UL);
byte step11 = (byte)(stepValue % 11UL); byte step11 = (byte)(stepValue % 11UL);
@@ -229,7 +228,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
ulong currentDivisorValue = (ulong)currentDivisor128; ulong currentDivisorValue = (ulong)currentDivisor128;
byte remainder10 = (byte)(currentDivisorValue % 10UL); byte remainder10 = (byte)(currentDivisorValue % 10UL);
byte remainder8 = (byte)(currentDivisorValue & 7UL); byte remainder8 = (byte)(currentDivisorValue & 7UL);
byte remainder5 = (byte)(currentDivisorValue % 5UL);
byte remainder3 = (byte)(currentDivisorValue % 3UL); byte remainder3 = (byte)(currentDivisorValue % 3UL);
byte remainder7 = (byte)(currentDivisorValue % 7UL); byte remainder7 = (byte)(currentDivisorValue % 7UL);
byte remainder11 = (byte)(currentDivisorValue % 11UL); byte remainder11 = (byte)(currentDivisorValue % 11UL);
@@ -275,14 +273,13 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
byte localRemainder10 = remainder10; byte localRemainder10 = remainder10;
byte localRemainder8 = remainder8; byte localRemainder8 = remainder8;
byte localRemainder5 = remainder5;
byte localRemainder3 = remainder3; byte localRemainder3 = remainder3;
byte localRemainder7 = remainder7; byte localRemainder7 = remainder7;
byte localRemainder11 = remainder11; byte localRemainder11 = remainder11;
for (int i = 0; i < chunkCount; i++) for (int i = 0; i < chunkCount; i++)
{ {
bool passesSmallModuli = localRemainder3 != 0 && localRemainder5 != 0 && localRemainder7 != 0 && localRemainder11 != 0; bool passesSmallModuli = localRemainder3 != 0 && localRemainder7 != 0 && localRemainder11 != 0;
if (passesSmallModuli && (localRemainder8 == 1 || localRemainder8 == 7) && ((decimalMask >> localRemainder10) & 1) != 0) if (passesSmallModuli && (localRemainder8 == 1 || localRemainder8 == 7) && ((decimalMask >> localRemainder10) & 1) != 0)
{ {
ulong divisorValue = (ulong)nextDivisor128; ulong divisorValue = (ulong)nextDivisor128;
@@ -290,21 +287,16 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
ulong divisorCycle = ResolveDivisorCycle(divisorValue, prime, in montgomeryData); ulong divisorCycle = ResolveDivisorCycle(divisorValue, prime, in montgomeryData);
if (divisorCycle == prime) if (divisorCycle == prime)
{ {
int targetIndex = admissibleCount; processedCount += (ulong)(i + 1);
divisorSpan[targetIndex] = divisorValue; lastProcessed = divisorValue;
exponentSpan[targetIndex] = prime; coveredRange = true;
divisorDataSpan[targetIndex] = new GpuDivisorPartialData(divisorValue); return true;
offsetSpan[targetIndex] = targetIndex;
countSpan[targetIndex] = 1;
cycleSpan[targetIndex] = divisorCycle;
admissibleCount++;
} }
} }
nextDivisor128 += twoP128; nextDivisor128 += twoP128;
localRemainder10 = AddMod10(localRemainder10, step10); localRemainder10 = AddMod10(localRemainder10, step10);
localRemainder8 = AddMod8(localRemainder8, step8); localRemainder8 = AddMod8(localRemainder8, step8);
localRemainder5 = AddMod5(localRemainder5, step5);
localRemainder3 = AddMod3(localRemainder3, step3); localRemainder3 = AddMod3(localRemainder3, step3);
localRemainder7 = AddMod7(localRemainder7, step7); localRemainder7 = AddMod7(localRemainder7, step7);
localRemainder11 = AddMod11(localRemainder11, step11); localRemainder11 = AddMod11(localRemainder11, step11);
@@ -312,7 +304,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
remainder10 = localRemainder10; remainder10 = localRemainder10;
remainder8 = localRemainder8; remainder8 = localRemainder8;
remainder5 = localRemainder5;
remainder3 = localRemainder3; remainder3 = localRemainder3;
remainder7 = localRemainder7; remainder7 = localRemainder7;
remainder11 = localRemainder11; remainder11 = localRemainder11;
@@ -380,19 +371,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
return (byte)sum; return (byte)sum;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte AddMod5(byte value, byte delta)
{
const int Modulus = 5;
int sum = value + delta;
if (sum >= Modulus)
{
sum -= Modulus;
}
return (byte)sum;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte AddMod7(byte value, byte delta) private static byte AddMod7(byte value, byte delta)
@@ -490,7 +468,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
private MemoryBuffer1D<byte, Stride1D.Dense> _hitBuffer = null!; private MemoryBuffer1D<byte, Stride1D.Dense> _hitBuffer = null!;
private ulong[] _hostBuffer = null!; private ulong[] _hostBuffer = null!;
private int _capacity; private int _capacity;
private bool _disposed;
internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner) internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner)
{ {
@@ -502,7 +479,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
internal void Reset() internal void Reset()
{ {
_disposed = false;
} }
private void EnsureExecutionResourcesLocked(int requiredCapacity) private void EnsureExecutionResourcesLocked(int requiredCapacity)
@@ -549,11 +525,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan<ulong> primes, Span<byte> hits) public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan<ulong> primes, Span<byte> hits)
{ {
if (_disposed)
{
throw new ObjectDisposedException(nameof(DivisorScanSession));
}
int length = primes.Length; int length = primes.Length;
// EvenPerfectBitScanner always supplies at least one exponent per divisor check, so the guard stays commented out. // EvenPerfectBitScanner always supplies at least one exponent per divisor check, so the guard stays commented out.
// if (length == 0) // if (length == 0)
@@ -622,12 +593,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
int sentinel = int.MaxValue; int sentinel = int.MaxValue;
firstHitView.CopyFromCPU(ref sentinel, 1); firstHitView.CopyFromCPU(ref sentinel, 1);
var kernel = _kernel; var kernel = _kernel!;
if (kernel is null)
{
Monitor.Exit(_lease.ExecutionLock);
throw new InvalidOperationException("GPU divisor kernel was not initialized.");
}
kernel(1, divisorView, offsetView, countView, exponentView, cycleView, hitView, firstHitView); kernel(1, divisorView, offsetView, countView, exponentView, cycleView, hitView, firstHitView);
@@ -640,12 +606,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
public void Dispose() public void Dispose()
{ {
if (_disposed)
{
return;
}
_disposed = true;
_owner._sessionPool.Add(this); _owner._sessionPool.Add(this);
} }
} }
@@ -695,7 +655,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
private void ReturnBatchResources(BatchResources resources) => _resourcePool.Add(resources); private void ReturnBatchResources(BatchResources resources) => _resourcePool.Add(resources);
private sealed class BatchResources : IDisposable private sealed class BatchResources
{ {
private Accelerator? _accelerator; private Accelerator? _accelerator;
private MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense>? _divisorDataBuffer; private MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense>? _divisorDataBuffer;
@@ -47,8 +47,6 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
int currentSize; int currentSize;
int i; int i;
UInt128 q = UInt128.Zero; UInt128 q = UInt128.Zero;
try
{
while (kStart <= maxK && Volatile.Read(ref isPrime)) while (kStart <= maxK && Volatile.Read(ref isPrime))
{ {
remaining = maxK - kStart + UInt128.One; remaining = maxK - kStart + UInt128.One;
@@ -122,13 +120,10 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
kStart += (UInt128)currentSize; kStart += (UInt128)currentSize;
} }
}
finally
{
ArrayPool<ulong>.Shared.Return(orderArray); ArrayPool<ulong>.Shared.Return(orderArray);
orderBuffer.Dispose(); orderBuffer.Dispose();
execution.Dispose(); execution.Dispose();
gpuLease.Dispose(); gpuLease.Dispose();
} }
}
} }
@@ -152,19 +152,13 @@ public class MersenneNumberLucasLehmerGpuTester
} }
GpuUInt128[] buffer = ArrayPool<GpuUInt128>.Shared.Rent(count); GpuUInt128[] buffer = ArrayPool<GpuUInt128>.Shared.Rent(count);
try
{
ComputeResidues(exponents, buffer.AsSpan(0, count)); ComputeResidues(exponents, buffer.AsSpan(0, count));
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
results[i] = buffer[i].IsZero; results[i] = buffer[i].IsZero;
} }
}
finally
{
ArrayPool<GpuUInt128>.Shared.Return(buffer); ArrayPool<GpuUInt128>.Shared.Return(buffer);
} }
}
private bool IsMersennePrimeNtt(ulong exponent, GpuUInt128 nttMod, GpuUInt128 primitiveRoot, bool runOnGpu) private bool IsMersennePrimeNtt(ulong exponent, GpuUInt128 nttMod, GpuUInt128 primitiveRoot, bool runOnGpu)
{ {
@@ -26,8 +26,7 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
var foundBuffer = accelerator.Allocate1D<int>(1); // TODO: Replace this allocation with the pooled device buffer from GpuOrderKernelBenchmarks so repeated scans reuse the pinned staging memory instead of allocating per run. var foundBuffer = accelerator.Allocate1D<int>(1); // TODO: Replace this allocation with the pooled device buffer from GpuOrderKernelBenchmarks so repeated scans reuse the pinned staging memory instead of allocating per run.
exponent.Mod10_8_5_3Steps(out ulong step10, out ulong step8, out ulong step5, out ulong step3); exponent.Mod10_8_5_3Steps(out ulong step10, out ulong step8, out ulong step5, out ulong step3);
try
{
UInt128 remaining; UInt128 remaining;
int currentSize; int currentSize;
int found = 0; int found = 0;
@@ -45,7 +44,6 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
q0.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3); q0.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
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); 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,
@@ -61,13 +59,9 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
kStart += (UInt128)currentSize; kStart += (UInt128)currentSize;
} }
}
finally
{
foundBuffer.Dispose(); foundBuffer.Dispose();
execution.Dispose(); execution.Dispose();
gpuLease.Dispose(); gpuLease.Dispose();
} }
}
} }
@@ -161,7 +161,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
private void ReturnResources(Accelerator accelerator, ResidueResources resources) => private void ReturnResources(Accelerator accelerator, ResidueResources resources) =>
_resourcePools.GetOrAdd(accelerator, static _ => new ConcurrentBag<ResidueResources>()).Add(resources); _resourcePools.GetOrAdd(accelerator, static _ => new ConcurrentBag<ResidueResources>()).Add(resources);
private sealed class ResidueResources : IDisposable private sealed class ResidueResources
{ {
internal ResidueResources(Accelerator accelerator, int capacity) internal ResidueResources(Accelerator accelerator, int capacity)
{ {
+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 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 MemoryBuffer1D<GpuUInt128, Stride1D.Dense> Buffer { get; }
public GpuUInt128 Root { get; } public GpuUInt128 Root { get; }
@@ -16,8 +16,10 @@ public interface IMersenneNumberDivisorByDivisorTester
void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues); void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues);
public interface IDivisorScanSession : IDisposable public interface IDivisorScanSession
{ {
void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan<ulong> primes, Span<byte> hits); void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan<ulong> primes, Span<byte> hits);
void Dispose();
} }
} }
+18 -7
View File
@@ -137,20 +137,31 @@ public class MersenneDivisorCycles
{ {
// Binary pairs: (ulong divisor, ulong cycle) // Binary pairs: (ulong divisor, ulong cycle)
using var reader = new BinaryReader(compressor, Encoding.UTF8, leaveOpen: true); using var reader = new BinaryReader(compressor, Encoding.UTF8, leaveOpen: true);
Stream stream = reader.BaseStream;
byte[] buffer = new byte[16];
while (true) while (true)
{ {
ulong d, c; int read = 0;
try Span<byte> span = buffer;
while (read < span.Length)
{ {
d = reader.ReadUInt64(); int chunk = stream.Read(span[read..]);
c = reader.ReadUInt64(); if (chunk == 0)
} {
catch (EndOfStreamException) if (read == 0)
{ {
yield break; yield break;
} }
yield return (d, c); throw new EndOfStreamException("Incomplete divisor-cycle entry.");
}
read += chunk;
}
ulong divisor = BinaryPrimitives.ReadUInt64LittleEndian(span[..8]);
ulong cycle = BinaryPrimitives.ReadUInt64LittleEndian(span[8..]);
yield return (divisor, cycle);
} }
} }
@@ -167,14 +167,10 @@ public static class MersenneNumberDivisorByDivisorTester
if (primesToTest.Count == 0) if (primesToTest.Count == 0)
{ {
if (applyStartPrime) string message = applyStartPrime
{ ? $"No primes greater than or equal to {startPrime.ToString(CultureInfo.InvariantCulture)} were found for --mersenne=bydivisor."
Console.WriteLine($"No primes greater than or equal to {startPrime.ToString(CultureInfo.InvariantCulture)} were found for --mersenne=bydivisor."); : "No prime candidates remain for --mersenne=bydivisor after filtering.";
} Console.WriteLine(message);
else
{
Console.WriteLine("No prime candidates remain for --mersenne=bydivisor after filtering.");
}
return; return;
} }
+1 -6
View File
@@ -152,8 +152,6 @@ public sealed class MersenneNumberTester(
var stream = gpuLease.Stream; var stream = gpuLease.Stream;
var orderKernel = gpuLease.OrderKernel!; var orderKernel = gpuLease.OrderKernel!;
try
{
// Guard long-running kernels by chunking the warm-up across batches. // Guard long-running kernels by chunking the warm-up across batches.
// Reuse the same ScanBatchSize knob used for scanning. // Reuse the same ScanBatchSize knob used for scanning.
int batchSize = GpuConstants.ScanBatchSize; int batchSize = GpuConstants.ScanBatchSize;
@@ -195,14 +193,11 @@ public sealed class MersenneNumberTester(
orderBuffer.Dispose(); orderBuffer.Dispose();
qBuffer.Dispose(); qBuffer.Dispose();
} }
}
finally
{
execution.Dispose(); execution.Dispose();
gpuLease.Dispose(); gpuLease.Dispose();
ArrayPool<UInt128>.Shared.Return(qs); ArrayPool<UInt128>.Shared.Return(qs);
} }
}
public bool IsMersennePrime(ulong exponent) public bool IsMersennePrime(ulong exponent)
{ {
@@ -14,7 +14,7 @@ public static class MersennePrimeFactorTester
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsPrimeFactor(ulong p, ulong q, CancellationToken ct) public static bool IsPrimeFactor(ulong p, ulong q, CancellationToken ct)
{ {
if (q < 2UL || (q & 1UL) == 0) if (q < 2UL)
{ {
return false; return false;
} }
@@ -19,22 +19,15 @@ internal static partial class PrimeOrderCalculator
var exponentBufferArray = ThreadStaticPools.IntPool.Rent(GpuSmallPrimeFactorSlots); var exponentBufferArray = ThreadStaticPools.IntPool.Rent(GpuSmallPrimeFactorSlots);
Span<ulong> primeBuffer = primeBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots); Span<ulong> primeBuffer = primeBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots);
Span<int> exponentBuffer = exponentBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots); Span<int> exponentBuffer = exponentBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots);
// primeBuffer.Clear();
// exponentBuffer.Clear();
remaining = value; remaining = value;
try
{
var lease = GpuKernelPool.GetKernel(useGpuOrder: true); var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
var execution = lease.EnterExecutionScope(); var execution = lease.EnterExecutionScope();
try
{
Accelerator accelerator = lease.Accelerator; Accelerator accelerator = lease.Accelerator;
AcceleratorStream stream = lease.Stream; AcceleratorStream stream = lease.Stream;
SmallPrimeFactorTables tables = GpuKernelPool.EnsureSmallPrimeFactorTables(accelerator); SmallPrimeFactorTables tables = GpuKernelPool.EnsureSmallPrimeFactorTables(accelerator);
SmallPrimeFactorScratch scratch = GpuKernelPool.EnsureSmallPrimeFactorScratch(accelerator, GpuSmallPrimeFactorSlots); SmallPrimeFactorScratch scratch = GpuKernelPool.EnsureSmallPrimeFactorScratch(accelerator, GpuSmallPrimeFactorSlots);
// scratch.Clear();
var kernel = lease.SmallPrimeFactorKernel; var kernel = lease.SmallPrimeFactorKernel;
kernel( kernel(
@@ -68,27 +61,13 @@ internal static partial class PrimeOrderCalculator
{ {
ulong primeValue = primeBuffer[i]; ulong primeValue = primeBuffer[i];
int exponent = exponentBuffer[i]; int exponent = exponentBuffer[i];
// This will never happen in production code
// if (primeValue == 0UL || exponent == 0)
// {
// continue;
// }
counts.Add(primeValue, exponent); counts.Add(primeValue, exponent);
} }
return true;
}
finally
{
execution.Dispose(); execution.Dispose();
lease.Dispose(); lease.Dispose();
}
}
finally
{
ThreadStaticPools.UlongPool.Return(primeBufferArray); ThreadStaticPools.UlongPool.Return(primeBufferArray);
ThreadStaticPools.IntPool.Return(exponentBufferArray); ThreadStaticPools.IntPool.Return(exponentBufferArray);
} return true;
} }
} }
@@ -193,9 +193,7 @@ internal static partial class PrimeOrderCalculator
// { // {
// return order; // return order;
// } // }
FactorEntry128[]? tempArray = pool.Rent(length + 1); FactorEntry128[] tempArray = pool.Rent(length + 1);
try
{
Span<FactorEntry128> buffer = tempArray.AsSpan(0, length); Span<FactorEntry128> buffer = tempArray.AsSpan(0, length);
factorSpan.CopyTo(buffer); factorSpan.CopyTo(buffer);
@@ -228,12 +226,8 @@ internal static partial class PrimeOrderCalculator
} }
} }
return order;
}
finally
{
pool.Return(tempArray, clearArray: false); pool.Return(tempArray, clearArray: false);
} return order;
} }
private static bool TryConfirmOrderWide(in UInt128 prime, in UInt128 order, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config) private static bool TryConfirmOrderWide(in UInt128 prime, in UInt128 order, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config)
+4 -15
View File
@@ -149,9 +149,8 @@ public sealed class PrimeTester
internal static class PrimeTesterGpuContextPool internal static class PrimeTesterGpuContextPool
{ {
internal sealed class PooledContext : IDisposable internal sealed class PooledContext
{ {
private bool _disposed;
public Context Context { get; } public Context Context { get; }
@@ -167,15 +166,9 @@ public sealed class PrimeTester
public void Dispose() public void Dispose()
{ {
if (_disposed)
{
return;
}
PrimeTester.ClearGpuCaches(Accelerator); PrimeTester.ClearGpuCaches(Accelerator);
Accelerator.Dispose(); Accelerator.Dispose();
Context.Dispose(); Context.Dispose();
_disposed = true;
} }
} }
@@ -209,7 +202,7 @@ public sealed class PrimeTester
Pool.Enqueue(ctx); Pool.Enqueue(ctx);
} }
internal struct PrimeTesterGpuContextLease : IDisposable internal struct PrimeTesterGpuContextLease
{ {
private PooledContext? _ctx; private PooledContext? _ctx;
@@ -224,11 +217,7 @@ public sealed class PrimeTester
public void Dispose() public void Dispose()
{ {
if (_ctx is { } ctx) Return(_ctx ?? throw new InvalidOperationException("GPU context lease is not initialized."));
{
Return(ctx);
_ctx = null;
}
} }
} }
} }
@@ -257,7 +246,7 @@ public sealed class PrimeTester
DevicePrimes.View.CopyFromCPU(primes); DevicePrimes.View.CopyFromCPU(primes);
} }
internal sealed class ScratchBuffers : IDisposable internal sealed class ScratchBuffers
{ {
public MemoryBuffer1D<ulong, Stride1D.Dense> Input { get; private set; } public MemoryBuffer1D<ulong, Stride1D.Dense> Input { get; private set; }
public MemoryBuffer1D<byte, Stride1D.Dense> Output { get; private set; } public MemoryBuffer1D<byte, Stride1D.Dense> Output { get; private set; }
+6 -17
View File
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
internal sealed class TaskThreadPool : IDisposable internal sealed class TaskThreadPool
{ {
private readonly struct WorkItem private readonly struct WorkItem
{ {
@@ -22,8 +22,6 @@ internal sealed class TaskThreadPool : IDisposable
private readonly BlockingCollection<WorkItem> _pendingTasks; private readonly BlockingCollection<WorkItem> _pendingTasks;
private readonly Thread[] _threads; private readonly Thread[] _threads;
private readonly Action<Task> _taskExecutor; private readonly Action<Task> _taskExecutor;
private int _disposed;
public TaskThreadPool(int minimumThreads, Action<Task> taskExecutor) public TaskThreadPool(int minimumThreads, Action<Task> taskExecutor)
{ {
if (minimumThreads <= 0) if (minimumThreads <= 0)
@@ -57,22 +55,18 @@ internal sealed class TaskThreadPool : IDisposable
throw new ArgumentNullException(nameof(task)); throw new ArgumentNullException(nameof(task));
} }
if (Volatile.Read(ref _disposed) != 0)
{
throw new ObjectDisposedException(nameof(TaskThreadPool));
}
try try
{ {
_pendingTasks.Add(new WorkItem(task)); _pendingTasks.Add(new WorkItem(task));
} }
catch (InvalidOperationException ex) when (Volatile.Read(ref _disposed) != 0) catch (Exception ex)
{
if (ex is InvalidOperationException || ex is ObjectDisposedException)
{ {
throw new ObjectDisposedException(nameof(TaskThreadPool), ex); throw new ObjectDisposedException(nameof(TaskThreadPool), ex);
} }
catch (ObjectDisposedException ex)
{ throw;
throw new ObjectDisposedException(nameof(TaskThreadPool), ex);
} }
} }
@@ -97,11 +91,6 @@ internal sealed class TaskThreadPool : IDisposable
public void Dispose() public void Dispose()
{ {
if (Interlocked.Exchange(ref _disposed, 1) != 0)
{
return;
}
_pendingTasks.CompleteAdding(); _pendingTasks.CompleteAdding();
foreach (Thread thread in _threads) foreach (Thread thread in _threads)
+1 -1
View File
@@ -6,7 +6,7 @@ namespace PerfectNumbers.Core;
/// <summary> /// <summary>
/// Provides thread-safe file writing for both text and binary data. /// Provides thread-safe file writing for both text and binary data.
/// </summary> /// </summary>
public sealed class TextFileWriter : IDisposable public sealed class TextFileWriter
{ {
private readonly FileStream _stream; private readonly FileStream _stream;
private StreamWriter? _textWriter; private StreamWriter? _textWriter;