Merge pull request #143 from mlemanczyk/codex/optimize-random-byte-generation-in-pollardrho
Improve Pollard-Rho random generation performance
This commit is contained in:
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user