Ensure GPU residue copies are synchronized and cover high divisor sets
This commit is contained in:
1 parent
633c94906a
commit
7449f146d9
7 files changed
+215
-2
No files matched your search
@@ -1,3 +1,5 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using Xunit;
|
||||
@@ -41,4 +43,69 @@ public class GpuUInt128Pow2Minus1ModTests
|
||||
results[i].Should().Be(single);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pow2Minus1Mod_matches_bigint_for_large_mersenne_residue_candidates()
|
||||
{
|
||||
const ulong exponent = 107UL;
|
||||
const int limit = 500_000;
|
||||
UInt128 twoP = (UInt128)(2UL * exponent);
|
||||
|
||||
for (int k = 1; k <= limit; k++)
|
||||
{
|
||||
UInt128 q = checked(twoP * (UInt128)(ulong)k + UInt128.One);
|
||||
GpuUInt128 gpuRemainder = GpuUInt128.Pow2Minus1Mod(exponent, (GpuUInt128)q);
|
||||
|
||||
BigInteger bigQ = ToBigInteger(q);
|
||||
BigInteger bigPow = BigInteger.ModPow(new BigInteger(2), checked((int)exponent), bigQ);
|
||||
BigInteger bigRemainder = (bigPow - BigInteger.One) % bigQ;
|
||||
if (bigRemainder.Sign < 0)
|
||||
{
|
||||
bigRemainder += bigQ;
|
||||
}
|
||||
|
||||
gpuRemainder.Should().Be(ToGpuUInt128(bigRemainder));
|
||||
}
|
||||
|
||||
UInt128[] largeKs =
|
||||
{
|
||||
UInt128.Parse("1000000000000000"),
|
||||
UInt128.Parse("1000000000000000000"),
|
||||
UInt128.Parse("1000000000000000000000"),
|
||||
UInt128.Parse("99999999998192000000000")
|
||||
};
|
||||
|
||||
foreach (UInt128 kValue in largeKs)
|
||||
{
|
||||
UInt128 q = checked(twoP * kValue + UInt128.One);
|
||||
GpuUInt128 gpuRemainder = GpuUInt128.Pow2Minus1Mod(exponent, (GpuUInt128)q);
|
||||
|
||||
BigInteger bigQ = ToBigInteger(q);
|
||||
BigInteger bigPow = BigInteger.ModPow(new BigInteger(2), checked((int)exponent), bigQ);
|
||||
BigInteger bigRemainder = (bigPow - BigInteger.One) % bigQ;
|
||||
if (bigRemainder.Sign < 0)
|
||||
{
|
||||
bigRemainder += bigQ;
|
||||
}
|
||||
|
||||
gpuRemainder.Should().Be(ToGpuUInt128(bigRemainder));
|
||||
}
|
||||
}
|
||||
|
||||
private static BigInteger ToBigInteger(UInt128 value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[16];
|
||||
buffer.Clear();
|
||||
Unsafe.WriteUnaligned(ref buffer[0], value);
|
||||
return new BigInteger(buffer, isUnsigned: true, isBigEndian: false);
|
||||
}
|
||||
|
||||
private static GpuUInt128 ToGpuUInt128(BigInteger value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[16];
|
||||
buffer.Clear();
|
||||
value.TryWriteBytes(buffer, out _, isUnsigned: true, isBigEndian: false);
|
||||
UInt128 converted = Unsafe.ReadUnaligned<UInt128>(ref buffer[0]);
|
||||
return (GpuUInt128)converted;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Threading;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
|
||||
[Trait("Category", "Fast")]
|
||||
public class PrimeTesterGpuTests
|
||||
{
|
||||
[Fact]
|
||||
public void IsPrimeGpu_accepts_known_primes()
|
||||
{
|
||||
GpuContextPool.ForceCpu = false;
|
||||
try
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
ulong[] primes = [5UL, 31UL, 61UL, 89UL, 107UL, 127UL];
|
||||
foreach (ulong prime in primes)
|
||||
{
|
||||
tester.IsPrimeGpu(prime, CancellationToken.None).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
GpuContextPool.ForceCpu = false;
|
||||
GpuContextPool.DisposeAll();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsPrimeGpu_rejects_composites()
|
||||
{
|
||||
GpuContextPool.ForceCpu = false;
|
||||
try
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
ulong[] composites = [21UL, 25UL, 27UL, 35UL, 55UL];
|
||||
foreach (ulong composite in composites)
|
||||
{
|
||||
tester.IsPrimeGpu(composite, CancellationToken.None).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
GpuContextPool.ForceCpu = false;
|
||||
GpuContextPool.DisposeAll();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -65,6 +65,17 @@ public class MersenneNumberResidueGpuTesterTests
|
||||
RunCase(new MersenneNumberResidueGpuTester(useGpuOrder: true), exponent, perSetLimit, setCount, overallLimit, expectedPrime: true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(107UL, 1UL, 1_024UL)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Scan_handles_single_lane_sets_for_known_primes(ulong exponent, ulong perSetLimit, ulong overallLimit)
|
||||
{
|
||||
ulong setCount = overallLimit;
|
||||
|
||||
RunCase(new MersenneNumberResidueGpuTester(useGpuOrder: false), exponent, perSetLimit, setCount, overallLimit, expectedPrime: true);
|
||||
RunCase(new MersenneNumberResidueGpuTester(useGpuOrder: true), exponent, perSetLimit, setCount, overallLimit, expectedPrime: true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
|
||||
@@ -19,6 +19,38 @@ public class MersenneNumberResidueIntegrationTests
|
||||
yield return new object[] { 4 };
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(107UL)]
|
||||
[InlineData(127UL)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Residue_gpu_mode_handles_large_residue_set_counts_for_known_primes(ulong exponent)
|
||||
{
|
||||
var tester = new MersenneNumberTester(
|
||||
useIncremental: false,
|
||||
useOrderCache: false,
|
||||
kernelType: GpuKernelType.Pow2Mod,
|
||||
useModuloWorkaround: false,
|
||||
useOrder: false,
|
||||
useGpuLucas: false,
|
||||
useGpuScan: true,
|
||||
useGpuOrder: true,
|
||||
useResidue: true,
|
||||
maxK: (UInt128)1_024UL,
|
||||
residueDivisorSets: (UInt128)1_024UL);
|
||||
|
||||
try
|
||||
{
|
||||
bool isPrime = tester.IsMersennePrime(exponent, out bool divisorsExhausted);
|
||||
|
||||
isPrime.Should().BeTrue();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
GpuContextPool.DisposeAll();
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ResidueThreadCounts))]
|
||||
[Trait("Category", "Fast")]
|
||||
|
||||
Reference in new issue
Block a user