Merge pull request #6 from mlemanczyk/codex/optimize-evenperfectbitscanner-calculations

Add batch Mersenne residue computation
This commit is contained in:
Marek Lemańczyk authored and GitHub committed 2025-09-10 18:06:22 +02:00
commit d8c73c4edc
3 files changed
+156 -1

No files matched your search

@@ -0,0 +1,34 @@
using FluentAssertions;
using PerfectNumbers.Core.Gpu;
using Xunit;
namespace PerfectNumbers.Core.Tests;
[Trait("Category", "Fast")]
public class GpuUInt128Pow2Minus1ModTests
{
[Fact]
public void Pow2Minus1Mod_matches_pow2mod_minus_one()
{
ulong exponent = 23UL;
GpuUInt128 modulus = new(0UL, 97UL);
var pow = GpuUInt128.Pow2Mod(exponent, modulus);
var expected = pow.IsZero ? modulus.Sub(GpuUInt128.One) : pow.Sub(GpuUInt128.One);
var actual = GpuUInt128.Pow2Minus1Mod(exponent, modulus);
actual.Should().Be(expected);
}
[Fact]
public void Pow2Minus1ModBatch_matches_individual()
{
ulong[] exponents = { 1UL, 2UL, 3UL, 4UL, 5UL, 6UL, 7UL, 8UL };
Span<GpuUInt128> results = stackalloc GpuUInt128[exponents.Length];
GpuUInt128 modulus = new(0UL, 97UL);
GpuUInt128.Pow2Minus1ModBatch(modulus, exponents, results);
for (int i = 0; i < exponents.Length; i++)
{
var single = GpuUInt128.Pow2Minus1Mod(exponents[i], modulus);
results[i].Should().Be(single);
}
}
}
+1 -1
View File
@@ -326,7 +326,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
return;
}
}
if (GpuUInt128.Pow2Mod(exponent, q) != GpuUInt128.One)
if (GpuUInt128.Pow2Minus1Mod(exponent, q) != GpuUInt128.Zero)
{
orders[index] = 0UL;
return;
+121
View File
@@ -214,6 +214,127 @@ public readonly struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt1
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static GpuUInt128 Pow2Minus1Mod(ulong exponent, GpuUInt128 modulus)
{
if (modulus.IsZero)
{
return Zero;
}
GpuUInt128 result = Zero;
ulong i = 0UL;
while (i < exponent)
{
// result = (result * 2) % modulus
result += result;
if (result.CompareTo(modulus) >= 0)
{
result -= modulus;
}
// result = (result + 1) % modulus
result += One;
if (result.CompareTo(modulus) >= 0)
{
result -= modulus;
}
i++;
}
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Pow2Minus1ModBatch(GpuUInt128 modulus, ReadOnlySpan<ulong> exponents, Span<GpuUInt128> results)
{
GpuUInt128 r0 = Zero, r1 = Zero, r2 = Zero, r3 = Zero, r4 = Zero, r5 = Zero, r6 = Zero, r7 = Zero;
ulong e0 = 0UL, e1 = 0UL, e2 = 0UL, e3 = 0UL, e4 = 0UL, e5 = 0UL, e6 = 0UL, e7 = 0UL;
bool a0 = false, a1 = false, a2 = false, a3 = false, a4 = false, a5 = false, a6 = false, a7 = false;
int len = exponents.Length;
if (len > 0) { e0 = exponents[0]; a0 = true; }
if (len > 1) { e1 = exponents[1]; a1 = true; }
if (len > 2) { e2 = exponents[2]; a2 = true; }
if (len > 3) { e3 = exponents[3]; a3 = true; }
if (len > 4) { e4 = exponents[4]; a4 = true; }
if (len > 5) { e5 = exponents[5]; a5 = true; }
if (len > 6) { e6 = exponents[6]; a6 = true; }
if (len > 7) { e7 = exponents[7]; a7 = true; }
ulong max = 0UL;
if (a0 && e0 > max) max = e0;
if (a1 && e1 > max) max = e1;
if (a2 && e2 > max) max = e2;
if (a3 && e3 > max) max = e3;
if (a4 && e4 > max) max = e4;
if (a5 && e5 > max) max = e5;
if (a6 && e6 > max) max = e6;
if (a7 && e7 > max) max = e7;
for (ulong i = 0UL; i < max; i++)
{
if (a0)
{
r0 += r0; if (r0.CompareTo(modulus) >= 0) r0 -= modulus;
r0 += One; if (r0.CompareTo(modulus) >= 0) r0 -= modulus;
if (i + 1UL == e0) a0 = false;
}
if (a1)
{
r1 += r1; if (r1.CompareTo(modulus) >= 0) r1 -= modulus;
r1 += One; if (r1.CompareTo(modulus) >= 0) r1 -= modulus;
if (i + 1UL == e1) a1 = false;
}
if (a2)
{
r2 += r2; if (r2.CompareTo(modulus) >= 0) r2 -= modulus;
r2 += One; if (r2.CompareTo(modulus) >= 0) r2 -= modulus;
if (i + 1UL == e2) a2 = false;
}
if (a3)
{
r3 += r3; if (r3.CompareTo(modulus) >= 0) r3 -= modulus;
r3 += One; if (r3.CompareTo(modulus) >= 0) r3 -= modulus;
if (i + 1UL == e3) a3 = false;
}
if (a4)
{
r4 += r4; if (r4.CompareTo(modulus) >= 0) r4 -= modulus;
r4 += One; if (r4.CompareTo(modulus) >= 0) r4 -= modulus;
if (i + 1UL == e4) a4 = false;
}
if (a5)
{
r5 += r5; if (r5.CompareTo(modulus) >= 0) r5 -= modulus;
r5 += One; if (r5.CompareTo(modulus) >= 0) r5 -= modulus;
if (i + 1UL == e5) a5 = false;
}
if (a6)
{
r6 += r6; if (r6.CompareTo(modulus) >= 0) r6 -= modulus;
r6 += One; if (r6.CompareTo(modulus) >= 0) r6 -= modulus;
if (i + 1UL == e6) a6 = false;
}
if (a7)
{
r7 += r7; if (r7.CompareTo(modulus) >= 0) r7 -= modulus;
r7 += One; if (r7.CompareTo(modulus) >= 0) r7 -= modulus;
if (i + 1UL == e7) a7 = false;
}
}
if (len > 0) results[0] = r0;
if (len > 1) results[1] = r1;
if (len > 2) results[2] = r2;
if (len > 3) results[3] = r3;
if (len > 4) results[4] = r4;
if (len > 5) results[5] = r5;
if (len > 6) results[6] = r6;
if (len > 7) results[7] = r7;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public GpuUInt128 AddMod(GpuUInt128 value, ulong modulus)
{