Guard residue order kernels against stale cycle cache entries
This commit is contained in:
1 parent
88d58c6176
commit
0ce17d19ae
4 files changed
+181
-10
No files matched your search
@@ -1,4 +1,9 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using PerfectNumbers.Core;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using UInt128 = System.UInt128;
|
using UInt128 = System.UInt128;
|
||||||
@@ -28,6 +33,60 @@ public class MersenneNumberOrderGpuTesterTests
|
|||||||
RunCase(tester, 127UL, 1_000UL, expectedPrime: true);
|
RunCase(tester, 127UL, 1_000UL, expectedPrime: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Category", "Fast")]
|
||||||
|
public void Scan_order_gpu_handles_inaccurate_small_cycle_entries()
|
||||||
|
{
|
||||||
|
const ulong exponent = 23UL;
|
||||||
|
const ulong divisor = 47UL; // 47 | M_23
|
||||||
|
|
||||||
|
string tempPath = Path.GetTempFileName();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var writer = new BinaryWriter(File.Open(tempPath, FileMode.Create, FileAccess.Write, FileShare.Read)))
|
||||||
|
{
|
||||||
|
writer.Write(divisor);
|
||||||
|
writer.Write((ulong)uint.MaxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cycles = MersenneDivisorCycles.Shared;
|
||||||
|
var tableField = typeof(MersenneDivisorCycles).GetField("_table", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||||
|
var smallCyclesField = typeof(MersenneDivisorCycles).GetField("_smallCycles", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||||
|
|
||||||
|
var originalTable = ((List<(ulong Divisor, ulong Cycle)>)tableField.GetValue(cycles)!).Select(x => x).ToList();
|
||||||
|
var originalSmall = (uint[]?)smallCyclesField.GetValue(cycles);
|
||||||
|
uint[]? backupSmall = originalSmall is null ? null : (uint[])originalSmall.Clone();
|
||||||
|
|
||||||
|
cycles.LoadFrom(tempPath);
|
||||||
|
GpuContextPool.DisposeAll();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tester = new MersenneNumberOrderGpuTester(GpuKernelType.Pow2Mod, useGpuOrder: false);
|
||||||
|
|
||||||
|
bool isPrime = true;
|
||||||
|
tester.Scan(
|
||||||
|
exponent,
|
||||||
|
(UInt128)exponent << 1,
|
||||||
|
LastDigitIsSeven(exponent),
|
||||||
|
(UInt128)10UL,
|
||||||
|
ref isPrime);
|
||||||
|
|
||||||
|
isPrime.Should().BeFalse();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
tableField.SetValue(cycles, originalTable);
|
||||||
|
smallCyclesField.SetValue(cycles, backupSmall);
|
||||||
|
GpuContextPool.DisposeAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
File.Delete(tempPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void RunCase(MersenneNumberOrderGpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
private static void RunCase(MersenneNumberOrderGpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||||
{
|
{
|
||||||
bool isPrime = true;
|
bool isPrime = true;
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using PerfectNumbers.Core;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using UInt128 = System.UInt128;
|
using UInt128 = System.UInt128;
|
||||||
@@ -25,6 +29,66 @@ public class MersenneNumberResidueGpuTesterTests
|
|||||||
RunCase(tester, 127UL, 1_001UL, expectedPrime: true);
|
RunCase(tester, 127UL, 1_001UL, expectedPrime: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Category", "Fast")]
|
||||||
|
public void Scan_residue_gpu_handles_inaccurate_small_cycle_entries()
|
||||||
|
{
|
||||||
|
const ulong exponent = 23UL;
|
||||||
|
const ulong divisor = 47UL; // 47 | M_23
|
||||||
|
|
||||||
|
string tempPath = Path.GetTempFileName();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var writer = new BinaryWriter(File.Open(tempPath, FileMode.Create, FileAccess.Write, FileShare.Read)))
|
||||||
|
{
|
||||||
|
writer.Write(divisor);
|
||||||
|
writer.Write((ulong)uint.MaxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cycles = MersenneDivisorCycles.Shared;
|
||||||
|
var tableField = typeof(MersenneDivisorCycles).GetField("_table", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||||
|
var smallCyclesField = typeof(MersenneDivisorCycles).GetField("_smallCycles", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||||
|
|
||||||
|
var originalTable = ((List<(ulong Divisor, ulong Cycle)>)tableField.GetValue(cycles)!).Select(x => x).ToList();
|
||||||
|
var originalSmall = (uint[]?)smallCyclesField.GetValue(cycles);
|
||||||
|
uint[]? backupSmall = originalSmall is null ? null : (uint[])originalSmall.Clone();
|
||||||
|
|
||||||
|
cycles.LoadFrom(tempPath);
|
||||||
|
GpuContextPool.DisposeAll();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tester = new MersenneNumberResidueGpuTester(useGpuOrder: false);
|
||||||
|
|
||||||
|
bool isPrime = true;
|
||||||
|
bool exhausted = false;
|
||||||
|
|
||||||
|
tester.Scan(
|
||||||
|
exponent,
|
||||||
|
(UInt128)exponent << 1,
|
||||||
|
lastIsSeven: true,
|
||||||
|
perSetLimit: (UInt128)10UL,
|
||||||
|
setCount: UInt128.One,
|
||||||
|
overallLimit: (UInt128)10UL,
|
||||||
|
ref isPrime,
|
||||||
|
ref exhausted);
|
||||||
|
|
||||||
|
isPrime.Should().BeFalse();
|
||||||
|
exhausted.Should().BeTrue();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
tableField.SetValue(cycles, originalTable);
|
||||||
|
smallCyclesField.SetValue(cycles, backupSmall);
|
||||||
|
GpuContextPool.DisposeAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
File.Delete(tempPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(false)]
|
[InlineData(false)]
|
||||||
[InlineData(true)]
|
[InlineData(true)]
|
||||||
|
|||||||
@@ -51,6 +51,38 @@ public class MersenneNumberResidueIntegrationTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(107UL)]
|
||||||
|
[InlineData(127UL)]
|
||||||
|
[Trait("Category", "Fast")]
|
||||||
|
public void Residue_gpu_mode_matches_cli_limits_for_known_primes(ulong exponent)
|
||||||
|
{
|
||||||
|
var tester = new MersenneNumberTester(
|
||||||
|
useIncremental: true,
|
||||||
|
useOrderCache: false,
|
||||||
|
kernelType: GpuKernelType.Pow2Mod,
|
||||||
|
useModuloWorkaround: false,
|
||||||
|
useOrder: false,
|
||||||
|
useGpuLucas: false,
|
||||||
|
useGpuScan: true,
|
||||||
|
useGpuOrder: true,
|
||||||
|
useResidue: true,
|
||||||
|
maxK: (UInt128)5_000_000UL,
|
||||||
|
residueDivisorSets: (UInt128)1_024UL);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool isPrime = tester.IsMersennePrime(exponent, out bool divisorsExhausted);
|
||||||
|
|
||||||
|
isPrime.Should().BeTrue();
|
||||||
|
divisorsExhausted.Should().BeTrue();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
GpuContextPool.DisposeAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(ResidueThreadCounts))]
|
[MemberData(nameof(ResidueThreadCounts))]
|
||||||
[Trait("Category", "Fast")]
|
[Trait("Category", "Fast")]
|
||||||
|
|||||||
@@ -246,10 +246,14 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
|||||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||||
{
|
{
|
||||||
uint cyc = smallCycles[(int)q.Low];
|
uint cyc = smallCycles[(int)q.Low];
|
||||||
if (cyc != 0U && (exponent % cyc) != 0UL)
|
if (cyc != 0U)
|
||||||
{
|
{
|
||||||
orders[index] = 0UL;
|
ulong cycle = cyc;
|
||||||
return;
|
if (cycle <= exponent && (exponent % cycle) != 0UL)
|
||||||
|
{
|
||||||
|
orders[index] = 0UL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,10 +400,14 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
|||||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||||
{
|
{
|
||||||
uint cyc = smallCycles[(int)q.Low];
|
uint cyc = smallCycles[(int)q.Low];
|
||||||
if (cyc != 0U && (exponent % cyc) != 0UL)
|
if (cyc != 0U)
|
||||||
{
|
{
|
||||||
orders[index] = 0UL;
|
ulong cycle = cyc;
|
||||||
return;
|
if (cycle <= exponent && (exponent % cycle) != 0UL)
|
||||||
|
{
|
||||||
|
orders[index] = 0UL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (GpuUInt128.Pow2Minus1Mod(exponent, q) != GpuUInt128.Zero)
|
if (GpuUInt128.Pow2Minus1Mod(exponent, q) != GpuUInt128.Zero)
|
||||||
@@ -524,9 +532,13 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
|||||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||||
{
|
{
|
||||||
uint cyc = smallCycles[(int)q.Low];
|
uint cyc = smallCycles[(int)q.Low];
|
||||||
if (cyc != 0U && (exponent % (ulong)cyc) != 0UL)
|
if (cyc != 0U)
|
||||||
{
|
{
|
||||||
return;
|
ulong cycle = cyc;
|
||||||
|
if (cycle <= exponent && (exponent % cycle) != 0UL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GpuUInt128 phi = q - GpuUInt128.One;
|
GpuUInt128 phi = q - GpuUInt128.One;
|
||||||
@@ -603,9 +615,13 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
|||||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||||
{
|
{
|
||||||
uint cyc = smallCycles[(int)q.Low];
|
uint cyc = smallCycles[(int)q.Low];
|
||||||
if (cyc != 0U && (exponent % (ulong)cyc) != 0UL)
|
if (cyc != 0U)
|
||||||
{
|
{
|
||||||
return;
|
ulong cycle = cyc;
|
||||||
|
if (cycle <= exponent && (exponent % cycle) != 0UL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (GpuUInt128.Pow2Mod(exponent, q) != GpuUInt128.One)
|
if (GpuUInt128.Pow2Mod(exponent, q) != GpuUInt128.One)
|
||||||
|
|||||||
Reference in new issue
Block a user