Cache benchmark sample data across iterations

This commit is contained in:
Marek Lemańczyk committed 2025-10-18 01:09:59 +02:00
1 parent d968f553af
commit a1bbd23594
4 files changed
+409 -178

No files matched your search

@@ -9,12 +9,10 @@ namespace EvenPerfectBitScanner.Benchmarks;
public class GpuModularArithmeticBenchmarks public class GpuModularArithmeticBenchmarks
{ {
private const int MaxBatchSize = 512; private const int MaxBatchSize = 512;
private readonly ulong[] _exponents = new ulong[MaxBatchSize]; private ulong[] _exponents = Array.Empty<ulong>();
private readonly ulong[] _moduli = new ulong[MaxBatchSize]; private ulong[] _moduli = Array.Empty<ulong>();
private readonly ulong[] _mersenneModuli = new ulong[MaxBatchSize]; private ulong[] _mersenneModuli = Array.Empty<ulong>();
private readonly int[] _mersenneBitWidths = new int[MaxBatchSize]; private int[] _mersenneBitWidths = Array.Empty<int>();
private readonly Random _random = new(7);
[Params(32, 256)] [Params(32, 256)]
public int BatchSize { get; set; } public int BatchSize { get; set; }
@@ -22,14 +20,69 @@ public class GpuModularArithmeticBenchmarks
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
for (int i = 0; i < MaxBatchSize; i++) ModularArithmeticSampleData data = ModularArithmeticSampleCache.Instance;
{ _exponents = data.Exponents;
_exponents[i] = NextExponent(); _moduli = data.Moduli;
_moduli[i] = NextOddModulus(); _mersenneModuli = data.MersenneModuli;
_mersenneBitWidths = data.MersenneBitWidths;
}
int bits = _random.Next(8, 62); private sealed class ModularArithmeticSampleData
_mersenneBitWidths[i] = bits; {
_mersenneModuli[i] = (1UL << bits) - 1UL; public ModularArithmeticSampleData(ulong[] exponents, ulong[] moduli, ulong[] mersenneModuli, int[] mersenneBitWidths)
{
Exponents = exponents;
Moduli = moduli;
MersenneModuli = mersenneModuli;
MersenneBitWidths = mersenneBitWidths;
}
public ulong[] Exponents { get; }
public ulong[] Moduli { get; }
public ulong[] MersenneModuli { get; }
public int[] MersenneBitWidths { get; }
}
private static class ModularArithmeticSampleCache
{
private static readonly Lazy<ModularArithmeticSampleData> Cache = new(Create);
public static ModularArithmeticSampleData Instance => Cache.Value;
private static ModularArithmeticSampleData Create()
{
Random random = new(7);
var exponents = new ulong[MaxBatchSize];
var moduli = new ulong[MaxBatchSize];
var mersenneModuli = new ulong[MaxBatchSize];
var mersenneBitWidths = new int[MaxBatchSize];
for (int i = 0; i < MaxBatchSize; i++)
{
exponents[i] = NextExponent(random);
moduli[i] = NextOddModulus(random);
int bits = random.Next(8, 62);
mersenneBitWidths[i] = bits;
mersenneModuli[i] = (1UL << bits) - 1UL;
}
return new ModularArithmeticSampleData(exponents, moduli, mersenneModuli, mersenneBitWidths);
}
private static ulong NextExponent(Random random)
{
return (ulong)random.Next(24, 63);
}
private static ulong NextOddModulus(Random random)
{
ulong value = (ulong)random.NextInt64(5L, long.MaxValue);
if ((value & 1UL) == 0UL)
{
value++;
}
return value;
} }
} }
@@ -69,22 +122,6 @@ public class GpuModularArithmeticBenchmarks
return checksum; return checksum;
} }
private ulong NextExponent()
{
return (ulong)_random.Next(24, 63);
}
private ulong NextOddModulus()
{
ulong value = (ulong)_random.NextInt64(5L, long.MaxValue);
if ((value & 1UL) == 0UL)
{
value++;
}
return value;
}
private static ulong Pow2ModBaseline(ulong exponent, ulong modulus) private static ulong Pow2ModBaseline(ulong exponent, ulong modulus)
{ {
if (modulus <= 1UL) if (modulus <= 1UL)
@@ -9,12 +9,10 @@ public class MontgomeryMultiplyBenchmarks
{ {
private const int MaxBatchSize = 1024; private const int MaxBatchSize = 1024;
private readonly ulong[] _operandsA = new ulong[MaxBatchSize]; private ulong[] _operandsA = Array.Empty<ulong>();
private readonly ulong[] _operandsB = new ulong[MaxBatchSize]; private ulong[] _operandsB = Array.Empty<ulong>();
private readonly ulong[] _moduli = new ulong[MaxBatchSize]; private ulong[] _moduli = Array.Empty<ulong>();
private readonly ulong[] _nPrimes = new ulong[MaxBatchSize]; private ulong[] _nPrimes = Array.Empty<ulong>();
private readonly Random _random = new(1979);
[Params(64, 256, 1024)] [Params(64, 256, 1024)]
public int BatchSize { get; set; } public int BatchSize { get; set; }
@@ -22,13 +20,64 @@ public class MontgomeryMultiplyBenchmarks
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
for (int i = 0; i < MaxBatchSize; i++) MontgomerySampleData data = MontgomerySampleDataCache.Instance;
_operandsA = data.OperandsA;
_operandsB = data.OperandsB;
_moduli = data.Moduli;
_nPrimes = data.NPrimes;
}
private sealed class MontgomerySampleData
{
public MontgomerySampleData(ulong[] operandsA, ulong[] operandsB, ulong[] moduli, ulong[] nPrimes)
{ {
ulong modulus = NextOddModulus(); OperandsA = operandsA;
_moduli[i] = modulus; OperandsB = operandsB;
_nPrimes[i] = ComputeMontgomeryNPrime(modulus); Moduli = moduli;
_operandsA[i] = (ulong)_random.NextInt64(0, (long)modulus); NPrimes = nPrimes;
_operandsB[i] = (ulong)_random.NextInt64(0, (long)modulus); }
public ulong[] OperandsA { get; }
public ulong[] OperandsB { get; }
public ulong[] Moduli { get; }
public ulong[] NPrimes { get; }
}
private static class MontgomerySampleDataCache
{
private static readonly Lazy<MontgomerySampleData> Cache = new(Create);
public static MontgomerySampleData Instance => Cache.Value;
private static MontgomerySampleData Create()
{
Random random = new(1979);
var operandsA = new ulong[MaxBatchSize];
var operandsB = new ulong[MaxBatchSize];
var moduli = new ulong[MaxBatchSize];
var nPrimes = new ulong[MaxBatchSize];
for (int i = 0; i < MaxBatchSize; i++)
{
ulong modulus = NextOddModulus(random);
moduli[i] = modulus;
nPrimes[i] = ComputeMontgomeryNPrime(modulus);
operandsA[i] = (ulong)random.NextInt64(0, (long)modulus);
operandsB[i] = (ulong)random.NextInt64(0, (long)modulus);
}
return new MontgomerySampleData(operandsA, operandsB, moduli, nPrimes);
}
private static ulong NextOddModulus(Random random)
{
ulong modulus = (ulong)random.NextInt64(3, long.MaxValue);
if ((modulus & 1UL) == 0UL)
{
modulus++;
}
return modulus;
} }
} }
@@ -68,17 +117,6 @@ public class MontgomeryMultiplyBenchmarks
return checksum; return checksum;
} }
private ulong NextOddModulus()
{
ulong modulus = (ulong)_random.NextInt64(3, long.MaxValue);
if ((modulus & 1UL) == 0UL)
{
modulus++;
}
return modulus;
}
private static ulong OriginalMontgomeryMultiply(ulong a, ulong b, ulong modulus, ulong nPrime) private static ulong OriginalMontgomeryMultiply(ulong a, ulong b, ulong modulus, ulong nPrime)
{ {
ulong tLow = unchecked(a * b); ulong tLow = unchecked(a * b);
@@ -26,20 +26,17 @@ public class Pow2MontgomeryModBenchmarks
private const ulong OneShiftedLeft63 = 1UL << 63; private const ulong OneShiftedLeft63 = 1UL << 63;
private const long OneShiftedLeft60 = 1L << 60; private const long OneShiftedLeft60 = 1L << 60;
private readonly MontgomeryDivisorData[] _smallDivisors = new MontgomeryDivisorData[SampleCount]; private MontgomeryDivisorData[] _smallDivisors = Array.Empty<MontgomeryDivisorData>();
private readonly MontgomeryDivisorData[] _largeDivisors = new MontgomeryDivisorData[SampleCount]; private MontgomeryDivisorData[] _largeDivisors = Array.Empty<MontgomeryDivisorData>();
private readonly MontgomeryDivisorData[] _veryLargeDivisors = new MontgomeryDivisorData[SampleCount]; private MontgomeryDivisorData[] _veryLargeDivisors = Array.Empty<MontgomeryDivisorData>();
private readonly UInt128[] _wideModuli = new UInt128[SampleCount]; private UInt128[] _wideModuli = Array.Empty<UInt128>();
private readonly ulong[] _smallExponents = new ulong[SampleCount]; private ulong[] _smallExponents = Array.Empty<ulong>();
private readonly ulong[] _largeExponents = new ulong[SampleCount]; private ulong[] _largeExponents = Array.Empty<ulong>();
private readonly ulong[] _veryLargeExponents = new ulong[SampleCount]; private ulong[] _veryLargeExponents = Array.Empty<ulong>();
private readonly ulong[] _smallCycles = new ulong[SampleCount]; private ulong[] _smallCycles = Array.Empty<ulong>();
private readonly ulong[] _largeCycles = new ulong[SampleCount]; private ulong[] _largeCycles = Array.Empty<ulong>();
private readonly ulong[] _veryLargeCycles = new ulong[SampleCount]; private ulong[] _veryLargeCycles = Array.Empty<ulong>();
private readonly UInt128[] _wideCycles = new UInt128[SampleCount]; private UInt128[] _wideCycles = Array.Empty<UInt128>();
private readonly Random _random = new(13);
private readonly Random _heuristicRandom = new(113);
private ulong? _previousPrimeOrder; private ulong? _previousPrimeOrder;
private UInt128? _previousWidePrimeOrder; private UInt128? _previousWidePrimeOrder;
@@ -56,34 +53,18 @@ public class Pow2MontgomeryModBenchmarks
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
for (int i = 0; i < SampleCount; i++) SampleCollections cache = SampleCollectionsProvider.Instance;
{ _smallDivisors = cache.SmallDivisors;
#if DEBUG _largeDivisors = cache.LargeDivisors;
Console.WriteLine($"Generating sample {i}"); _veryLargeDivisors = cache.VeryLargeDivisors;
#endif _wideModuli = cache.WideModuli;
ulong smallModulus = NextSmallOddModulus(); _smallExponents = cache.SmallExponents;
_smallDivisors[i] = CreateMontgomeryDivisorData(smallModulus); _largeExponents = cache.LargeExponents;
_smallExponents[i] = NextSmallExponent(); _veryLargeExponents = cache.VeryLargeExponents;
_smallCycles[i] = MersenneDivisorCycles.CalculateCycleLength(smallModulus, MontgomeryDivisorData.FromModulus(smallModulus)); _smallCycles = cache.SmallCycles;
_largeCycles = cache.LargeCycles;
(ulong largeModulus, ulong largeCycle) = NextLargeModulusAndCycle(); _veryLargeCycles = cache.VeryLargeCycles;
_largeDivisors[i] = CreateMontgomeryDivisorData(largeModulus); _wideCycles = cache.WideCycles;
_largeExponents[i] = NextLargeExponent();
_largeCycles[i] = largeCycle;
ulong veryLargeModulus = NextVeryLargeOddModulus();
_veryLargeDivisors[i] = CreateMontgomeryDivisorData(veryLargeModulus);
_veryLargeExponents[i] = NextVeryLargeExponent();
#if DEBUG
Console.WriteLine($"Calculating cycle length {veryLargeModulus}");
#endif
_veryLargeCycles[i] = CalculateCycleLengthWithHeuristics(veryLargeModulus);
Console.WriteLine($"Generating wide sample {i}");
UInt128 wideModulus = NextWideOddModulus();
_wideModuli[i] = wideModulus;
_wideCycles[i] = CalculateCycleLengthWithHeuristics(wideModulus);
}
} }
/// <summary> /// <summary>
@@ -230,7 +211,6 @@ public class Pow2MontgomeryModBenchmarks
for (int i = 0; i < SampleCount; i++) for (int i = 0; i < SampleCount; i++)
{ {
UInt128 cycle = CalculateCycleLengthWithHeuristics(_wideModuli[i]); UInt128 cycle = CalculateCycleLengthWithHeuristics(_wideModuli[i]);
_wideCycles[i] = cycle;
checksum ^= cycle; checksum ^= cycle;
} }
@@ -363,50 +343,199 @@ public class Pow2MontgomeryModBenchmarks
} }
} }
private ulong NextSmallExponent() => (ulong)_random.NextInt64(1L, 1_000_000L); private sealed class SampleCollections
private ulong NextLargeExponent() => (ulong)_random.NextInt64(OneShiftedLeft60, long.MaxValue);
private ulong NextVeryLargeExponent()
{ {
Span<byte> buffer = stackalloc byte[8]; public SampleCollections(
_heuristicRandom.NextBytes(buffer); MontgomeryDivisorData[] smallDivisors,
ulong value = checked(BinaryPrimitives.ReadUInt64LittleEndian(buffer) | OneShiftedLeft63); MontgomeryDivisorData[] largeDivisors,
return value; MontgomeryDivisorData[] veryLargeDivisors,
UInt128[] wideModuli,
ulong[] smallExponents,
ulong[] largeExponents,
ulong[] veryLargeExponents,
ulong[] smallCycles,
ulong[] largeCycles,
ulong[] veryLargeCycles,
UInt128[] wideCycles)
{
SmallDivisors = smallDivisors;
LargeDivisors = largeDivisors;
VeryLargeDivisors = veryLargeDivisors;
WideModuli = wideModuli;
SmallExponents = smallExponents;
LargeExponents = largeExponents;
VeryLargeExponents = veryLargeExponents;
SmallCycles = smallCycles;
LargeCycles = largeCycles;
VeryLargeCycles = veryLargeCycles;
WideCycles = wideCycles;
}
public MontgomeryDivisorData[] SmallDivisors { get; }
public MontgomeryDivisorData[] LargeDivisors { get; }
public MontgomeryDivisorData[] VeryLargeDivisors { get; }
public UInt128[] WideModuli { get; }
public ulong[] SmallExponents { get; }
public ulong[] LargeExponents { get; }
public ulong[] VeryLargeExponents { get; }
public ulong[] SmallCycles { get; }
public ulong[] LargeCycles { get; }
public ulong[] VeryLargeCycles { get; }
public UInt128[] WideCycles { get; }
} }
private ulong NextSmallOddModulus() private static class SampleCollectionsProvider
{ {
ulong value = (ulong)_random.NextInt64(3L, 1_000_000L); private static readonly Lazy<SampleCollections> Cache = new(Create);
return value | 1UL;
}
private (ulong modulus, ulong cycleLength) NextLargeModulusAndCycle() public static SampleCollections Instance => Cache.Value;
{
int bitLength = _random.Next(48, 64);
ulong modulus = (1UL << bitLength) - 1UL;
return (modulus, (ulong)bitLength);
}
private ulong NextVeryLargeOddModulus() private static SampleCollections Create()
{ {
Span<byte> buffer = stackalloc byte[8]; Random random = new(13);
_heuristicRandom.NextBytes(buffer); Random heuristicRandom = new(113);
ulong value = BinaryPrimitives.ReadUInt64LittleEndian(buffer) | OneShiftedLeft63 | 1UL;
return value;
}
private UInt128 NextWideOddModulus() var smallDivisors = new MontgomeryDivisorData[SampleCount];
{ var largeDivisors = new MontgomeryDivisorData[SampleCount];
Span<byte> buffer = stackalloc byte[16]; var veryLargeDivisors = new MontgomeryDivisorData[SampleCount];
_heuristicRandom.NextBytes(buffer); var wideModuli = new UInt128[SampleCount];
var smallExponents = new ulong[SampleCount];
var largeExponents = new ulong[SampleCount];
var veryLargeExponents = new ulong[SampleCount];
var smallCycles = new ulong[SampleCount];
var largeCycles = new ulong[SampleCount];
var veryLargeCycles = new ulong[SampleCount];
var wideCycles = new UInt128[SampleCount];
ulong low = BinaryPrimitives.ReadUInt64LittleEndian(buffer); ulong? previousPrimeOrder = null;
Span<byte> highSlice = buffer.Slice(8, 8); UInt128? previousWidePrimeOrder = null;
ulong high = BinaryPrimitives.ReadUInt64LittleEndian(highSlice) | (1UL << 63);
UInt128 value = ((UInt128)high << 64) | low; for (int i = 0; i < SampleCount; i++)
return value | UInt128.One; {
ulong smallModulus = NextSmallOddModulus(random);
smallDivisors[i] = CreateMontgomeryDivisorData(smallModulus);
smallExponents[i] = NextSmallExponent(random);
smallCycles[i] = MersenneDivisorCycles.CalculateCycleLength(smallModulus, MontgomeryDivisorData.FromModulus(smallModulus));
(ulong largeModulus, ulong largeCycle) = NextLargeModulusAndCycle(random);
largeDivisors[i] = CreateMontgomeryDivisorData(largeModulus);
largeExponents[i] = NextLargeExponent(random);
largeCycles[i] = largeCycle;
ulong veryLargeModulus = NextVeryLargeOddModulus(heuristicRandom);
veryLargeDivisors[i] = CreateMontgomeryDivisorData(veryLargeModulus);
veryLargeExponents[i] = NextVeryLargeExponent(heuristicRandom);
veryLargeCycles[i] = CalculateCycleLengthWithHeuristics(veryLargeModulus, ref previousPrimeOrder);
UInt128 wideModulus = NextWideOddModulus(heuristicRandom);
wideModuli[i] = wideModulus;
wideCycles[i] = CalculateCycleLengthWithHeuristics(wideModulus, ref previousWidePrimeOrder);
}
return new SampleCollections(
smallDivisors,
largeDivisors,
veryLargeDivisors,
wideModuli,
smallExponents,
largeExponents,
veryLargeExponents,
smallCycles,
largeCycles,
veryLargeCycles,
wideCycles);
}
private static ulong NextSmallExponent(Random random) => (ulong)random.NextInt64(1L, 1_000_000L);
private static ulong NextLargeExponent(Random random) => (ulong)random.NextInt64(OneShiftedLeft60, long.MaxValue);
private static ulong NextVeryLargeExponent(Random random)
{
Span<byte> buffer = stackalloc byte[8];
random.NextBytes(buffer);
ulong value = checked(BinaryPrimitives.ReadUInt64LittleEndian(buffer) | OneShiftedLeft63);
return value;
}
private static ulong NextSmallOddModulus(Random random)
{
ulong value = (ulong)random.NextInt64(3L, 1_000_000L);
return value | 1UL;
}
private static (ulong Modulus, ulong CycleLength) NextLargeModulusAndCycle(Random random)
{
int bitLength = random.Next(48, 64);
ulong modulus = (1UL << bitLength) - 1UL;
return (modulus, (ulong)bitLength);
}
private static ulong NextVeryLargeOddModulus(Random random)
{
Span<byte> buffer = stackalloc byte[8];
random.NextBytes(buffer);
ulong value = BinaryPrimitives.ReadUInt64LittleEndian(buffer) | OneShiftedLeft63 | 1UL;
return value;
}
private static UInt128 NextWideOddModulus(Random random)
{
Span<byte> buffer = stackalloc byte[16];
random.NextBytes(buffer);
ulong low = BinaryPrimitives.ReadUInt64LittleEndian(buffer);
ulong high = BinaryPrimitives.ReadUInt64LittleEndian(buffer[8..]) | (1UL << 63);
UInt128 value = ((UInt128)high << 64) | low;
return value | UInt128.One;
}
private static ulong CalculateCycleLengthWithHeuristics(ulong modulus, ref ulong? previousPrimeOrder)
{
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(modulus);
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
{
return MersenneDivisorCycles.CalculateCycleLength(modulus, divisorData);
}
ulong order = PrimeOrderCalculator.Calculate(
modulus,
previousPrimeOrder,
divisorData,
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
if (order != 0UL)
{
previousPrimeOrder = order;
return order;
}
previousPrimeOrder = null;
return MersenneDivisorCycles.CalculateCycleLength(modulus, divisorData);
}
private static UInt128 CalculateCycleLengthWithHeuristics(UInt128 modulus, ref UInt128? previousWidePrimeOrder)
{
if (modulus <= UInt128.One || (modulus & UInt128.One) == UInt128.Zero)
{
return MersenneDivisorCycles.GetCycle(modulus);
}
UInt128 order = PrimeOrderCalculator.Calculate(
modulus,
previousWidePrimeOrder,
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
if (order != UInt128.Zero)
{
previousWidePrimeOrder = order;
return order;
}
previousWidePrimeOrder = null;
return MersenneDivisorCycles.GetCycle(modulus);
}
} }
private static MontgomeryDivisorData CreateMontgomeryDivisorData(ulong modulus) private static MontgomeryDivisorData CreateMontgomeryDivisorData(ulong modulus)
@@ -13,21 +13,86 @@ public class Pow2MontgomeryModUInt128Benchmarks
private const int SampleCount = 256; private const int SampleCount = 256;
private const ulong Pow2WindowFallbackThreshold = 32UL; private const ulong Pow2WindowFallbackThreshold = 32UL;
private readonly UInt128[] _moduli = new UInt128[SampleCount]; private UInt128[] _moduli = Array.Empty<UInt128>();
private readonly UInt128[] _exponents = new UInt128[SampleCount]; private UInt128[] _exponents = Array.Empty<UInt128>();
private readonly Random _random = new(19);
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
for (int i = 0; i < SampleCount; i++) WideSampleData cache = WideSampleDataCache.Instance;
{ _moduli = cache.Moduli;
UInt128 modulus = NextWideOddModulus(); _exponents = cache.Exponents;
_moduli[i] = modulus; }
UInt128 exponent = NextWideExponent(); private sealed class WideSampleData
_exponents[i] = exponent; {
public WideSampleData(UInt128[] moduli, UInt128[] exponents)
{
Moduli = moduli;
Exponents = exponents;
}
public UInt128[] Moduli { get; }
public UInt128[] Exponents { get; }
}
private static class WideSampleDataCache
{
private static readonly Lazy<WideSampleData> Cache = new(Create);
public static WideSampleData Instance => Cache.Value;
private static WideSampleData Create()
{
Random random = new(19);
var moduli = new UInt128[SampleCount];
var exponents = new UInt128[SampleCount];
for (int i = 0; i < SampleCount; i++)
{
moduli[i] = NextWideOddModulus(random);
exponents[i] = NextWideExponent(random);
}
return new WideSampleData(moduli, exponents);
}
private static UInt128 NextWideOddModulus(Random random)
{
Span<byte> buffer = stackalloc byte[16];
random.NextBytes(buffer);
ulong low = BinaryPrimitives.ReadUInt64LittleEndian(buffer);
ulong high = BinaryPrimitives.ReadUInt64LittleEndian(buffer[8..]);
if (high == 0UL)
{
high = 1UL;
}
UInt128 modulus = ((UInt128)high << 64) | low;
modulus |= UInt128.One;
if (modulus <= UInt128.One)
{
modulus += 2UL;
}
return modulus;
}
private static UInt128 NextWideExponent(Random random)
{
Span<byte> buffer = stackalloc byte[16];
random.NextBytes(buffer);
ulong low = BinaryPrimitives.ReadUInt64LittleEndian(buffer);
ulong high = BinaryPrimitives.ReadUInt64LittleEndian(buffer[8..]);
if (high == 0UL)
{
high = 1UL;
}
return ((UInt128)high << 64) | low;
} }
} }
@@ -63,44 +128,6 @@ public class Pow2MontgomeryModUInt128Benchmarks
return checksum; return checksum;
} }
private UInt128 NextWideOddModulus()
{
Span<byte> buffer = stackalloc byte[16];
_random.NextBytes(buffer);
ulong low = BinaryPrimitives.ReadUInt64LittleEndian(buffer);
ulong high = BinaryPrimitives.ReadUInt64LittleEndian(buffer[8..]);
if (high == 0UL)
{
high = 1UL;
}
UInt128 modulus = ((UInt128)high << 64) | low;
modulus |= UInt128.One;
if (modulus <= UInt128.One)
{
modulus += 2UL;
}
return modulus;
}
private UInt128 NextWideExponent()
{
Span<byte> buffer = stackalloc byte[16];
_random.NextBytes(buffer);
ulong low = BinaryPrimitives.ReadUInt64LittleEndian(buffer);
ulong high = BinaryPrimitives.ReadUInt64LittleEndian(buffer[8..]);
if (high == 0UL)
{
high = 1UL;
}
return ((UInt128)high << 64) | low;
}
private static UInt128 Pow2MontgomeryModWindowedGpuShim(UInt128 exponent, UInt128 modulus) private static UInt128 Pow2MontgomeryModWindowedGpuShim(UInt128 exponent, UInt128 modulus)
{ {
if (modulus == UInt128.One) if (modulus == UInt128.One)