96 lines
3.2 KiB
C#
96 lines
3.2 KiB
C#
using System.Diagnostics;
|
|
using PerfectNumbers.Core;
|
|
|
|
namespace HeuristicMultiplyShiftTiming;
|
|
|
|
internal static class Program
|
|
{
|
|
private static readonly MultiplyShiftInput[] Inputs =
|
|
[
|
|
new(0x3FFF_FFFF_FFFF_FFFFUL, 3UL, 3, "NearOverflowShift"),
|
|
new(0x2000_0000_0000_0000UL, 7UL, 4, "HalfRange"),
|
|
new(0x1234_5678_9ABC_DEF0UL, 5UL, 5, "MixedBits"),
|
|
new(0x0000_0000_1000_0000UL, 11UL, 3, "Sparse"),
|
|
];
|
|
|
|
private const int Iterations = 200_000_000;
|
|
|
|
public static void Main()
|
|
{
|
|
Console.WriteLine($"Timing each method for {Iterations:N0} iterations per input...\n");
|
|
|
|
foreach (MultiplyShiftInput input in Inputs)
|
|
{
|
|
Console.WriteLine($"Input: {input.Name} (value=0x{input.Value:X16}, multiplier={input.Multiplier}, shift={input.Shift})");
|
|
|
|
Measurement safe = MeasureSafe(input);
|
|
Console.WriteLine($" MultiplyShiftRight (UInt128 product): {safe.AverageNanoseconds:F3} ns | checksum=0x{safe.Checksum:X16}");
|
|
|
|
Measurement shiftFirst = MeasureShiftFirst(input);
|
|
Console.WriteLine($" MultiplyShiftRightShiftFirst (shift-first): {shiftFirst.AverageNanoseconds:F3} ns | checksum=0x{shiftFirst.Checksum:X16}");
|
|
|
|
Measurement naive = MeasureNaive(input);
|
|
Console.WriteLine($" Naive ((value * multiplier) >> shift): {naive.AverageNanoseconds:F3} ns | checksum=0x{naive.Checksum:X16}\n");
|
|
}
|
|
}
|
|
|
|
private static Measurement MeasureSafe(MultiplyShiftInput input)
|
|
{
|
|
ulong checksum = 0UL;
|
|
Stopwatch sw = Stopwatch.StartNew();
|
|
ulong value = input.Value;
|
|
|
|
for (int i = 0; i < Iterations; i++)
|
|
{
|
|
checksum += ULongExtensions.MultiplyShiftRight(value, input.Multiplier, input.Shift);
|
|
value += 1UL;
|
|
}
|
|
|
|
sw.Stop();
|
|
return CreateMeasurement(sw, checksum);
|
|
}
|
|
|
|
private static Measurement MeasureShiftFirst(MultiplyShiftInput input)
|
|
{
|
|
ulong checksum = 0UL;
|
|
Stopwatch sw = Stopwatch.StartNew();
|
|
ulong value = input.Value;
|
|
|
|
for (int i = 0; i < Iterations; i++)
|
|
{
|
|
checksum += ULongExtensions.MultiplyShiftRightShiftFirst(value, input.Multiplier, input.Shift);
|
|
value += 1UL;
|
|
}
|
|
|
|
sw.Stop();
|
|
return CreateMeasurement(sw, checksum);
|
|
}
|
|
|
|
private static Measurement MeasureNaive(MultiplyShiftInput input)
|
|
{
|
|
ulong checksum = 0UL;
|
|
Stopwatch sw = Stopwatch.StartNew();
|
|
ulong value = input.Value;
|
|
|
|
for (int i = 0; i < Iterations; i++)
|
|
{
|
|
checksum += (value * input.Multiplier) >> input.Shift;
|
|
value += 1UL;
|
|
}
|
|
|
|
sw.Stop();
|
|
return CreateMeasurement(sw, checksum);
|
|
}
|
|
|
|
private static Measurement CreateMeasurement(Stopwatch sw, ulong checksum)
|
|
{
|
|
double elapsedNanoseconds = sw.ElapsedTicks * (1_000_000_000.0 / Stopwatch.Frequency);
|
|
double averageNanoseconds = elapsedNanoseconds / Iterations;
|
|
return new Measurement(averageNanoseconds, checksum);
|
|
}
|
|
|
|
private readonly record struct MultiplyShiftInput(ulong Value, ulong Multiplier, int Shift, string Name);
|
|
|
|
private readonly record struct Measurement(double AverageNanoseconds, ulong Checksum);
|
|
}
|