Optimize GPU divisor heuristics pipeline
This commit is contained in:
1 parent
6af0ae900e
commit
ab1a069e57
8 files changed
+494
-362
No files matched your search
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using PerfectNumbers.Core;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks;
|
||||
|
||||
internal sealed class Pow2MontgomeryModWindowedBenchmarkInputs
|
||||
{
|
||||
public Pow2MontgomeryModWindowedBenchmarkInputs(Pow2MontgomeryModWindowedBenchmarkCase[] cases)
|
||||
{
|
||||
Cases = cases;
|
||||
}
|
||||
|
||||
public Pow2MontgomeryModWindowedBenchmarkCase[] Cases { get; }
|
||||
}
|
||||
|
||||
internal readonly struct Pow2MontgomeryModWindowedBenchmarkCase
|
||||
{
|
||||
public Pow2MontgomeryModWindowedBenchmarkCase(
|
||||
ulong exponent,
|
||||
ulong modulus,
|
||||
in MontgomeryDivisorData divisor,
|
||||
ulong cycleLength,
|
||||
ulong reducedExponent)
|
||||
{
|
||||
Exponent = exponent;
|
||||
Modulus = modulus;
|
||||
Divisor = divisor;
|
||||
CycleLength = cycleLength;
|
||||
ReducedExponent = reducedExponent;
|
||||
}
|
||||
|
||||
public ulong Exponent { get; }
|
||||
|
||||
public ulong Modulus { get; }
|
||||
|
||||
public MontgomeryDivisorData Divisor { get; }
|
||||
|
||||
public ulong CycleLength { get; }
|
||||
|
||||
public ulong ReducedExponent { get; }
|
||||
}
|
||||
|
||||
internal static class Pow2MontgomeryModWindowedBenchmarkInputsProvider
|
||||
{
|
||||
private static readonly Lazy<Pow2MontgomeryModWindowedBenchmarkInputs> Cache = new(Create);
|
||||
|
||||
public static Pow2MontgomeryModWindowedBenchmarkInputs Instance => Cache.Value;
|
||||
|
||||
private static Pow2MontgomeryModWindowedBenchmarkInputs Create()
|
||||
{
|
||||
Pow2MontgomeryModWindowedBenchmarkSeed[] seeds =
|
||||
{
|
||||
new Pow2MontgomeryModWindowedBenchmarkSeed(138_000_001UL, 1UL),
|
||||
new Pow2MontgomeryModWindowedBenchmarkSeed(150_000_013UL, 2UL),
|
||||
new Pow2MontgomeryModWindowedBenchmarkSeed(175_000_019UL, 3UL),
|
||||
new Pow2MontgomeryModWindowedBenchmarkSeed(190_000_151UL, 4UL),
|
||||
new Pow2MontgomeryModWindowedBenchmarkSeed(210_000_089UL, 5UL),
|
||||
new Pow2MontgomeryModWindowedBenchmarkSeed(230_000_039UL, 6UL),
|
||||
new Pow2MontgomeryModWindowedBenchmarkSeed(260_000_111UL, 7UL),
|
||||
new Pow2MontgomeryModWindowedBenchmarkSeed(300_000_007UL, 8UL)
|
||||
};
|
||||
|
||||
var cases = new Pow2MontgomeryModWindowedBenchmarkCase[seeds.Length];
|
||||
for (int i = 0; i < seeds.Length; i++)
|
||||
{
|
||||
Pow2MontgomeryModWindowedBenchmarkSeed seed = seeds[i];
|
||||
ulong modulus = 2UL * seed.Multiplier * seed.Exponent + 1UL;
|
||||
MontgomeryDivisorData divisor = MontgomeryDivisorData.FromModulus(modulus);
|
||||
ulong cycleLength = MersenneDivisorCycles.CalculateCycleLength(modulus, divisor);
|
||||
ulong reducedExponent = seed.Exponent % cycleLength;
|
||||
cases[i] = new Pow2MontgomeryModWindowedBenchmarkCase(
|
||||
seed.Exponent,
|
||||
modulus,
|
||||
divisor,
|
||||
cycleLength,
|
||||
reducedExponent);
|
||||
}
|
||||
|
||||
return new Pow2MontgomeryModWindowedBenchmarkInputs(cases);
|
||||
}
|
||||
|
||||
private readonly struct Pow2MontgomeryModWindowedBenchmarkSeed
|
||||
{
|
||||
public Pow2MontgomeryModWindowedBenchmarkSeed(ulong exponent, ulong multiplier)
|
||||
{
|
||||
Exponent = exponent;
|
||||
Multiplier = multiplier;
|
||||
}
|
||||
|
||||
public ulong Exponent { get; }
|
||||
|
||||
public ulong Multiplier { get; }
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using PerfectNumbers.Core;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks;
|
||||
|
||||
/// <summary>
|
||||
/// Benchmarks CPU Montgomery exponentiation helpers for exponents at or above 138 million.
|
||||
/// Reuses the shared benchmark input cache so every method processes identical samples.
|
||||
/// </summary>
|
||||
[MemoryDiagnoser]
|
||||
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
|
||||
public class Pow2MontgomeryModWindowedKeepMontgomeryCpuBenchmarks
|
||||
{
|
||||
private Pow2MontgomeryModWindowedBenchmarkInputs _inputs = null!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_inputs = Pow2MontgomeryModWindowedBenchmarkInputsProvider.Instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Baseline CPU windowed Montgomery exponentiation that converts the residue back to standard form.
|
||||
/// </summary>
|
||||
[Benchmark(Baseline = true)]
|
||||
public ulong WindowedMontgomeryConvert()
|
||||
{
|
||||
ulong checksum = 0UL;
|
||||
Pow2MontgomeryModWindowedBenchmarkCase[] cases = _inputs.Cases;
|
||||
|
||||
for (int i = 0; i < cases.Length; i++)
|
||||
{
|
||||
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
|
||||
checksum ^= current.Exponent.Pow2MontgomeryModWindowedCpu(current.Divisor, keepMontgomery: false);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CPU windowed Montgomery exponentiation that keeps the residue in the Montgomery domain.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong WindowedMontgomeryKeep()
|
||||
{
|
||||
ulong checksum = 0UL;
|
||||
Pow2MontgomeryModWindowedBenchmarkCase[] cases = _inputs.Cases;
|
||||
|
||||
for (int i = 0; i < cases.Length; i++)
|
||||
{
|
||||
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
|
||||
checksum ^= current.Exponent.Pow2MontgomeryModWindowedCpu(current.Divisor, keepMontgomery: true);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the known cycle length to fold the exponent before running the windowed Montgomery ladder.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong WindowedMontgomeryWithCycle()
|
||||
{
|
||||
ulong checksum = 0UL;
|
||||
Pow2MontgomeryModWindowedBenchmarkCase[] cases = _inputs.Cases;
|
||||
|
||||
for (int i = 0; i < cases.Length; i++)
|
||||
{
|
||||
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
|
||||
checksum ^= current.Exponent.Pow2MontgomeryModWithCycleCpu(current.CycleLength, current.Divisor);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the windowed Montgomery ladder on the reduced exponent that the divisor cycle exposes.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong WindowedMontgomeryFromCycleRemainder()
|
||||
{
|
||||
ulong checksum = 0UL;
|
||||
Pow2MontgomeryModWindowedBenchmarkCase[] cases = _inputs.Cases;
|
||||
|
||||
for (int i = 0; i < cases.Length; i++)
|
||||
{
|
||||
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
|
||||
checksum ^= current.ReducedExponent.Pow2MontgomeryModFromCycleRemainderCpu(current.Divisor);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using PerfectNumbers.Core;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks;
|
||||
|
||||
/// <summary>
|
||||
/// Benchmarks GPU Montgomery exponentiation helpers for exponents at or above 138 million.
|
||||
/// The sample set is generated once and reused across all benchmark methods so every variant
|
||||
/// processes the same data without incurring repeated initialization costs.
|
||||
/// </summary>
|
||||
[MemoryDiagnoser]
|
||||
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
|
||||
public class Pow2MontgomeryModWindowedKeepMontgomeryGpuBenchmarks
|
||||
{
|
||||
private Pow2MontgomeryModWindowedBenchmarkInputs _inputs = null!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_inputs = Pow2MontgomeryModWindowedBenchmarkInputsProvider.Instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Baseline GPU windowed Montgomery exponentiation that converts back to the standard residue.
|
||||
/// </summary>
|
||||
[Benchmark(Baseline = true)]
|
||||
public ulong WindowedMontgomeryConvert()
|
||||
{
|
||||
ulong checksum = 0UL;
|
||||
Pow2MontgomeryModWindowedBenchmarkCase[] cases = _inputs.Cases;
|
||||
|
||||
for (int i = 0; i < cases.Length; i++)
|
||||
{
|
||||
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
|
||||
checksum ^= current.Exponent.Pow2MontgomeryModWindowedGpu(current.Divisor, keepMontgomery: false);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GPU windowed Montgomery exponentiation that keeps the result in the Montgomery domain.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong WindowedMontgomeryKeep()
|
||||
{
|
||||
ulong checksum = 0UL;
|
||||
Pow2MontgomeryModWindowedBenchmarkCase[] cases = _inputs.Cases;
|
||||
|
||||
for (int i = 0; i < cases.Length; i++)
|
||||
{
|
||||
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
|
||||
checksum ^= current.Exponent.Pow2MontgomeryModWindowedGpu(current.Divisor, keepMontgomery: true);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GPU windowed modular exponentiation without Montgomery reduction for comparison.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong WindowedStandardMod()
|
||||
{
|
||||
ulong checksum = 0UL;
|
||||
Pow2MontgomeryModWindowedBenchmarkCase[] cases = _inputs.Cases;
|
||||
|
||||
for (int i = 0; i < cases.Length; i++)
|
||||
{
|
||||
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
|
||||
checksum ^= current.Exponent.Pow2ModWindowedGpu(current.Modulus);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user