Small primes array type changed to uint[] to avoid excessive memory use, especially on GPU

This commit is contained in:
Marek Lemańczyk committed 2025-09-09 23:58:09 +02:00
1 parent dafdc3d6b6
commit 2be49020a9
6 files changed
+61 -88

No files matched your search

@@ -12,9 +12,9 @@ public class PrimesGeneratorTests
var primes = PrimesGenerator.SmallPrimes; var primes = PrimesGenerator.SmallPrimes;
var squares = PrimesGenerator.SmallPrimesPow2; var squares = PrimesGenerator.SmallPrimesPow2;
primes[..5].Should().Equal(new ulong[] { 2UL, 3UL, 5UL, 7UL, 11UL }); primes[..5].Should().Equal([2, 3, 5, 7, 11]);
squares[..5].Should().Equal(new ulong[] { 4UL, 9UL, 25UL, 49UL, 121UL }); squares[..5].Should().Equal([4UL, 9UL, 25UL, 49UL, 121UL]);
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
@@ -3,7 +3,7 @@ namespace PerfectNumbers.Core;
public static class PerfectNumberConstants public static class PerfectNumberConstants
{ {
public const ulong BiggestKnownEvenPerfectP = 136279841UL; public const ulong BiggestKnownEvenPerfectP = 136279841UL;
public static readonly ulong PrimesLimit = 1_000_000; //(ulong)Array.MaxLength;// 1_000_000; public static readonly uint PrimesLimit = 1_000_000; //(ulong)Array.MaxLength;// 1_000_000;
// Maximum K for residue-based Mersenne test (q = 2*p*k + 1 up to this k) // Maximum K for residue-based Mersenne test (q = 2*p*k + 1 up to this k)
public static readonly UInt128 ResidueMaxK = 5_000_000UL; public static readonly UInt128 ResidueMaxK = 5_000_000UL;
+8 -8
View File
@@ -55,7 +55,7 @@ public sealed class PrimeTester(bool useInternal = false)
else else
{ {
var smallPrimeDivisorsLength = PrimesGenerator.SmallPrimes.Length; var smallPrimeDivisorsLength = PrimesGenerator.SmallPrimes.Length;
ulong[] smallPrimeDivisors = PrimesGenerator.SmallPrimes; uint[] smallPrimeDivisors = PrimesGenerator.SmallPrimes;
ulong[] smallPrimeDivisorsMul = PrimesGenerator.SmallPrimesPow2; ulong[] smallPrimeDivisorsMul = PrimesGenerator.SmallPrimesPow2;
for (int i = 0; i < smallPrimeDivisorsLength; i++) for (int i = 0; i < smallPrimeDivisorsLength; i++)
{ {
@@ -128,8 +128,8 @@ public sealed class PrimeTester(bool useInternal = false)
// Per-accelerator GPU state for prime sieve (kernel + uploaded primes). // Per-accelerator GPU state for prime sieve (kernel + uploaded primes).
private sealed class KernelState private sealed class KernelState
{ {
public Action<Index1D, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> Kernel { get; } public Action<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<byte>> Kernel { get; }
public MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimes { get; } public MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimes { get; }
private readonly Accelerator _accel; private readonly Accelerator _accel;
private readonly System.Collections.Concurrent.ConcurrentBag<ScratchBuffers> _scratchPool = new(); private readonly System.Collections.Concurrent.ConcurrentBag<ScratchBuffers> _scratchPool = new();
@@ -137,10 +137,10 @@ public sealed class PrimeTester(bool useInternal = false)
{ {
_accel = accelerator; _accel = accelerator;
// Compile once per accelerator and upload primes once. // Compile once per accelerator and upload primes once.
Kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(SmallPrimeSieveKernel); Kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<byte>>(SmallPrimeSieveKernel);
var primes = PrimesGenerator.SmallPrimes; var primes = PrimesGenerator.SmallPrimes;
DevicePrimes = accelerator.Allocate1D<ulong>(primes.Length); DevicePrimes = accelerator.Allocate1D<uint>(primes.Length);
DevicePrimes.View.CopyFromCPU(primes); DevicePrimes.View.CopyFromCPU(primes);
} }
@@ -232,7 +232,7 @@ public sealed class PrimeTester(bool useInternal = false)
// GPU kernel: small-prime sieve only. Returns 1 if passes sieve (probable prime), 0 otherwise. // GPU kernel: small-prime sieve only. Returns 1 if passes sieve (probable prime), 0 otherwise.
// TODO: Follow-up: consider integration with MR rounds for bases {2,3,5,7,11,13} to make it deterministic for 64-bit. // TODO: Follow-up: consider integration with MR rounds for bases {2,3,5,7,11,13} to make it deterministic for 64-bit.
private static void SmallPrimeSieveKernel(Index1D index, ArrayView<ulong> numbers, ArrayView<ulong> smallPrimes, ArrayView<byte> results) private static void SmallPrimeSieveKernel(Index1D index, ArrayView<ulong> numbers, ArrayView<uint> smallPrimes, ArrayView<byte> results)
{ {
ulong n = numbers[index]; ulong n = numbers[index];
if (n <= 3UL) if (n <= 3UL)
@@ -256,9 +256,9 @@ public sealed class PrimeTester(bool useInternal = false)
} }
long len = smallPrimes.Length; long len = smallPrimes.Length;
for (long i = 0; i < len; i++) for (int i = 0; i < len; i++)
{ {
ulong p = smallPrimes[(int)i]; ulong p = smallPrimes[i];
if (p * p > n) if (p * p > n)
{ {
break; break;
+42 -68
View File
@@ -2,18 +2,14 @@ namespace PerfectNumbers.Core;
public static class PrimesGenerator public static class PrimesGenerator
{ {
private static ulong[]? _primes; private static uint[]? _primes;
private static ulong[]? _primesPow2; private static ulong[]? _primesPow2;
// private static UInt128[]? _primes128;
// private static UInt128[]? _primesPow2_128;
public static readonly ulong[] SmallPrimes = BuildSmallPrimesInternal(); public static readonly uint[] SmallPrimes = BuildSmallPrimesInternal();
public static readonly ulong[] SmallPrimesPow2 = BuildSmallPrimesPow2Internal(); public static readonly ulong[] SmallPrimesPow2 = BuildSmallPrimesPow2Internal();
// public static readonly UInt128[] SmallPrimes128 = BuildSmallPrimes128Internal();
// public static readonly UInt128[] SmallPrimesPow2_128 = BuildSmallPrimesPow2_128Internal();
private static ulong[] BuildSmallPrimesInternal() private static uint[] BuildSmallPrimesInternal()
{ {
if (_primes == null) if (_primes == null)
{ {
@@ -33,74 +29,52 @@ public static class PrimesGenerator
return _primesPow2!; return _primesPow2!;
} }
// private static UInt128[] BuildSmallPrimes128Internal()
// {
// if (_primes == null)
// {
// BuildSmallPrimes();
// }
// return _primes128!;
// }
// private static UInt128[] BuildSmallPrimesPow2_128Internal()
// {
// if (_primesPow2 == null)
// {
// BuildSmallPrimes();
// }
// return _primesPow2_128!;
// }
private static void BuildSmallPrimes() private static void BuildSmallPrimes()
{ {
ulong limit = PerfectNumberConstants.PrimesLimit; uint limit = PerfectNumberConstants.PrimesLimit;
var primes = new ulong[limit]; var primes = new uint[limit];
primes[0] = 2UL; primes[0] = 2;
var primesPow2 = new ulong[limit]; var primesPow2 = new ulong[limit];
primesPow2[0] = 4UL; primesPow2[0] = 4;
int j; int j;
ulong i = 3UL, p, sqrt; uint i = 3, p, sqrt;
bool isPrime; bool isPrime;
int primeIndex = 1; int primeIndex = 1;
for (; i <= limit; i += 2UL) for (; i <= limit; i += 2)
{ {
isPrime = true; isPrime = true;
sqrt = (ulong)Math.Sqrt(i); sqrt = (uint)Math.Sqrt(i);
for (j = 0; j < primeIndex; j++) for (j = 0; j < primeIndex; j++)
{ {
p = primes[j]; p = primes[j];
if (p > sqrt) if (p > sqrt)
{ {
break; break;
} }
if (i % p == 0UL) if (i % p == 0UL)
{ {
isPrime = false; isPrime = false;
break; break;
} }
} }
if (isPrime) if (isPrime)
{ {
primes[primeIndex] = i; primes[primeIndex] = i;
primesPow2[primeIndex] = i * i; primesPow2[primeIndex] = i * i;
primeIndex++; primeIndex++;
} }
} }
Array.Resize(ref primes, primeIndex); Array.Resize(ref primes, primeIndex);
Array.Resize(ref primesPow2, primeIndex); Array.Resize(ref primesPow2, primeIndex);
_primes = primes; _primes = primes;
_primesPow2 = primesPow2; _primesPow2 = primesPow2;
// _primes128 = Array.ConvertAll(_primes, p => (UInt128)p);
// _primesPow2_128 = Array.ConvertAll(_primesPow2, p => (UInt128)p);
return; return;
} }
} }
+3 -3
View File
@@ -52,8 +52,8 @@ public static class UInt128Extensions
} }
ulong order = (ulong)phi; ulong order = (ulong)phi;
ulong[] smallPrimes = PrimesGenerator.SmallPrimes, uint[] smallPrimes = PrimesGenerator.SmallPrimes;
smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2; ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2;
int i = 0, primesLength = smallPrimes.Length; int i = 0, primesLength = smallPrimes.Length;
UInt128 cycle = MersenneDivisorCycles.GetCycle(q); UInt128 cycle = MersenneDivisorCycles.GetCycle(q);
@@ -100,7 +100,7 @@ public static class UInt128Extensions
{ {
UInt128 p, zero = UInt128.Zero; UInt128 p, zero = UInt128.Zero;
ulong[] smallPrimes = PrimesGenerator.SmallPrimes; uint[] smallPrimes = PrimesGenerator.SmallPrimes;
ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2; ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2;
ulong i, smallPrimesCount = (ulong)smallPrimes.Length; ulong i, smallPrimesCount = (ulong)smallPrimes.Length;
for (i = 0UL; i < smallPrimesCount; i++) for (i = 0UL; i < smallPrimesCount; i++)
+5 -6
View File
@@ -12,23 +12,22 @@ public static class ULongExtensions
return 0UL; return 0UL;
} }
ulong order = q - 1UL; ulong order = q - 1UL, prime, temp;
ulong[] smallPrimes = PrimesGenerator.SmallPrimes; uint[] smallPrimes = PrimesGenerator.SmallPrimes;
ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2; ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2;
int i = 0, primesLength = smallPrimes.Length; int i = 0, primesLength = smallPrimes.Length;
ulong prime, temp;
UInt128 q128 = q, UInt128 q128 = q,
cycle = MersenneDivisorCycles.GetCycle(q128); cycle = MersenneDivisorCycles.GetCycle(q128);
for (; i < primesLength; i++) for (; i < primesLength; i++)
{ {
prime = smallPrimes[i];
if (smallPrimesPow2[i] > order) if (smallPrimesPow2[i] > order)
{ {
break; break;
} }
prime = smallPrimes[i];
while (order % prime == 0UL) while (order % prime == 0UL)
{ {
temp = order / prime; temp = order / prime;
@@ -63,7 +62,7 @@ public static class ULongExtensions
{ {
int i = 0; int i = 0;
ulong p; ulong p;
ulong[] smallPrimes = PrimesGenerator.SmallPrimes; uint[] smallPrimes = PrimesGenerator.SmallPrimes;
ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2; ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2;
int len = smallPrimes.Length; int len = smallPrimes.Length;
for (; i < len; i++) for (; i < len; i++)
@@ -388,7 +387,7 @@ public static class ULongExtensions
{ {
ulong value = exponent - 1UL; ulong value = exponent - 1UL;
value >>= BitOperations.TrailingZeroCount(value); value >>= BitOperations.TrailingZeroCount(value);
ulong[] smallPrimes = PrimesGenerator.SmallPrimes; uint[] smallPrimes = PrimesGenerator.SmallPrimes;
ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2; ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2;
int i = 1, smallPrimesLength = smallPrimes.Length; int i = 1, smallPrimesLength = smallPrimes.Length;
ulong prime; ulong prime;