Merge pull request #193 from mlemanczyk/codex/optimize-benchmarks-for-data-collection
Cache benchmark data to avoid regenerating per iteration
This commit is contained in:
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)
|
||||||
|
|||||||
Reference in new issue
Block a user