Remove temporary by-divisor review documents
This commit is contained in:
1 parent
bd0727131f
commit
6dd5cea79f
27 files changed
+498
-600
No files matched your search
@@ -104,14 +104,7 @@ internal static class Program
|
||||
testPrimeCandidateLimit = Math.Max(1, threadCount) * 3;
|
||||
}
|
||||
|
||||
if (useByDivisor)
|
||||
{
|
||||
_byDivisorStartPrime = startPrimeProvided ? currentP : 0UL;
|
||||
}
|
||||
else
|
||||
{
|
||||
_byDivisorStartPrime = 0UL;
|
||||
}
|
||||
_byDivisorStartPrime = useByDivisor && startPrimeProvided ? currentP : 0UL;
|
||||
|
||||
if (useByDivisor && !testMode && string.IsNullOrEmpty(filterFile))
|
||||
{
|
||||
@@ -177,10 +170,7 @@ internal static class Program
|
||||
|
||||
Console.WriteLine("Divisor cycles are ready");
|
||||
|
||||
if (useByDivisor)
|
||||
{
|
||||
blockSize = 1;
|
||||
}
|
||||
blockSize = useByDivisor ? 1 : blockSize;
|
||||
|
||||
CandidatesCalculator.Initialize(currentP);
|
||||
PrimeTesters = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
||||
|
||||
@@ -18,22 +18,33 @@ 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();
|
||||
startedSecond.TrySetResult(true);
|
||||
var second = GpuPrimeWorkLimiter.Acquire();
|
||||
try
|
||||
{
|
||||
startedSecond.TrySetResult(true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
second.Dispose();
|
||||
}
|
||||
});
|
||||
|
||||
// Give secondTask a moment to attempt acquire (it should block).
|
||||
await Task.Delay(50);
|
||||
startedSecond.Task.IsCompleted.Should().BeFalse();
|
||||
|
||||
// Release first after short delay, allowing second to continue.
|
||||
// (Dispose at scope end, but we want to end now.)
|
||||
first.Dispose();
|
||||
try
|
||||
{
|
||||
// Give secondTask a moment to attempt acquire (it should block).
|
||||
await Task.Delay(50);
|
||||
startedSecond.Task.IsCompleted.Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Release first after the initial wait so the second task can proceed.
|
||||
first.Dispose();
|
||||
}
|
||||
|
||||
await secondTask.WaitAsync(TimeSpan.FromSeconds(1));
|
||||
sw.Stop();
|
||||
|
||||
@@ -17,19 +17,31 @@ 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();
|
||||
startedSecond.TrySetResult(true);
|
||||
var second = GpuWorkLimiter.Acquire();
|
||||
try
|
||||
{
|
||||
startedSecond.TrySetResult(true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
second.Dispose();
|
||||
}
|
||||
});
|
||||
|
||||
await Task.Delay(50);
|
||||
startedSecond.Task.IsCompleted.Should().BeFalse();
|
||||
|
||||
first.Dispose();
|
||||
try
|
||||
{
|
||||
await Task.Delay(50);
|
||||
startedSecond.Task.IsCompleted.Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
first.Dispose();
|
||||
}
|
||||
|
||||
await secondTask.WaitAsync(TimeSpan.FromSeconds(1));
|
||||
sw.Stop();
|
||||
|
||||
@@ -30,18 +30,25 @@ public class MersenneNumberDivisorCpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(13UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
ulong[] primes = { 5UL, 7UL, 11UL, 13UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 5UL, 7UL, 11UL, 13UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
|
||||
ulong cycle23 = MersenneDivisorCycles.CalculateCycleLength(23UL, MontgomeryDivisorData.FromModulus(23UL));
|
||||
session.CheckDivisor(23UL, MontgomeryDivisorData.FromModulus(23UL), cycle23, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
|
||||
ulong cycle23 = MersenneDivisorCycles.CalculateCycleLength(23UL, MontgomeryDivisorData.FromModulus(23UL));
|
||||
session.CheckDivisor(23UL, MontgomeryDivisorData.FromModulus(23UL), cycle23, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
|
||||
|
||||
Array.Fill(hits, (byte)0);
|
||||
ulong cycle31 = MersenneDivisorCycles.CalculateCycleLength(31UL, MontgomeryDivisorData.FromModulus(31UL));
|
||||
session.CheckDivisor(31UL, MontgomeryDivisorData.FromModulus(31UL), cycle31, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 });
|
||||
Array.Fill(hits, (byte)0);
|
||||
ulong cycle31 = MersenneDivisorCycles.CalculateCycleLength(31UL, MontgomeryDivisorData.FromModulus(31UL));
|
||||
session.CheckDivisor(31UL, MontgomeryDivisorData.FromModulus(31UL), cycle31, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -51,15 +58,22 @@ public class MersenneNumberDivisorCpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(7UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(7UL, divisorData);
|
||||
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(7UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(7UL, divisorData);
|
||||
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
|
||||
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -69,15 +83,22 @@ public class MersenneNumberDivisorCpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(61UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(11UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(11UL, divisorData);
|
||||
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(11UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(11UL, divisorData);
|
||||
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
|
||||
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,13 +149,20 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
ulong[] primes = { 31UL, 37UL, 41UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), 0UL, primes, hits);
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), 0UL, primes, hits);
|
||||
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0 });
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -165,18 +172,25 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
|
||||
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(223UL, MontgomeryDivisorData.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0 });
|
||||
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(223UL, MontgomeryDivisorData.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0 });
|
||||
|
||||
Array.Fill(hits, (byte)0);
|
||||
ulong cycle13367 = MersenneDivisorCycles.CalculateCycleLength(13367UL, MontgomeryDivisorData.FromModulus(13367UL));
|
||||
session.CheckDivisor(13367UL, MontgomeryDivisorData.FromModulus(13367UL), cycle13367, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
|
||||
Array.Fill(hits, (byte)0);
|
||||
ulong cycle13367 = MersenneDivisorCycles.CalculateCycleLength(13367UL, MontgomeryDivisorData.FromModulus(13367UL));
|
||||
session.CheckDivisor(13367UL, MontgomeryDivisorData.FromModulus(13367UL), cycle13367, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -190,14 +204,21 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
|
||||
tester.ConfigureFromMaxPrime(47UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
|
||||
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(223UL, MontgomeryDivisorData.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes, hits);
|
||||
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(223UL, MontgomeryDivisorData.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes, hits);
|
||||
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 });
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -210,16 +231,23 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
};
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(7UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(7UL, divisorData);
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(7UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(7UL, divisorData);
|
||||
|
||||
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
|
||||
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
|
||||
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -232,16 +260,23 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
};
|
||||
tester.ConfigureFromMaxPrime(61UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(11UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(11UL, divisorData);
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(11UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(11UL, divisorData);
|
||||
|
||||
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
|
||||
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
|
||||
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -297,14 +332,21 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
|
||||
tester.ConfigureFromMaxPrime(47UL);
|
||||
|
||||
using var session = tester.CreateDivisorSession();
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
|
||||
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(223UL, MontgomeryDivisorData.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes, hits);
|
||||
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(223UL, MontgomeryDivisorData.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes, hits);
|
||||
|
||||
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
|
||||
{
|
||||
private bool _disposed;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_disposed = false;
|
||||
}
|
||||
|
||||
public void CheckDivisor(
|
||||
@@ -67,12 +64,6 @@ internal sealed class MersenneCpuDivisorScanSession : IMersenneNumberDivisorByDi
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(MersenneCpuDivisorScanSession), "Divisor scan session disposed twice.");
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
ThreadStaticPools.ReturnMersenneCpuDivisorSession(this);
|
||||
}
|
||||
}
|
||||
@@ -145,14 +145,12 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
|
||||
byte step10;
|
||||
byte step8;
|
||||
byte step5;
|
||||
byte step3;
|
||||
byte step7;
|
||||
byte step11;
|
||||
|
||||
byte remainder10;
|
||||
byte remainder8;
|
||||
byte remainder5;
|
||||
byte remainder3;
|
||||
byte remainder7;
|
||||
byte remainder11;
|
||||
@@ -161,14 +159,12 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
{
|
||||
step10 = (byte)(stepLow % 10UL);
|
||||
step8 = (byte)(stepLow & 7UL);
|
||||
step5 = (byte)(stepLow % 5UL);
|
||||
step3 = (byte)(stepLow % 3UL);
|
||||
step7 = (byte)(stepLow % 7UL);
|
||||
step11 = (byte)(stepLow % 11UL);
|
||||
|
||||
remainder10 = (byte)(divisorLow % 10UL);
|
||||
remainder8 = (byte)(divisorLow & 7UL);
|
||||
remainder5 = (byte)(divisorLow % 5UL);
|
||||
remainder3 = (byte)(divisorLow % 3UL);
|
||||
remainder7 = (byte)(divisorLow % 7UL);
|
||||
remainder11 = (byte)(divisorLow % 11UL);
|
||||
@@ -181,13 +177,11 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
decimalMask,
|
||||
step10,
|
||||
step8,
|
||||
step5,
|
||||
step3,
|
||||
step7,
|
||||
step11,
|
||||
remainder10,
|
||||
remainder8,
|
||||
remainder5,
|
||||
remainder3,
|
||||
remainder7,
|
||||
remainder11,
|
||||
@@ -197,21 +191,19 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
ulong divisorHigh = divisor.High;
|
||||
step10 = (byte)((((stepHigh % 10UL) * 6UL) + (stepLow % 10UL)) % 10UL);
|
||||
step8 = (byte)(stepLow % 8UL);
|
||||
step5 = (byte)(((stepHigh % 5UL) + (stepLow % 5UL)) % 5UL);
|
||||
step3 = (byte)(((stepHigh % 3UL) + (stepLow % 3UL)) % 3UL);
|
||||
step7 = (byte)((((stepHigh % 7UL) * 2UL) + (stepLow % 7UL)) % 7UL);
|
||||
step11 = (byte)((((stepHigh % 11UL) * 5UL) + (stepLow % 11UL)) % 11UL);
|
||||
|
||||
remainder10 = (byte)((((divisorHigh % 10UL) * 6UL) + (divisorLow % 10UL)) % 10UL);
|
||||
remainder8 = (byte)(divisorLow % 8UL);
|
||||
remainder5 = (byte)(((divisorHigh % 5UL) + (divisorLow % 5UL)) % 5UL);
|
||||
remainder3 = (byte)(((divisorHigh % 3UL) + (divisorLow % 3UL)) % 3UL);
|
||||
remainder7 = (byte)((((divisorHigh % 7UL) * 2UL) + (divisorLow % 7UL)) % 7UL);
|
||||
remainder11 = (byte)((((divisorHigh % 11UL) * 5UL) + (divisorLow % 11UL)) % 11UL);
|
||||
|
||||
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)
|
||||
{
|
||||
ulong candidate = divisor.Low;
|
||||
@@ -261,7 +253,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
divisor.Add(step);
|
||||
remainder10 = AddMod10(remainder10, step10);
|
||||
remainder8 = AddMod8(remainder8, step8);
|
||||
remainder5 = AddMod5(remainder5, step5);
|
||||
remainder3 = AddMod3(remainder3, step3);
|
||||
remainder7 = AddMod7(remainder7, step7);
|
||||
remainder11 = AddMod11(remainder11, step11);
|
||||
@@ -278,13 +269,11 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
ushort decimalMask,
|
||||
byte step10,
|
||||
byte step8,
|
||||
byte step5,
|
||||
byte step3,
|
||||
byte step7,
|
||||
byte step11,
|
||||
byte remainder10,
|
||||
byte remainder8,
|
||||
byte remainder5,
|
||||
byte remainder3,
|
||||
byte remainder7,
|
||||
byte remainder11,
|
||||
@@ -297,7 +286,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
{
|
||||
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);
|
||||
ulong divisorCycle;
|
||||
@@ -345,7 +334,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
|
||||
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)
|
||||
{
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
|
||||
@@ -389,7 +378,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
divisor += step;
|
||||
remainder10 = AddMod10(remainder10, step10);
|
||||
remainder8 = AddMod8(remainder8, step8);
|
||||
remainder5 = AddMod5(remainder5, step5);
|
||||
remainder3 = AddMod3(remainder3, step3);
|
||||
remainder7 = AddMod7(remainder7, step7);
|
||||
remainder11 = AddMod11(remainder11, step11);
|
||||
@@ -416,19 +404,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
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)]
|
||||
private static byte AddMod7(byte value, byte delta)
|
||||
|
||||
@@ -14,8 +14,6 @@ public static class GpuContextPool
|
||||
|
||||
internal sealed class PooledContext
|
||||
{
|
||||
private bool disposed;
|
||||
|
||||
public Context Context { get; }
|
||||
public Accelerator Accelerator { get; }
|
||||
public bool IsCpu { get; }
|
||||
@@ -34,10 +32,6 @@ public static class GpuContextPool
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure all cached GPU buffers for this accelerator are released.
|
||||
NttGpuMath.ClearCaches(Accelerator);
|
||||
@@ -48,7 +42,6 @@ public static class GpuContextPool
|
||||
|
||||
Accelerator.Dispose();
|
||||
Context.Dispose();
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +124,6 @@ public static class GpuContextPool
|
||||
public struct GpuContextLease
|
||||
{
|
||||
private readonly PooledContext _ctx;
|
||||
private bool disposed;
|
||||
|
||||
internal GpuContextLease(PooledContext ctx, object kernelInitLock)
|
||||
{
|
||||
@@ -150,13 +142,8 @@ public static class GpuContextPool
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Return(_ctx);
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using ILGPU;
|
||||
using ILGPU.Runtime;
|
||||
@@ -9,30 +10,28 @@ public sealed class GpuKernelLease
|
||||
{
|
||||
private static readonly ConcurrentQueue<GpuKernelLease> Pool = new();
|
||||
|
||||
private IDisposable? _limiter;
|
||||
private Action? _releaseLimiter;
|
||||
private GpuContextLease? _gpu;
|
||||
private KernelContainer? _kernels;
|
||||
private AcceleratorStream? _stream;
|
||||
private object? _executionLock;
|
||||
private bool _disposed;
|
||||
|
||||
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))
|
||||
{
|
||||
lease = new GpuKernelLease();
|
||||
}
|
||||
|
||||
lease._limiter = limiter;
|
||||
lease._releaseLimiter = limiter.Dispose;
|
||||
lease._gpu = gpu;
|
||||
lease._kernels = kernels;
|
||||
lease._stream = null;
|
||||
lease._executionLock = gpu.ExecutionLock;
|
||||
lease._disposed = false;
|
||||
return lease;
|
||||
}
|
||||
|
||||
@@ -158,35 +157,21 @@ public sealed class GpuKernelLease
|
||||
}
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_stream?.Dispose();
|
||||
_stream = null;
|
||||
|
||||
_gpu?.Dispose();
|
||||
_gpu = null;
|
||||
|
||||
_executionLock = null;
|
||||
|
||||
_limiter?.Dispose();
|
||||
_limiter = null;
|
||||
}
|
||||
|
||||
_kernels = null;
|
||||
_disposed = true;
|
||||
Pool.Enqueue(this);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: false);
|
||||
_stream?.Dispose();
|
||||
_stream = null;
|
||||
|
||||
_gpu?.Dispose();
|
||||
_gpu = null;
|
||||
|
||||
_executionLock = null;
|
||||
|
||||
_releaseLimiter?.Invoke();
|
||||
_releaseLimiter = null;
|
||||
|
||||
_kernels = null;
|
||||
Pool.Enqueue(this);
|
||||
}
|
||||
|
||||
public readonly struct ExecutionScope
|
||||
|
||||
@@ -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.
|
||||
// GPU jobs stop allocating Lease instances on every acquisition.
|
||||
var sem = _semaphore;
|
||||
sem.Wait();
|
||||
return new Releaser(sem);
|
||||
return new Lease(sem);
|
||||
}
|
||||
|
||||
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 bool _disposed;
|
||||
|
||||
public Releaser(SemaphoreSlim sem)
|
||||
internal Lease(SemaphoreSlim sem)
|
||||
{
|
||||
_sem = sem;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_sem.Release();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
// 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
|
||||
// GPU scanning hot path.
|
||||
var sem = _semaphore;
|
||||
sem.Wait();
|
||||
return new Releaser(sem);
|
||||
return new Lease(sem);
|
||||
}
|
||||
|
||||
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
|
||||
// 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
|
||||
// limiter state whenever limits are adjusted from the CLI.
|
||||
_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 bool _disposed;
|
||||
|
||||
public Releaser(SemaphoreSlim sem)
|
||||
internal Lease(SemaphoreSlim sem)
|
||||
{
|
||||
_sem = sem;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_sem.Release();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +220,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
ulong stepValue = (ulong)twoP128;
|
||||
byte step10 = (byte)(stepValue % 10UL);
|
||||
byte step8 = (byte)(stepValue & 7UL);
|
||||
byte step5 = (byte)(stepValue % 5UL);
|
||||
byte step3 = (byte)(stepValue % 3UL);
|
||||
byte step7 = (byte)(stepValue % 7UL);
|
||||
byte step11 = (byte)(stepValue % 11UL);
|
||||
@@ -229,7 +228,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
ulong currentDivisorValue = (ulong)currentDivisor128;
|
||||
byte remainder10 = (byte)(currentDivisorValue % 10UL);
|
||||
byte remainder8 = (byte)(currentDivisorValue & 7UL);
|
||||
byte remainder5 = (byte)(currentDivisorValue % 5UL);
|
||||
byte remainder3 = (byte)(currentDivisorValue % 3UL);
|
||||
byte remainder7 = (byte)(currentDivisorValue % 7UL);
|
||||
byte remainder11 = (byte)(currentDivisorValue % 11UL);
|
||||
@@ -275,14 +273,13 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
|
||||
byte localRemainder10 = remainder10;
|
||||
byte localRemainder8 = remainder8;
|
||||
byte localRemainder5 = remainder5;
|
||||
byte localRemainder3 = remainder3;
|
||||
byte localRemainder7 = remainder7;
|
||||
byte localRemainder11 = remainder11;
|
||||
|
||||
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)
|
||||
{
|
||||
ulong divisorValue = (ulong)nextDivisor128;
|
||||
@@ -290,21 +287,16 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
ulong divisorCycle = ResolveDivisorCycle(divisorValue, prime, in montgomeryData);
|
||||
if (divisorCycle == prime)
|
||||
{
|
||||
int targetIndex = admissibleCount;
|
||||
divisorSpan[targetIndex] = divisorValue;
|
||||
exponentSpan[targetIndex] = prime;
|
||||
divisorDataSpan[targetIndex] = new GpuDivisorPartialData(divisorValue);
|
||||
offsetSpan[targetIndex] = targetIndex;
|
||||
countSpan[targetIndex] = 1;
|
||||
cycleSpan[targetIndex] = divisorCycle;
|
||||
admissibleCount++;
|
||||
processedCount += (ulong)(i + 1);
|
||||
lastProcessed = divisorValue;
|
||||
coveredRange = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
nextDivisor128 += twoP128;
|
||||
localRemainder10 = AddMod10(localRemainder10, step10);
|
||||
localRemainder8 = AddMod8(localRemainder8, step8);
|
||||
localRemainder5 = AddMod5(localRemainder5, step5);
|
||||
localRemainder3 = AddMod3(localRemainder3, step3);
|
||||
localRemainder7 = AddMod7(localRemainder7, step7);
|
||||
localRemainder11 = AddMod11(localRemainder11, step11);
|
||||
@@ -312,7 +304,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
|
||||
remainder10 = localRemainder10;
|
||||
remainder8 = localRemainder8;
|
||||
remainder5 = localRemainder5;
|
||||
remainder3 = localRemainder3;
|
||||
remainder7 = localRemainder7;
|
||||
remainder11 = localRemainder11;
|
||||
@@ -380,19 +371,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
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)]
|
||||
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 ulong[] _hostBuffer = null!;
|
||||
private int _capacity;
|
||||
private bool _disposed;
|
||||
|
||||
internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner)
|
||||
{
|
||||
@@ -502,7 +479,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
_disposed = false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(DivisorScanSession));
|
||||
}
|
||||
|
||||
int length = primes.Length;
|
||||
// EvenPerfectBitScanner always supplies at least one exponent per divisor check, so the guard stays commented out.
|
||||
// if (length == 0)
|
||||
@@ -622,12 +593,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
int sentinel = int.MaxValue;
|
||||
firstHitView.CopyFromCPU(ref sentinel, 1);
|
||||
|
||||
var kernel = _kernel;
|
||||
if (kernel is null)
|
||||
{
|
||||
Monitor.Exit(_lease.ExecutionLock);
|
||||
throw new InvalidOperationException("GPU divisor kernel was not initialized.");
|
||||
}
|
||||
var kernel = _kernel!;
|
||||
|
||||
kernel(1, divisorView, offsetView, countView, exponentView, cycleView, hitView, firstHitView);
|
||||
|
||||
@@ -640,12 +606,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
_owner._sessionPool.Add(this);
|
||||
}
|
||||
}
|
||||
@@ -695,7 +655,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
|
||||
private void ReturnBatchResources(BatchResources resources) => _resourcePool.Add(resources);
|
||||
|
||||
private sealed class BatchResources : IDisposable
|
||||
private sealed class BatchResources
|
||||
{
|
||||
private Accelerator? _accelerator;
|
||||
private MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense>? _divisorDataBuffer;
|
||||
|
||||
@@ -47,88 +47,83 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
|
||||
int currentSize;
|
||||
int i;
|
||||
UInt128 q = UInt128.Zero;
|
||||
try
|
||||
while (kStart <= maxK && Volatile.Read(ref isPrime))
|
||||
{
|
||||
while (kStart <= maxK && Volatile.Read(ref isPrime))
|
||||
remaining = maxK - kStart + UInt128.One;
|
||||
currentSize = remaining > (UInt128)batchSize ? batchSize : (int)remaining;
|
||||
Span<ulong> orders = orderArray.AsSpan(0, currentSize);
|
||||
// Precompute residue automaton bases for this batch
|
||||
UInt128 q0 = twoP * kStart + UInt128.One;
|
||||
if (_kernelType == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
remaining = maxK - kStart + UInt128.One;
|
||||
currentSize = remaining > (UInt128)batchSize ? batchSize : (int)remaining;
|
||||
Span<ulong> orders = orderArray.AsSpan(0, currentSize);
|
||||
// Precompute residue automaton bases for this batch
|
||||
UInt128 q0 = twoP * kStart + UInt128.One;
|
||||
if (_kernelType == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
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);
|
||||
pow2Kernel(
|
||||
stream,
|
||||
currentSize,
|
||||
exponent,
|
||||
twoPGpu,
|
||||
(GpuUInt128)kStart,
|
||||
last,
|
||||
divMul,
|
||||
kernelArgs,
|
||||
orderBuffer.View,
|
||||
smallCyclesView,
|
||||
primeViews.LastOne,
|
||||
primeViews.LastSeven,
|
||||
primeViews.LastOnePow2,
|
||||
primeViews.LastSevenPow2);
|
||||
}
|
||||
else
|
||||
{
|
||||
q0.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
|
||||
incKernel(
|
||||
stream,
|
||||
currentSize,
|
||||
exponent,
|
||||
twoPGpu,
|
||||
(GpuUInt128)kStart,
|
||||
last,
|
||||
divMul,
|
||||
q0m10,
|
||||
q0m8,
|
||||
q0m3,
|
||||
q0m5,
|
||||
orderBuffer.View,
|
||||
smallCyclesView);
|
||||
}
|
||||
|
||||
stream.Synchronize();
|
||||
orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize);
|
||||
if (_kernelType == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
for (i = 0; i < currentSize; i++)
|
||||
{
|
||||
if (orders[i] != 0UL)
|
||||
{
|
||||
Volatile.Write(ref isPrime, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
q = twoP * kStart + UInt128.One;
|
||||
for (i = 0; i < currentSize && Volatile.Read(ref isPrime); i++, q += twoP)
|
||||
{
|
||||
if (orders[i] != 0UL && q.IsPrimeCandidate())
|
||||
{
|
||||
Volatile.Write(ref isPrime, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kStart += (UInt128)currentSize;
|
||||
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);
|
||||
pow2Kernel(
|
||||
stream,
|
||||
currentSize,
|
||||
exponent,
|
||||
twoPGpu,
|
||||
(GpuUInt128)kStart,
|
||||
last,
|
||||
divMul,
|
||||
kernelArgs,
|
||||
orderBuffer.View,
|
||||
smallCyclesView,
|
||||
primeViews.LastOne,
|
||||
primeViews.LastSeven,
|
||||
primeViews.LastOnePow2,
|
||||
primeViews.LastSevenPow2);
|
||||
}
|
||||
else
|
||||
{
|
||||
q0.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
|
||||
incKernel(
|
||||
stream,
|
||||
currentSize,
|
||||
exponent,
|
||||
twoPGpu,
|
||||
(GpuUInt128)kStart,
|
||||
last,
|
||||
divMul,
|
||||
q0m10,
|
||||
q0m8,
|
||||
q0m3,
|
||||
q0m5,
|
||||
orderBuffer.View,
|
||||
smallCyclesView);
|
||||
}
|
||||
|
||||
stream.Synchronize();
|
||||
orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize);
|
||||
if (_kernelType == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
for (i = 0; i < currentSize; i++)
|
||||
{
|
||||
if (orders[i] != 0UL)
|
||||
{
|
||||
Volatile.Write(ref isPrime, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
q = twoP * kStart + UInt128.One;
|
||||
for (i = 0; i < currentSize && Volatile.Read(ref isPrime); i++, q += twoP)
|
||||
{
|
||||
if (orders[i] != 0UL && q.IsPrimeCandidate())
|
||||
{
|
||||
Volatile.Write(ref isPrime, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kStart += (UInt128)currentSize;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<ulong>.Shared.Return(orderArray);
|
||||
orderBuffer.Dispose();
|
||||
execution.Dispose();
|
||||
gpuLease.Dispose();
|
||||
}
|
||||
|
||||
ArrayPool<ulong>.Shared.Return(orderArray);
|
||||
orderBuffer.Dispose();
|
||||
execution.Dispose();
|
||||
gpuLease.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -152,18 +152,12 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
}
|
||||
|
||||
GpuUInt128[] buffer = ArrayPool<GpuUInt128>.Shared.Rent(count);
|
||||
try
|
||||
ComputeResidues(exponents, buffer.AsSpan(0, count));
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ComputeResidues(exponents, buffer.AsSpan(0, count));
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
results[i] = buffer[i].IsZero;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<GpuUInt128>.Shared.Return(buffer);
|
||||
results[i] = buffer[i].IsZero;
|
||||
}
|
||||
ArrayPool<GpuUInt128>.Shared.Return(buffer);
|
||||
}
|
||||
|
||||
private bool IsMersennePrimeNtt(ulong exponent, GpuUInt128 nttMod, GpuUInt128 primitiveRoot, bool runOnGpu)
|
||||
|
||||
@@ -4,19 +4,19 @@ namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuOrder)
|
||||
{
|
||||
private readonly GpuKernelType _kernelType = kernelType;
|
||||
private readonly bool _useGpuOrder = useGpuOrder;
|
||||
private readonly GpuKernelType _kernelType = kernelType;
|
||||
private readonly bool _useGpuOrder = useGpuOrder;
|
||||
|
||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||
{
|
||||
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
|
||||
var execution = gpuLease.EnterExecutionScope();
|
||||
var accelerator = gpuLease.Accelerator;
|
||||
var stream = gpuLease.Stream;
|
||||
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR
|
||||
UInt128 kStart = UInt128.One;
|
||||
ulong divMul = (ulong)((((UInt128)1 << 64) - UInt128.One) / exponent) + 1UL;
|
||||
byte last = lastDigit == LastDigit.Seven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
|
||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||
{
|
||||
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
|
||||
var execution = gpuLease.EnterExecutionScope();
|
||||
var accelerator = gpuLease.Accelerator;
|
||||
var stream = gpuLease.Stream;
|
||||
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR
|
||||
UInt128 kStart = UInt128.One;
|
||||
ulong divMul = (ulong)((((UInt128)1 << 64) - UInt128.One) / exponent) + 1UL;
|
||||
byte last = lastDigit == LastDigit.Seven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
|
||||
|
||||
var kernel = _kernelType switch
|
||||
{
|
||||
@@ -26,48 +26,42 @@ 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.
|
||||
exponent.Mod10_8_5_3Steps(out ulong step10, out ulong step8, out ulong step5, out ulong step3);
|
||||
try
|
||||
|
||||
UInt128 remaining;
|
||||
int currentSize;
|
||||
int found = 0;
|
||||
while (kStart <= maxK && Volatile.Read(ref isPrime))
|
||||
{
|
||||
UInt128 remaining;
|
||||
int currentSize;
|
||||
int found = 0;
|
||||
while (kStart <= maxK && Volatile.Read(ref isPrime))
|
||||
{
|
||||
remaining = maxK - kStart + 1UL;
|
||||
currentSize = remaining > (UInt128)batchSize ? batchSize : (int)remaining;
|
||||
// This clear is required because the result may not always be set. Remove it after the kernels are modified to always set the result.
|
||||
foundBuffer.MemSetToZero();
|
||||
// Precompute residue automaton bases for this batch
|
||||
UInt128 q0 = twoP * kStart + 1UL;
|
||||
// TODO: Replace these modulo recomputations with the divisor-cycle stepping deltas captured in
|
||||
// MersenneDivisorCycleLengthGpuBenchmarks so each batch advances via cached residues from the
|
||||
// single snapshot block instead of recalculating mod 10/8/5/3 in every iteration.
|
||||
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);
|
||||
remaining = maxK - kStart + 1UL;
|
||||
currentSize = remaining > (UInt128)batchSize ? batchSize : (int)remaining;
|
||||
// This clear is required because the result may not always be set. Remove it after the kernels are modified to always set the result.
|
||||
foundBuffer.MemSetToZero();
|
||||
// Precompute residue automaton bases for this batch
|
||||
UInt128 q0 = twoP * kStart + 1UL;
|
||||
// TODO: Replace these modulo recomputations with the divisor-cycle stepping deltas captured in
|
||||
// MersenneDivisorCycleLengthGpuBenchmarks so each batch advances via cached residues from the
|
||||
// single snapshot block instead of recalculating mod 10/8/5/3 in every iteration.
|
||||
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);
|
||||
|
||||
// Ensure device has small cycles table for early rejections in order kernels
|
||||
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
|
||||
kernel(stream, currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, divMul,
|
||||
kernelArgs, foundBuffer.View, smallCyclesView);
|
||||
|
||||
// Ensure device has small cycles table for early rejections in order kernels
|
||||
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
|
||||
kernel(stream, currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, divMul,
|
||||
kernelArgs, foundBuffer.View, smallCyclesView);
|
||||
stream.Synchronize();
|
||||
foundBuffer.View.CopyToCPU(ref found, 1);
|
||||
if (found != 0)
|
||||
{
|
||||
Volatile.Write(ref isPrime, false);
|
||||
break;
|
||||
}
|
||||
|
||||
stream.Synchronize();
|
||||
foundBuffer.View.CopyToCPU(ref found, 1);
|
||||
if (found != 0)
|
||||
{
|
||||
Volatile.Write(ref isPrime, false);
|
||||
break;
|
||||
}
|
||||
kStart += (UInt128)currentSize;
|
||||
}
|
||||
|
||||
kStart += (UInt128)currentSize;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
foundBuffer.Dispose();
|
||||
execution.Dispose();
|
||||
gpuLease.Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
foundBuffer.Dispose();
|
||||
execution.Dispose();
|
||||
gpuLease.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
private void ReturnResources(Accelerator accelerator, ResidueResources 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)
|
||||
{
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -16,8 +16,10 @@ public interface IMersenneNumberDivisorByDivisorTester
|
||||
|
||||
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 Dispose();
|
||||
}
|
||||
}
|
||||
@@ -137,20 +137,31 @@ public class MersenneDivisorCycles
|
||||
{
|
||||
// Binary pairs: (ulong divisor, ulong cycle)
|
||||
using var reader = new BinaryReader(compressor, Encoding.UTF8, leaveOpen: true);
|
||||
Stream stream = reader.BaseStream;
|
||||
byte[] buffer = new byte[16];
|
||||
while (true)
|
||||
{
|
||||
ulong d, c;
|
||||
try
|
||||
int read = 0;
|
||||
Span<byte> span = buffer;
|
||||
while (read < span.Length)
|
||||
{
|
||||
d = reader.ReadUInt64();
|
||||
c = reader.ReadUInt64();
|
||||
}
|
||||
catch (EndOfStreamException)
|
||||
{
|
||||
yield break;
|
||||
int chunk = stream.Read(span[read..]);
|
||||
if (chunk == 0)
|
||||
{
|
||||
if (read == 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
throw new EndOfStreamException("Incomplete divisor-cycle entry.");
|
||||
}
|
||||
|
||||
read += chunk;
|
||||
}
|
||||
|
||||
yield return (d, c);
|
||||
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 (applyStartPrime)
|
||||
{
|
||||
Console.WriteLine($"No primes greater than or equal to {startPrime.ToString(CultureInfo.InvariantCulture)} were found for --mersenne=bydivisor.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("No prime candidates remain for --mersenne=bydivisor after filtering.");
|
||||
}
|
||||
string message = applyStartPrime
|
||||
? $"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);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -152,56 +152,51 @@ public sealed class MersenneNumberTester(
|
||||
var stream = gpuLease.Stream;
|
||||
var orderKernel = gpuLease.OrderKernel!;
|
||||
|
||||
try
|
||||
// Guard long-running kernels by chunking the warm-up across batches.
|
||||
// Reuse the same ScanBatchSize knob used for scanning.
|
||||
int batchSize = GpuConstants.ScanBatchSize;
|
||||
ulong divMul = (ulong)((((UInt128)1 << 64) - 1UL) / exponent) + 1UL;
|
||||
int offset = 0;
|
||||
while (offset < idx)
|
||||
{
|
||||
// Guard long-running kernels by chunking the warm-up across batches.
|
||||
// Reuse the same ScanBatchSize knob used for scanning.
|
||||
int batchSize = GpuConstants.ScanBatchSize;
|
||||
ulong divMul = (ulong)((((UInt128)1 << 64) - 1UL) / exponent) + 1UL;
|
||||
int offset = 0;
|
||||
while (offset < idx)
|
||||
int count = Math.Min(batchSize, idx - offset);
|
||||
var qBuffer = accelerator.Allocate1D<PerfectNumbers.Core.Gpu.GpuUInt128>(count);
|
||||
var orderBuffer = accelerator.Allocate1D<ulong>(count);
|
||||
// Convert to device-friendly GpuUInt128 on the fly
|
||||
var tmp = System.Buffers.ArrayPool<PerfectNumbers.Core.Gpu.GpuUInt128>.Shared.Rent(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int count = Math.Min(batchSize, idx - offset);
|
||||
var qBuffer = accelerator.Allocate1D<PerfectNumbers.Core.Gpu.GpuUInt128>(count);
|
||||
var orderBuffer = accelerator.Allocate1D<ulong>(count);
|
||||
// Convert to device-friendly GpuUInt128 on the fly
|
||||
var tmp = System.Buffers.ArrayPool<PerfectNumbers.Core.Gpu.GpuUInt128>.Shared.Rent(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
tmp[i] = (PerfectNumbers.Core.Gpu.GpuUInt128)qs[offset + i];
|
||||
}
|
||||
// Copy only the populated elements to the device buffer to avoid overrun on pooled arrays.
|
||||
qBuffer.View.CopyFromCPU(ref tmp[0], count);
|
||||
System.Buffers.ArrayPool<PerfectNumbers.Core.Gpu.GpuUInt128>.Shared.Return(tmp);
|
||||
orderKernel(stream, count, exponent, divMul, qBuffer.View, orderBuffer.View);
|
||||
stream.Synchronize();
|
||||
|
||||
ulong[] orders = orderBuffer.GetAsArray1D();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ulong order = orders[i];
|
||||
if (order == 0UL)
|
||||
{
|
||||
order = qs[offset + i].CalculateOrder();
|
||||
}
|
||||
|
||||
if (useOrderCache)
|
||||
{
|
||||
OrderCache[qs[offset + i]] = order;
|
||||
}
|
||||
}
|
||||
|
||||
offset += count;
|
||||
orderBuffer.Dispose();
|
||||
qBuffer.Dispose();
|
||||
tmp[i] = (PerfectNumbers.Core.Gpu.GpuUInt128)qs[offset + i];
|
||||
}
|
||||
// Copy only the populated elements to the device buffer to avoid overrun on pooled arrays.
|
||||
qBuffer.View.CopyFromCPU(ref tmp[0], count);
|
||||
System.Buffers.ArrayPool<PerfectNumbers.Core.Gpu.GpuUInt128>.Shared.Return(tmp);
|
||||
orderKernel(stream, count, exponent, divMul, qBuffer.View, orderBuffer.View);
|
||||
stream.Synchronize();
|
||||
|
||||
ulong[] orders = orderBuffer.GetAsArray1D();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ulong order = orders[i];
|
||||
if (order == 0UL)
|
||||
{
|
||||
order = qs[offset + i].CalculateOrder();
|
||||
}
|
||||
|
||||
if (useOrderCache)
|
||||
{
|
||||
OrderCache[qs[offset + i]] = order;
|
||||
}
|
||||
}
|
||||
|
||||
offset += count;
|
||||
orderBuffer.Dispose();
|
||||
qBuffer.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
execution.Dispose();
|
||||
gpuLease.Dispose();
|
||||
ArrayPool<UInt128>.Shared.Return(qs);
|
||||
}
|
||||
|
||||
execution.Dispose();
|
||||
gpuLease.Dispose();
|
||||
ArrayPool<UInt128>.Shared.Return(qs);
|
||||
}
|
||||
|
||||
public bool IsMersennePrime(ulong exponent)
|
||||
|
||||
@@ -14,7 +14,7 @@ public static class MersennePrimeFactorTester
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsPrimeFactor(ulong p, ulong q, CancellationToken ct)
|
||||
{
|
||||
if (q < 2UL || (q & 1UL) == 0)
|
||||
if (q < 2UL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -9,86 +9,65 @@ namespace PerfectNumbers.Core;
|
||||
|
||||
internal static partial class PrimeOrderCalculator
|
||||
{
|
||||
private static bool IsGpuPow2Allowed => s_pow2ModeInitialized && s_allowGpuPow2;
|
||||
private static bool IsGpuPow2Allowed => s_pow2ModeInitialized && s_allowGpuPow2;
|
||||
|
||||
private const int GpuSmallPrimeFactorSlots = 64;
|
||||
private const int GpuSmallPrimeFactorSlots = 64;
|
||||
|
||||
private static bool TryPopulateSmallPrimeFactorsGpu(ulong value, uint limit, Dictionary<ulong, int> counts, out int factorCount, out ulong remaining)
|
||||
{
|
||||
var primeBufferArray = ThreadStaticPools.UlongPool.Rent(GpuSmallPrimeFactorSlots);
|
||||
var exponentBufferArray = ThreadStaticPools.IntPool.Rent(GpuSmallPrimeFactorSlots);
|
||||
Span<ulong> primeBuffer = primeBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots);
|
||||
Span<int> exponentBuffer = exponentBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots);
|
||||
// primeBuffer.Clear();
|
||||
// exponentBuffer.Clear();
|
||||
remaining = value;
|
||||
private static bool TryPopulateSmallPrimeFactorsGpu(ulong value, uint limit, Dictionary<ulong, int> counts, out int factorCount, out ulong remaining)
|
||||
{
|
||||
var primeBufferArray = ThreadStaticPools.UlongPool.Rent(GpuSmallPrimeFactorSlots);
|
||||
var exponentBufferArray = ThreadStaticPools.IntPool.Rent(GpuSmallPrimeFactorSlots);
|
||||
Span<ulong> primeBuffer = primeBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots);
|
||||
Span<int> exponentBuffer = exponentBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots);
|
||||
remaining = value;
|
||||
|
||||
try
|
||||
{
|
||||
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
||||
var execution = lease.EnterExecutionScope();
|
||||
try
|
||||
{
|
||||
Accelerator accelerator = lease.Accelerator;
|
||||
AcceleratorStream stream = lease.Stream;
|
||||
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
||||
var execution = lease.EnterExecutionScope();
|
||||
Accelerator accelerator = lease.Accelerator;
|
||||
AcceleratorStream stream = lease.Stream;
|
||||
|
||||
SmallPrimeFactorTables tables = GpuKernelPool.EnsureSmallPrimeFactorTables(accelerator);
|
||||
SmallPrimeFactorScratch scratch = GpuKernelPool.EnsureSmallPrimeFactorScratch(accelerator, GpuSmallPrimeFactorSlots);
|
||||
// scratch.Clear();
|
||||
SmallPrimeFactorTables tables = GpuKernelPool.EnsureSmallPrimeFactorTables(accelerator);
|
||||
SmallPrimeFactorScratch scratch = GpuKernelPool.EnsureSmallPrimeFactorScratch(accelerator, GpuSmallPrimeFactorSlots);
|
||||
|
||||
var kernel = lease.SmallPrimeFactorKernel;
|
||||
kernel(
|
||||
stream,
|
||||
1,
|
||||
value,
|
||||
limit,
|
||||
tables.PrimesView,
|
||||
tables.SquaresView,
|
||||
tables.Count,
|
||||
scratch.PrimeSlots.View,
|
||||
scratch.ExponentSlots.View,
|
||||
scratch.CountSlot.View,
|
||||
scratch.RemainingSlot.View);
|
||||
var kernel = lease.SmallPrimeFactorKernel;
|
||||
kernel(
|
||||
stream,
|
||||
1,
|
||||
value,
|
||||
limit,
|
||||
tables.PrimesView,
|
||||
tables.SquaresView,
|
||||
tables.Count,
|
||||
scratch.PrimeSlots.View,
|
||||
scratch.ExponentSlots.View,
|
||||
scratch.CountSlot.View,
|
||||
scratch.RemainingSlot.View);
|
||||
|
||||
stream.Synchronize();
|
||||
stream.Synchronize();
|
||||
|
||||
factorCount = 0;
|
||||
scratch.CountSlot.View.CopyToCPU(ref factorCount, 1);
|
||||
factorCount = Math.Min(factorCount, GpuSmallPrimeFactorSlots);
|
||||
factorCount = 0;
|
||||
scratch.CountSlot.View.CopyToCPU(ref factorCount, 1);
|
||||
factorCount = Math.Min(factorCount, GpuSmallPrimeFactorSlots);
|
||||
|
||||
if (factorCount > 0)
|
||||
{
|
||||
scratch.PrimeSlots.View.CopyToCPU(ref MemoryMarshal.GetReference(primeBuffer), factorCount);
|
||||
scratch.ExponentSlots.View.CopyToCPU(ref MemoryMarshal.GetReference(exponentBuffer), factorCount);
|
||||
}
|
||||
if (factorCount > 0)
|
||||
{
|
||||
scratch.PrimeSlots.View.CopyToCPU(ref MemoryMarshal.GetReference(primeBuffer), factorCount);
|
||||
scratch.ExponentSlots.View.CopyToCPU(ref MemoryMarshal.GetReference(exponentBuffer), factorCount);
|
||||
}
|
||||
|
||||
scratch.RemainingSlot.View.CopyToCPU(ref remaining, 1);
|
||||
scratch.RemainingSlot.View.CopyToCPU(ref remaining, 1);
|
||||
|
||||
for (int i = 0; i < factorCount; i++)
|
||||
{
|
||||
ulong primeValue = primeBuffer[i];
|
||||
int exponent = exponentBuffer[i];
|
||||
// This will never happen in production code
|
||||
// if (primeValue == 0UL || exponent == 0)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
for (int i = 0; i < factorCount; i++)
|
||||
{
|
||||
ulong primeValue = primeBuffer[i];
|
||||
int exponent = exponentBuffer[i];
|
||||
counts.Add(primeValue, exponent);
|
||||
}
|
||||
|
||||
counts.Add(primeValue, exponent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
execution.Dispose();
|
||||
lease.Dispose();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ThreadStaticPools.UlongPool.Return(primeBufferArray);
|
||||
ThreadStaticPools.IntPool.Return(exponentBufferArray);
|
||||
}
|
||||
}
|
||||
execution.Dispose();
|
||||
lease.Dispose();
|
||||
ThreadStaticPools.UlongPool.Return(primeBufferArray);
|
||||
ThreadStaticPools.IntPool.Return(exponentBufferArray);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -185,55 +185,49 @@ internal static partial class PrimeOrderCalculator
|
||||
|
||||
private static UInt128 ExponentLoweringWide(UInt128 order, in UInt128 prime, in MontgomeryDivisorData divisorData, in PartialFactorResult128 factors)
|
||||
{
|
||||
ArrayPool<FactorEntry128> pool = ThreadStaticPools.FactorEntry128Pool;
|
||||
ReadOnlySpan<FactorEntry128> factorSpan = factors.Factors;
|
||||
int length = factors.Count;
|
||||
// TODO: This should never trigger from production code - check
|
||||
// if (length == 0)
|
||||
// {
|
||||
// return order;
|
||||
// }
|
||||
FactorEntry128[]? tempArray = pool.Rent(length + 1);
|
||||
try
|
||||
ArrayPool<FactorEntry128> pool = ThreadStaticPools.FactorEntry128Pool;
|
||||
ReadOnlySpan<FactorEntry128> factorSpan = factors.Factors;
|
||||
int length = factors.Count;
|
||||
// TODO: This should never trigger from production code - check
|
||||
// if (length == 0)
|
||||
// {
|
||||
// return order;
|
||||
// }
|
||||
FactorEntry128[] tempArray = pool.Rent(length + 1);
|
||||
|
||||
Span<FactorEntry128> buffer = tempArray.AsSpan(0, length);
|
||||
factorSpan.CopyTo(buffer);
|
||||
|
||||
if (!factors.FullyFactored && factors.Cofactor > UInt128.One && IsPrimeWide(factors.Cofactor))
|
||||
{
|
||||
buffer[length] = new FactorEntry128(factors.Cofactor, 1);
|
||||
length++;
|
||||
}
|
||||
|
||||
Span<FactorEntry128> buffer = tempArray.AsSpan(0, length);
|
||||
factorSpan.CopyTo(buffer);
|
||||
buffer.Slice(0, length).Sort(static (a, b) => a.Value.CompareTo(b.Value));
|
||||
|
||||
if (!factors.FullyFactored && factors.Cofactor > UInt128.One && IsPrimeWide(factors.Cofactor))
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
UInt128 primeFactor = buffer[i].Value;
|
||||
int exponent = buffer[i].Exponent;
|
||||
for (int iteration = 0; iteration < exponent; iteration++)
|
||||
{
|
||||
buffer[length] = new FactorEntry128(factors.Cofactor, 1);
|
||||
length++;
|
||||
}
|
||||
|
||||
buffer.Slice(0, length).Sort(static (a, b) => a.Value.CompareTo(b.Value));
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
UInt128 primeFactor = buffer[i].Value;
|
||||
int exponent = buffer[i].Exponent;
|
||||
for (int iteration = 0; iteration < exponent; iteration++)
|
||||
if ((order % primeFactor) == UInt128.Zero)
|
||||
{
|
||||
if ((order % primeFactor) == UInt128.Zero)
|
||||
UInt128 reduced = order / primeFactor;
|
||||
if (Pow2ModWide(reduced, prime, divisorData) == UInt128.One)
|
||||
{
|
||||
UInt128 reduced = order / primeFactor;
|
||||
if (Pow2ModWide(reduced, prime, divisorData) == UInt128.One)
|
||||
{
|
||||
order = reduced;
|
||||
continue;
|
||||
}
|
||||
order = reduced;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
finally
|
||||
{
|
||||
pool.Return(tempArray, clearArray: false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pool.Return(tempArray, clearArray: false);
|
||||
return order;
|
||||
}
|
||||
|
||||
private static bool TryConfirmOrderWide(in UInt128 prime, in UInt128 order, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config)
|
||||
|
||||
@@ -149,9 +149,8 @@ public sealed class PrimeTester
|
||||
|
||||
internal static class PrimeTesterGpuContextPool
|
||||
{
|
||||
internal sealed class PooledContext : IDisposable
|
||||
internal sealed class PooledContext
|
||||
{
|
||||
private bool _disposed;
|
||||
|
||||
public Context Context { get; }
|
||||
|
||||
@@ -167,15 +166,9 @@ public sealed class PrimeTester
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrimeTester.ClearGpuCaches(Accelerator);
|
||||
Accelerator.Dispose();
|
||||
Context.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +202,7 @@ public sealed class PrimeTester
|
||||
Pool.Enqueue(ctx);
|
||||
}
|
||||
|
||||
internal struct PrimeTesterGpuContextLease : IDisposable
|
||||
internal struct PrimeTesterGpuContextLease
|
||||
{
|
||||
private PooledContext? _ctx;
|
||||
|
||||
@@ -224,11 +217,7 @@ public sealed class PrimeTester
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_ctx is { } ctx)
|
||||
{
|
||||
Return(ctx);
|
||||
_ctx = null;
|
||||
}
|
||||
Return(_ctx ?? throw new InvalidOperationException("GPU context lease is not initialized."));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,7 +246,7 @@ public sealed class PrimeTester
|
||||
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; }
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
internal sealed class TaskThreadPool : IDisposable
|
||||
internal sealed class TaskThreadPool
|
||||
{
|
||||
private readonly struct WorkItem
|
||||
{
|
||||
@@ -22,8 +22,6 @@ internal sealed class TaskThreadPool : IDisposable
|
||||
private readonly BlockingCollection<WorkItem> _pendingTasks;
|
||||
private readonly Thread[] _threads;
|
||||
private readonly Action<Task> _taskExecutor;
|
||||
private int _disposed;
|
||||
|
||||
public TaskThreadPool(int minimumThreads, Action<Task> taskExecutor)
|
||||
{
|
||||
if (minimumThreads <= 0)
|
||||
@@ -57,22 +55,18 @@ internal sealed class TaskThreadPool : IDisposable
|
||||
throw new ArgumentNullException(nameof(task));
|
||||
}
|
||||
|
||||
if (Volatile.Read(ref _disposed) != 0)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(TaskThreadPool));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_pendingTasks.Add(new WorkItem(task));
|
||||
}
|
||||
catch (InvalidOperationException ex) when (Volatile.Read(ref _disposed) != 0)
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(TaskThreadPool), ex);
|
||||
}
|
||||
catch (ObjectDisposedException ex)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(TaskThreadPool), ex);
|
||||
if (ex is InvalidOperationException || ex is ObjectDisposedException)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(TaskThreadPool), ex);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,11 +91,6 @@ internal sealed class TaskThreadPool : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Interlocked.Exchange(ref _disposed, 1) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_pendingTasks.CompleteAdding();
|
||||
|
||||
foreach (Thread thread in _threads)
|
||||
|
||||
@@ -6,7 +6,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;
|
||||
|
||||
Reference in new issue
Block a user