Merge pull request #143 from mlemanczyk/codex/optimize-random-byte-generation-in-pollardrho

Improve Pollard-Rho random generation performance
This commit is contained in:
Marek Lemańczyk authored and GitHub committed 2025-10-10 10:42:01 +02:00
commit 01ac80466b
4 files changed
+76 -67

No files matched your search

@@ -0,0 +1,49 @@
using System;
using System.Threading;
namespace PerfectNumbers.Core;
internal static class DeterministicRandom
{
[ThreadStatic]
private static ulong s_threadState;
private static long s_seedCounter;
public static ulong NextUInt64()
{
ulong state = s_threadState;
if (state == 0UL)
{
state = InitializeThreadState();
}
state ^= state >> 12;
state ^= state << 25;
state ^= state >> 27;
s_threadState = state;
return state * 2685821657736338717UL;
}
public static UInt128 NextUInt128()
{
UInt128 high = (UInt128)NextUInt64();
UInt128 low = (UInt128)NextUInt64();
return (high << 64) | low;
}
private static ulong InitializeThreadState()
{
ulong seed = (ulong)Interlocked.Increment(ref s_seedCounter);
seed += 0x9E3779B97F4A7C15UL;
seed = (seed ^ (seed >> 30)) * 0xBF58476D1CE4E5B9UL;
seed = (seed ^ (seed >> 27)) * 0x94D049BB133111EBUL;
seed ^= seed >> 31;
if (seed == 0UL)
{
seed = 0x9E3779B97F4A7C15UL;
}
return seed;
}
}
+2 -38
View File
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
using ILGPU; using ILGPU;
using ILGPU.Runtime; using ILGPU.Runtime;
@@ -16,11 +15,6 @@ public class MersenneDivisorCycles
// Lightweight read-mostly cache for small divisors (<= 4,000,000). 0 => unknown // Lightweight read-mostly cache for small divisors (<= 4,000,000). 0 => unknown
private ulong[]? _smallCycles; private ulong[]? _smallCycles;
[ThreadStatic]
private static ulong _threadRandomState;
private static long _randomSeedCounter;
public static MersenneDivisorCycles Shared { get; } = new MersenneDivisorCycles(); public static MersenneDivisorCycles Shared { get; } = new MersenneDivisorCycles();
public MersenneDivisorCycles() public MersenneDivisorCycles()
@@ -535,8 +529,8 @@ public class MersenneDivisorCycles
while (true) while (true)
{ {
ulong c = (NextRandomUInt64() % (n - 1UL)) + 1UL; ulong c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL;
ulong x = (NextRandomUInt64() % (n - 2UL)) + 2UL; ulong x = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL;
ulong y = x; ulong y = x;
ulong d = 1UL; ulong d = 1UL;
@@ -595,36 +589,6 @@ public class MersenneDivisorCycles
} }
} }
private static ulong NextRandomUInt64()
{
ulong state = _threadRandomState;
if (state == 0UL)
{
state = InitializeThreadRandomState();
}
state ^= state >> 12;
state ^= state << 25;
state ^= state >> 27;
_threadRandomState = state;
return state * 2685821657736338717UL;
}
private static ulong InitializeThreadRandomState()
{
ulong seed = (ulong)Interlocked.Increment(ref _randomSeedCounter);
seed += 0x9E3779B97F4A7C15UL;
seed = (seed ^ (seed >> 30)) * 0xBF58476D1CE4E5B9UL;
seed = (seed ^ (seed >> 27)) * 0x94D049BB133111EBUL;
seed ^= seed >> 31;
if (seed == 0UL)
{
seed = 0x9E3779B97F4A7C15UL;
}
return seed;
}
public readonly struct FactorCacheEntry public readonly struct FactorCacheEntry
{ {
public FactorCacheEntry(ulong[] primes, byte[] exponents, int count) public FactorCacheEntry(ulong[] primes, byte[] exponents, int count)
@@ -371,10 +371,6 @@ public static class MersennePrimeFactorTester
return 2UL; return 2UL;
} }
var rng = new Random(1234567); // TODO: Replace this RNG with the deterministic span-based
// sequence measured faster in PollardRhoBenchmarks so we avoid
// per-call Random allocations.
ulong c, d, x, y, diff; ulong c, d, x, y, diff;
while (true) while (true)
{ {
@@ -383,8 +379,8 @@ public static class MersennePrimeFactorTester
return 0UL; return 0UL;
} }
c = (ulong)rng.Next(2, int.MaxValue); c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL;
x = (ulong)rng.Next(2, int.MaxValue); x = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL;
y = x; y = x;
d = 1UL; d = 1UL;
@@ -523,9 +519,6 @@ public static class MersennePrimeFactorTester
return 2; return 2;
} }
var rng = new Random(1234567); // TODO: Replace with the deterministic UInt128 sequence from
// PollardRho128Benchmarks so we eliminate Random allocations.
UInt128 c, d, x, y, diff; UInt128 c, d, x, y, diff;
while (true) while (true)
{ {
@@ -534,8 +527,8 @@ public static class MersennePrimeFactorTester
return 0; return 0;
} }
c = (UInt128)rng.NextInt64(2, long.MaxValue); c = (DeterministicRandom.NextUInt128() % (n - 1)) + 1;
x = (UInt128)rng.NextInt64(2, long.MaxValue); x = (DeterministicRandom.NextUInt128() % (n - 2)) + 2;
y = x; y = x;
d = 1; d = 1;
+21 -18
View File
@@ -4,7 +4,6 @@ using System.Diagnostics;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Cryptography;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
@@ -1165,7 +1164,11 @@ internal static partial class PrimeOrderCalculator
return true; return true;
} }
Span<byte> buffer = stackalloc byte[8]; ulong c;
ulong x;
ulong y;
ulong d;
ulong diff = 0UL;
while (true) while (true)
{ {
@@ -1174,12 +1177,10 @@ internal static partial class PrimeOrderCalculator
return false; return false;
} }
RandomNumberGenerator.Fill(buffer); c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL;
ulong c = (BinaryPrimitives.ReadUInt64LittleEndian(buffer) % (n - 1UL)) + 1UL; x = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL;
RandomNumberGenerator.Fill(buffer); y = x;
ulong x = (BinaryPrimitives.ReadUInt64LittleEndian(buffer) % (n - 2UL)) + 2UL; d = 1UL;
ulong y = x;
ulong d = 1UL;
while (d == 1UL) while (d == 1UL)
{ {
@@ -1191,7 +1192,7 @@ internal static partial class PrimeOrderCalculator
x = AdvancePolynomial(x, c, n); x = AdvancePolynomial(x, c, n);
y = AdvancePolynomial(y, c, n); y = AdvancePolynomial(y, c, n);
y = AdvancePolynomial(y, c, n); y = AdvancePolynomial(y, c, n);
ulong diff = x > y ? x - y : y - x; diff = x > y ? x - y : y - x;
d = BinaryGcd(diff, n); d = BinaryGcd(diff, n);
} }
@@ -1396,23 +1397,25 @@ internal static partial class PrimeOrderCalculator
return 2UL; return 2UL;
} }
Span<byte> buffer = stackalloc byte[8]; ulong c;
ulong x;
ulong y;
ulong d;
ulong diff = 0UL;
while (true) while (true)
{ {
RandomNumberGenerator.Fill(buffer); c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL;
ulong c = (BinaryPrimitives.ReadUInt64LittleEndian(buffer) % (n - 1UL)) + 1UL; x = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL;
y = x;
RandomNumberGenerator.Fill(buffer); d = 1UL;
ulong x = (BinaryPrimitives.ReadUInt64LittleEndian(buffer) % (n - 2UL)) + 2UL;
ulong y = x;
ulong d = 1UL;
while (d == 1UL) while (d == 1UL)
{ {
x = AdvancePolynomial(x, c, n); x = AdvancePolynomial(x, c, n);
y = AdvancePolynomial(y, c, n); y = AdvancePolynomial(y, c, n);
y = AdvancePolynomial(y, c, n); y = AdvancePolynomial(y, c, n);
ulong diff = x > y ? x - y : y - x; diff = x > y ? x - y : y - x;
d = BinaryGcd(diff, n); d = BinaryGcd(diff, n);
} }