Benchmark results and TODOs added / updated
This commit is contained in:
1 parent
507b9c91f4
commit
f9e548279c
31 files changed
+347
-130
No files matched your search
@@ -8,6 +8,10 @@ public class Mod10UInt128Benchmarks
|
|||||||
[Params(3UL, 8191UL, 131071UL, 2147483647UL)]
|
[Params(3UL, 8191UL, 131071UL, 2147483647UL)]
|
||||||
public UInt128 Value { get; set; }
|
public UInt128 Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Direct `% 10` using the <see cref="UInt128"/> operator; lands between 3.25 ns and 3.38 ns (3.378 ns at value 3, 3.269 ns at
|
||||||
|
/// 8191, 3.248 ns at 131071, 3.281 ns at 2147483647) and serves as the baseline.
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public UInt128 StandardModulo()
|
public UInt128 StandardModulo()
|
||||||
{
|
{
|
||||||
@@ -15,7 +19,8 @@ public class Mod10UInt128Benchmarks
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// Splits the value into high/low halves and folds with the 2^64 ≡ 6 (mod 10) rule; fastest option at 1.78-1.82 ns (1.804 ns at
|
||||||
|
/// value 3, 1.798 ns at 8191, 1.817 ns at 131071, 1.779 ns at 2147483647), beating the baseline by ~1.9x.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong ModWithHighLowModulo()
|
public ulong ModWithHighLowModulo()
|
||||||
@@ -25,11 +30,14 @@ public class Mod10UInt128Benchmarks
|
|||||||
|
|
||||||
private static ulong ModWithHighLowModuloHelper(UInt128 value128)
|
private static ulong ModWithHighLowModuloHelper(UInt128 value128)
|
||||||
{
|
{
|
||||||
// Split and fold under mod 10: 2^64 ≡ 6 (mod 10)
|
|
||||||
ulong value = (ulong)value128;
|
ulong value = (ulong)value128;
|
||||||
return ((value % 10UL) + ((value >> 64) % 10UL) * 6UL) % 10UL;
|
return ((value % 10UL) + ((value >> 64) % 10UL) * 6UL) % 10UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loop-based folding that repeatedly applies the 2^64 ≡ 6 rule; measures 2.20-2.27 ns (2.204 ns at value 3, 2.234 ns at 8191,
|
||||||
|
/// 2.268 ns at 131071, 2.216 ns at 2147483647), situating it between the high/low helper and the raw modulo.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong ModWithLoopModulo()
|
public ulong ModWithLoopModulo()
|
||||||
{
|
{
|
||||||
@@ -40,14 +48,15 @@ public class Mod10UInt128Benchmarks
|
|||||||
{
|
{
|
||||||
UInt128 zero = UInt128.Zero;
|
UInt128 zero = UInt128.Zero;
|
||||||
if (value == zero)
|
if (value == zero)
|
||||||
|
{
|
||||||
return 0UL;
|
return 0UL;
|
||||||
|
}
|
||||||
|
|
||||||
ulong result = (ulong)value;
|
ulong result = (ulong)value;
|
||||||
value >>= 64;
|
value >>= 64;
|
||||||
|
|
||||||
while (value != zero)
|
while (value != zero)
|
||||||
{
|
{
|
||||||
// 2^64 ≡ 6 (mod 10)
|
|
||||||
result = (result + (ulong)value * 6UL) % 10UL;
|
result = (result + (ulong)value * 6UL) % 10UL;
|
||||||
value >>= 64;
|
value >>= 64;
|
||||||
}
|
}
|
||||||
@@ -55,4 +64,3 @@ public class Mod10UInt128Benchmarks
|
|||||||
return result % 10UL;
|
return result % 10UL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,7 +16,8 @@ public class Mod10UIntBenchmarks
|
|||||||
public uint Value { get; set; }
|
public uint Value { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// Direct `% 10` on <see cref="uint"/>; BenchmarkDotNet flagged near-zero timings, but the measured means were 0.0407 ns at
|
||||||
|
/// value 10, 0.3224 ns at 2047, 0.2382 ns at 65535, and 0.0555 ns at 2147483647.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public uint ModuloOperator()
|
public uint ModuloOperator()
|
||||||
@@ -24,10 +25,14 @@ public class Mod10UIntBenchmarks
|
|||||||
return Value % 10U;
|
return Value % 10U;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optimized extension method that folds with a precomputed mask; still costs 0.77 ns on the tiny value 10 but drops to
|
||||||
|
/// 0.0448 ns at 2047, 0.0405 ns at 65535, and 0.0372 ns at 2147483647 (roughly 6-9x faster where the baseline exceeded the
|
||||||
|
/// empty-loop noise floor).
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public uint ExtensionMethod()
|
public uint ExtensionMethod()
|
||||||
{
|
{
|
||||||
return Mod10(Value);
|
return Mod10(Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9,6 +9,10 @@ public class Mod10ULongBenchmarks
|
|||||||
[Params(3UL, 8191UL, 131071UL, 2147483647UL)]
|
[Params(3UL, 8191UL, 131071UL, 2147483647UL)]
|
||||||
public ulong Value { get; set; }
|
public ulong Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scalar `% 10` baseline; consistently measured around 0.26 ns (0.2591 ns at value 3, 0.2597 ns at 8191, 0.2605 ns at 131071,
|
||||||
|
/// 0.2598 ns at 2147483647).
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public ulong LegacyModulo()
|
public ulong LegacyModulo()
|
||||||
{
|
{
|
||||||
@@ -16,7 +20,8 @@ public class Mod10ULongBenchmarks
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// Extension helper that applies the multiply-high mask; 0.73-0.75 ns across the suite (0.7503 ns at value 3, 0.7320 ns at
|
||||||
|
/// 8191, 0.7346 ns at 131071, 0.7411 ns at 2147483647), roughly 2.8-2.9x slower than the raw modulo on `ulong`.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong ModMethodModulo()
|
public ulong ModMethodModulo()
|
||||||
@@ -24,4 +29,3 @@ public class Mod10ULongBenchmarks
|
|||||||
return Value.Mod10();
|
return Value.Mod10();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10,7 +10,8 @@ public class Mod10_8_5_3Benchmarks
|
|||||||
public ulong Value { get; set; }
|
public ulong Value { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// Baseline scalar modulo path; held steady between 1.13 ns and 1.14 ns across every value (1.138 ns at 3, 1.143 ns at 8191,
|
||||||
|
/// 1.134 ns at 131071, 1.141 ns at 2147483647), making it the fastest option overall.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModuloOperator()
|
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModuloOperator()
|
||||||
@@ -19,10 +20,17 @@ public class Mod10_8_5_3Benchmarks
|
|||||||
return (value % 10UL, value % 8UL, value % 3UL, value % 5UL);
|
return (value % 10UL, value % 8UL, value % 3UL, value % 5UL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Legacy folding helper; useful for cross-checks but stays at 2.83-3.07 ns (2.834 ns at 3, 3.038 ns at 8191, 3.067 ns at
|
||||||
|
/// 131071, 3.039 ns at 2147483647), roughly 2.5-2.7x slower than the baseline.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) LegacyModulo()
|
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) LegacyModulo()
|
||||||
{
|
{
|
||||||
ulong mod10, mod8, mod3, mod5;
|
ulong mod10;
|
||||||
|
ulong mod8;
|
||||||
|
ulong mod3;
|
||||||
|
ulong mod5;
|
||||||
UInt128 zero = UInt128.Zero;
|
UInt128 zero = UInt128.Zero;
|
||||||
if (Value == zero)
|
if (Value == zero)
|
||||||
{
|
{
|
||||||
@@ -36,16 +44,15 @@ public class Mod10_8_5_3Benchmarks
|
|||||||
UInt128 high = Value >> 64;
|
UInt128 high = Value >> 64;
|
||||||
ulong result = Value;
|
ulong result = Value;
|
||||||
mod8 = result & 7UL;
|
mod8 = result & 7UL;
|
||||||
// 2^64 ≡ 1 (mod 3)
|
|
||||||
ulong modRem = (result % 3UL) + ((ulong)high % 3UL);
|
ulong modRem = (result % 3UL) + ((ulong)high % 3UL);
|
||||||
mod3 = modRem >= 3UL ? modRem - 3UL : modRem;
|
mod3 = modRem >= 3UL ? modRem - 3UL : modRem;
|
||||||
// 2^64 ≡ 1 (mod 5)
|
|
||||||
modRem = (result % 5UL) + ((ulong)high % 5UL);
|
modRem = (result % 5UL) + ((ulong)high % 5UL);
|
||||||
mod5 = modRem >= 5UL ? modRem - 5UL : modRem;
|
mod5 = modRem >= 5UL ? modRem - 5UL : modRem;
|
||||||
|
|
||||||
while (high != zero)
|
while (high != zero)
|
||||||
{
|
{
|
||||||
// 2^64 ≡ 6 (mod 10)
|
|
||||||
result = (result + (ulong)high * 6UL) % 10UL;
|
result = (result + (ulong)high * 6UL) % 10UL;
|
||||||
high >>= 64;
|
high >>= 64;
|
||||||
}
|
}
|
||||||
@@ -54,6 +61,10 @@ public class Mod10_8_5_3Benchmarks
|
|||||||
return (mod10, mod8, mod3, mod5);
|
return (mod10, mod8, mod3, mod5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Method-based optimized helper; clocks in at 1.63 ns for value 3, 1.64 ns at 8191, 1.875 ns at 131071, and 2.164 ns at
|
||||||
|
/// 2147483647 (1.43-1.90x the baseline) so it trails the raw `%` but remains 1.6x quicker than the legacy fold.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModMethodModulo()
|
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModMethodModulo()
|
||||||
{
|
{
|
||||||
@@ -61,4 +72,3 @@ public class Mod10_8_5_3Benchmarks
|
|||||||
return (mod10, mod8, mod3, mod5);
|
return (mod10, mod8, mod3, mod5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,7 +18,6 @@ public class Mod10_8_5_3PropertiesBenchmarks
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static ulong Mod3(UInt128 value)
|
private static ulong Mod3(UInt128 value)
|
||||||
{
|
{
|
||||||
// 2^64 ≡ 1 (mod 3)
|
|
||||||
ulong high = (ulong)(value >> 64);
|
ulong high = (ulong)(value >> 64);
|
||||||
ulong low = (ulong)value;
|
ulong low = (ulong)value;
|
||||||
ulong rem = (low % 3UL) + (high % 3UL);
|
ulong rem = (low % 3UL) + (high % 3UL);
|
||||||
@@ -28,18 +27,34 @@ public class Mod10_8_5_3PropertiesBenchmarks
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static ulong Mod5(UInt128 value)
|
private static ulong Mod5(UInt128 value)
|
||||||
{
|
{
|
||||||
// 2^64 ≡ 1 (mod 5)
|
|
||||||
ulong high = (ulong)(value >> 64);
|
ulong high = (ulong)(value >> 64);
|
||||||
ulong low = (ulong)value;
|
ulong low = (ulong)value;
|
||||||
ulong rem = (low % 5UL) + (high % 5UL);
|
ulong rem = (low % 5UL) + (high % 5UL);
|
||||||
return rem >= 5UL ? rem - 5UL : rem;
|
return rem >= 5UL ? rem - 5UL : rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Params(3UL, 6UL, 128UL, 256UL, 2_047UL, 8_191UL, 65_535UL, 131_071UL, 2_147_483_647UL, ulong.MaxValue - 1_024UL, ulong.MaxValue - 511UL, ulong.MaxValue - 255UL, ulong.MaxValue - 127UL, ulong.MaxValue - 63UL, ulong.MaxValue - 31UL, ulong.MaxValue - 17UL)]
|
[Params(
|
||||||
|
3UL,
|
||||||
|
6UL,
|
||||||
|
128UL,
|
||||||
|
256UL,
|
||||||
|
2047UL,
|
||||||
|
8191UL,
|
||||||
|
65535UL,
|
||||||
|
131071UL,
|
||||||
|
2147483647UL,
|
||||||
|
ulong.MaxValue - 1024UL,
|
||||||
|
ulong.MaxValue - 511UL,
|
||||||
|
ulong.MaxValue - 255UL,
|
||||||
|
ulong.MaxValue - 127UL,
|
||||||
|
ulong.MaxValue - 63UL,
|
||||||
|
ulong.MaxValue - 31UL,
|
||||||
|
ulong.MaxValue - 17UL)]
|
||||||
public ulong Value { get; set; }
|
public ulong Value { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// Legacy property-based helper; stayed within 2.35-2.46 ns on the small inputs and 2.36-2.46 ns across the near-ulong.Max
|
||||||
|
/// cases (e.g. 2.413 ns at value 3, 2.378 ns at 6, 2.458 ns at 2147483647, 2.371 ns near ulong.Max), keeping it in front.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public (ulong Mod10, ulong Mod8, ulong Mod5, ulong Mod3) LegacyModulo()
|
public (ulong Mod10, ulong Mod8, ulong Mod5, ulong Mod3) LegacyModulo()
|
||||||
@@ -52,6 +67,11 @@ public class Mod10_8_5_3PropertiesBenchmarks
|
|||||||
return (Mod10R, Mod8R, Mod5R, Mod3R);
|
return (Mod10R, Mod8R, Mod5R, Mod3R);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Method variant that routes through <see cref="UInt128Extensions.Mod10_8_5_3"/>; ranged from 2.79 ns at value 3 to 4.34 ns on
|
||||||
|
/// the largest operands (3.135 ns at 6, 3.033 ns at 2047, 3.441 ns at 2147483647, 4.289 ns near ulong.Max), roughly 1.16-1.83x
|
||||||
|
/// slower than the legacy path.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModMethodModulo()
|
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModMethodModulo()
|
||||||
{
|
{
|
||||||
@@ -59,4 +79,3 @@ public class Mod10_8_5_3PropertiesBenchmarks
|
|||||||
return (Mod10R, Mod8R, Mod3R, Mod5R);
|
return (Mod10R, Mod8R, Mod3R, Mod5R);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9,6 +9,10 @@ public class Mod10_8_5_3StepsBenchmarks
|
|||||||
[Params(3UL, 8191UL, 131071UL, 2147483647UL)]
|
[Params(3UL, 8191UL, 131071UL, 2147483647UL)]
|
||||||
public ulong Value { get; set; }
|
public ulong Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Legacy step-per-bit helper; required 11.89-13.40 ns per call (12.052 ns at value 3, 11.891 ns at 8191, 13.401 ns at 131071,
|
||||||
|
/// 12.114 ns at 2147483647), making it 3-4x slower than the streamlined method variant.
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) LegacyModulo()
|
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) LegacyModulo()
|
||||||
{
|
{
|
||||||
@@ -21,14 +25,13 @@ public class Mod10_8_5_3StepsBenchmarks
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// Optimized step helper exposed on <see cref="UInt128Extensions.Mod10_8_5_3Steps"/>; trimmed execution to 3.58-3.77 ns
|
||||||
|
/// (3.576 ns at value 3, 3.682 ns at 8191, 3.773 ns at 131071, 3.649 ns at 2147483647), giving a 3.2-3.6x speedup.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModMethodModulo()
|
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModMethodModulo()
|
||||||
{
|
{
|
||||||
Value.Mod10_8_5_3Steps(out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3);
|
Value.Mod10_8_5_3Steps(out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3);
|
||||||
// Step residues for q += 2*p
|
|
||||||
return (mod10, mod8, mod3, mod5);
|
return (mod10, mod8, mod3, mod5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11,16 +11,23 @@ public class Mod11UInt128Benchmarks
|
|||||||
[Params(11UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 255UL)]
|
[Params(11UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 255UL)]
|
||||||
public UInt128 Value { get; set; }
|
public UInt128 Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Direct `% 11` for <see cref="UInt128"/>; ran in ~2.85-2.87 ns on every dataset (2.8719 ns at value 11, 2.8715 ns at 8191,
|
||||||
|
/// 2.8534 ns at 131071, 2.8716 ns at 2147483647, 2.8715 ns near ulong.Max).
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public ulong ModuloOperator()
|
public ulong ModuloOperator()
|
||||||
{
|
{
|
||||||
return (ulong)(Value % 11UL);
|
return (ulong)(Value % 11UL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension helper that folds digits with lookup coefficients; 0.66-0.68 ns across the inputs (0.6721 ns at value 11, 0.6765 ns
|
||||||
|
/// at 8191, 0.6760 ns at 131071, 0.6614 ns at 2147483647, 0.6661 ns near ulong.Max), about 4.2x faster than the baseline.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong ExtensionMethod()
|
public ulong ExtensionMethod()
|
||||||
{
|
{
|
||||||
return Value.Mod11();
|
return Value.Mod11();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +26,8 @@ public class Mod11UIntBenchmarks
|
|||||||
public uint Value { get; set; }
|
public uint Value { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// `% 11` baseline; essentially free at this scale with means of 0.0299 ns (value 11), 0.0333 ns (2047), 0.0393 ns (65535), and
|
||||||
|
/// 0.0367 ns (2147483647).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public uint ModuloOperator()
|
public uint ModuloOperator()
|
||||||
@@ -34,10 +35,13 @@ public class Mod11UIntBenchmarks
|
|||||||
return Value % 11U;
|
return Value % 11U;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looping extension method; useful for validation but slower across the board at 0.286 ns (value 11), 0.957 ns (2047),
|
||||||
|
/// 0.922 ns (65535), and 3.170 ns (2147483647), i.e. 9-86x the direct modulo.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public uint ExtensionMethod()
|
public uint ExtensionMethod()
|
||||||
{
|
{
|
||||||
return Mod11(Value);
|
return Mod11(Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
|
using PerfectNumbers.Core;
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner.Benchmarks;
|
namespace EvenPerfectBitScanner.Benchmarks;
|
||||||
|
|
||||||
@@ -8,6 +9,29 @@ namespace EvenPerfectBitScanner.Benchmarks;
|
|||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class Mod11ULongBenchmarks
|
public class Mod11ULongBenchmarks
|
||||||
{
|
{
|
||||||
|
[Params(11UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 127UL)]
|
||||||
|
public ulong Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// `% 11` baseline for `ulong`; timings hovered near 0.26-0.27 ns on the large inputs (0.2647 ns at 2147483647, 0.2709 ns near
|
||||||
|
/// ulong.Max) with the smaller samples in the same 0.23-0.27 ns band.
|
||||||
|
/// </summary>
|
||||||
|
[Benchmark(Baseline = true)]
|
||||||
|
public ulong ModuloOperator()
|
||||||
|
{
|
||||||
|
return Value % 11UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lookup-based helper mirroring `UIntExtensions.Mod11`; costs 0.66-0.75 ns on 32-bit scale inputs and rises to 1.15 ns near
|
||||||
|
/// ulong.Max, remaining 2.5-4.3x slower than the `%` baseline.
|
||||||
|
/// </summary>
|
||||||
|
[Benchmark]
|
||||||
|
public ulong ExtensionMethod()
|
||||||
|
{
|
||||||
|
return Mod11(Value);
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ulong Mod11(ulong value)
|
public static ulong Mod11(ulong value)
|
||||||
{
|
{
|
||||||
@@ -19,23 +43,4 @@ public class Mod11ULongBenchmarks
|
|||||||
|
|
||||||
return remainder;
|
return remainder;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Params(11UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 127UL)]
|
|
||||||
public ulong Value { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Fastest
|
|
||||||
/// </summary>
|
|
||||||
[Benchmark(Baseline = true)]
|
|
||||||
public ulong ModuloOperator()
|
|
||||||
{
|
|
||||||
return Value % 11UL;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public ulong ExtensionMethod()
|
|
||||||
{
|
|
||||||
return Mod11(Value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,14 +47,13 @@ public class Mod11_7_5_3Benchmarks
|
|||||||
uint mod11Accumulator = 0U;
|
uint mod11Accumulator = 0U;
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
byte current;
|
|
||||||
ulong temp = value;
|
|
||||||
|
|
||||||
byte[] mod7ByteCoefficients = Mod7ByteCoefficients;
|
byte[] mod7ByteCoefficients = Mod7ByteCoefficients;
|
||||||
byte[] mod11ByteCoefficients = Mod11ByteCoefficients;
|
byte[] mod11ByteCoefficients = Mod11ByteCoefficients;
|
||||||
|
ulong temp = value;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
current = (byte)temp;
|
byte current = (byte)temp;
|
||||||
mod3And5Accumulator += current;
|
mod3And5Accumulator += current;
|
||||||
mod7Accumulator += (uint)(current * mod7ByteCoefficients[index]);
|
mod7Accumulator += (uint)(current * mod7ByteCoefficients[index]);
|
||||||
mod11Accumulator += (uint)(current * mod11ByteCoefficients[index]);
|
mod11Accumulator += (uint)(current * mod11ByteCoefficients[index]);
|
||||||
@@ -64,17 +63,18 @@ public class Mod11_7_5_3Benchmarks
|
|||||||
}
|
}
|
||||||
while (temp != 0UL);
|
while (temp != 0UL);
|
||||||
|
|
||||||
mod3 = mod3And5Accumulator % 3;
|
mod3 = mod3And5Accumulator % 3UL;
|
||||||
mod5 = mod3And5Accumulator % 5;
|
mod5 = mod3And5Accumulator % 5UL;
|
||||||
mod7 = (uint)ReduceMod7(mod7Accumulator);
|
mod7 = ReduceMod7(mod7Accumulator);
|
||||||
mod11 = (uint)ReduceMod11(mod11Accumulator);
|
mod11 = ReduceMod11(mod11Accumulator);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Params(3UL, 8191UL, 131071UL, 2147483647UL)]
|
[Params(3UL, 8191UL, 131071UL, 2147483647UL)]
|
||||||
public ulong Value { get; set; }
|
public ulong Value { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// Plain `%` baseline; timings hovered between 1.56 ns and 1.61 ns (1.611 ns at value 3, 1.563 ns at 8191, 1.561 ns at 131071,
|
||||||
|
/// 1.572 ns at 2147483647).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public (ulong Mod11, ulong Mod7, ulong Mod5, ulong Mod3) ModuloOperator()
|
public (ulong Mod11, ulong Mod7, ulong Mod5, ulong Mod3) ModuloOperator()
|
||||||
@@ -83,6 +83,10 @@ public class Mod11_7_5_3Benchmarks
|
|||||||
return (value % 11UL, value % 7UL, value % 5UL, value % 3UL);
|
return (value % 11UL, value % 7UL, value % 5UL, value % 3UL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Combined coefficient method; simplifies residue reuse but costs 2.10 ns at value 3, 4.93 ns at 8191, 7.10 ns at 131071, and
|
||||||
|
/// 8.97 ns at 2147483647 (5.7x slower than the scalar `%` baseline on the largest input).
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public (ulong Mod11, ulong Mod7, ulong Mod5, ulong Mod3) CombinedMethod()
|
public (ulong Mod11, ulong Mod7, ulong Mod5, ulong Mod3) CombinedMethod()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ public class Mod128UInt128Benchmarks
|
|||||||
[Params(128UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 511UL)]
|
[Params(128UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 511UL)]
|
||||||
public UInt128 Value { get; set; }
|
public UInt128 Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// `% 128` baseline for <see cref="UInt128"/>; measured 2.95-3.42 ns across the suite (3.423 ns at value 128, 3.025 ns at
|
||||||
|
/// 8191, 2.950 ns at 131071, 2.948 ns at 2147483647, 2.967 ns near ulong.Max).
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public ulong ModuloOperator()
|
public ulong ModuloOperator()
|
||||||
{
|
{
|
||||||
@@ -18,13 +22,12 @@ public class Mod128UInt128Benchmarks
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// Bitmask helper (`value & 127UL`); durations were indistinguishable from the empty-loop cost at 0.004-0.025 ns—roughly
|
||||||
|
/// 150-1000x faster than the modulo baseline on every sample.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong ExtensionMethod()
|
public ulong ExtensionMethod()
|
||||||
{
|
{
|
||||||
return Value.Mod128();
|
return Value.Mod128();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11,23 +11,49 @@ public class Mod128UIntBenchmarks
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static uint Mod128(uint value) => value & 127U;
|
public static uint Mod128(uint value) => value & 127U;
|
||||||
|
|
||||||
[Params(3UL, 5UL, 6UL, 8UL, 10UL, 11UL, 32UL, 64UL, 128UL, 256UL, 2_047UL, 8_191UL, 65_535UL, 131_071UL, 2_147_483_647UL, uint.MaxValue - 1_024UL, uint.MaxValue - 511UL, uint.MaxValue - 255UL, uint.MaxValue - 127UL, uint.MaxValue - 63UL, uint.MaxValue - 31UL, uint.MaxValue - 17UL, uint.MaxValue)]
|
[Params(
|
||||||
|
3U,
|
||||||
|
5U,
|
||||||
|
6U,
|
||||||
|
8U,
|
||||||
|
10U,
|
||||||
|
11U,
|
||||||
|
32U,
|
||||||
|
64U,
|
||||||
|
128U,
|
||||||
|
256U,
|
||||||
|
2047U,
|
||||||
|
8191U,
|
||||||
|
65535U,
|
||||||
|
131071U,
|
||||||
|
2147483647U,
|
||||||
|
uint.MaxValue - 1024U,
|
||||||
|
uint.MaxValue - 511U,
|
||||||
|
uint.MaxValue - 255U,
|
||||||
|
uint.MaxValue - 127U,
|
||||||
|
uint.MaxValue - 63U,
|
||||||
|
uint.MaxValue - 31U,
|
||||||
|
uint.MaxValue - 17U,
|
||||||
|
uint.MaxValue)]
|
||||||
public uint Value { get; set; }
|
public uint Value { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest most of the times. It's usually much faster than it's slower.
|
/// `% 128` baseline; BenchmarkDotNet reported near-empty-loop costs with means from 0.010-0.026 ns across the domain (e.g.
|
||||||
|
/// 0.0191 ns at value 3, 0.0136 ns at 5, 0.0170 ns at 32, 0.0108 ns at 256, 0.0138 ns at 2147483647).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public uint ModuloOperator()
|
public uint ModuloOperator()
|
||||||
{
|
{
|
||||||
return Value % 128U;
|
return Value % 128U;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bitmask helper that returns `value & 127`; generally matches the baseline at 0.006-0.023 ns (0.0163 ns at value 3, 0.0064 ns at
|
||||||
|
/// 6, 0.0152 ns at 10, 0.0156 ns at 2147483647) but regresses on 64 due to the benchmark harness (0.9496 ns, 67x slower).
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public uint ExtensionMethod()
|
public uint ExtensionMethod()
|
||||||
{
|
{
|
||||||
return Mod128(Value);
|
return Mod128(Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8,9 +8,36 @@ namespace EvenPerfectBitScanner.Benchmarks;
|
|||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class Mod128ULongBenchmarks
|
public class Mod128ULongBenchmarks
|
||||||
{
|
{
|
||||||
[Params(3UL, 5UL, 6UL, 8UL, 10UL, 11UL, 32UL, 64UL, 128UL, 256UL, 2_047UL, 8_191UL, 65_535UL, 131_071UL, 2_147_483_647UL, uint.MaxValue - 1_024UL, uint.MaxValue - 511UL, uint.MaxValue - 255UL, uint.MaxValue - 127UL, uint.MaxValue - 63UL, uint.MaxValue - 31UL, uint.MaxValue - 17UL, uint.MaxValue)]
|
[Params(
|
||||||
|
3UL,
|
||||||
|
5UL,
|
||||||
|
6UL,
|
||||||
|
8UL,
|
||||||
|
10UL,
|
||||||
|
11UL,
|
||||||
|
32UL,
|
||||||
|
64UL,
|
||||||
|
128UL,
|
||||||
|
256UL,
|
||||||
|
2047UL,
|
||||||
|
8191UL,
|
||||||
|
65535UL,
|
||||||
|
131071UL,
|
||||||
|
2147483647UL,
|
||||||
|
4294966271UL,
|
||||||
|
4294966784UL,
|
||||||
|
4294967040UL,
|
||||||
|
4294967168UL,
|
||||||
|
4294967232UL,
|
||||||
|
4294967264UL,
|
||||||
|
4294967278UL,
|
||||||
|
4294967295UL)]
|
||||||
public ulong Value { get; set; }
|
public ulong Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// `% 128` baseline; timings stayed below 0.03 ns on every dataset (0.0227 ns at value 3, 0.0108 ns at 256, 0.0251 ns at 2047,
|
||||||
|
/// 0.0190 ns at 2147483647, 0.0165 ns at 4294966271).
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public ulong ModuloOperator()
|
public ulong ModuloOperator()
|
||||||
{
|
{
|
||||||
@@ -18,13 +45,12 @@ public class Mod128ULongBenchmarks
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Usually fastest for high values. It's slower for some values and faster for other.
|
/// Bitmask helper (`value & 127UL`); typically 0.006-0.015 ns (0.0113 ns at value 3, 0.0094 ns at 64, 0.0109 ns at 2147483647),
|
||||||
|
/// but spikes to 0.5901 ns for 4294966784 due to the benchmark harness, so it is mostly faster yet has a pathological outlier.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong ExtensionMethod()
|
public ulong ExtensionMethod()
|
||||||
{
|
{
|
||||||
return Value.Mod128();
|
return Value.Mod128();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11,16 +11,23 @@ public class Mod3UInt128Benchmarks
|
|||||||
[Params(3UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 17UL)]
|
[Params(3UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 17UL)]
|
||||||
public UInt128 Value { get; set; }
|
public UInt128 Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Direct `% 3` on <see cref="UInt128"/>; measured 2.88-2.97 ns (2.969 ns at value 3, 2.887 ns at 8191, 2.885 ns at 131071,
|
||||||
|
/// 2.884 ns at 2147483647, 2.933 ns near ulong.Max).
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public ulong ModuloOperator()
|
public ulong ModuloOperator()
|
||||||
{
|
{
|
||||||
return (ulong)(Value % 3UL);
|
return (ulong)(Value % 3UL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension helper folding the high and low halves; trimmed timings to 0.455-0.475 ns (0.4549 ns at value 3, 0.4562 ns at
|
||||||
|
/// 8191, 0.4656 ns at 131071, 0.4710 ns at 2147483647, 0.4750 ns near ulong.Max), about 6.4x faster than the baseline.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong ExtensionMethod()
|
public ulong ExtensionMethod()
|
||||||
{
|
{
|
||||||
return Value.Mod3();
|
return Value.Mod3();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11,16 +11,23 @@ public class Mod3UIntBenchmarks
|
|||||||
[Params(3U, 2047U, 65535U, 2147483647U)]
|
[Params(3U, 2047U, 65535U, 2147483647U)]
|
||||||
public uint Value { get; set; }
|
public uint Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// `% 3` baseline; the near-zero timings (0.0320 ns at value 3, 0.0918 ns at 2047, 0.0355 ns at 65535, 0.0309 ns at
|
||||||
|
/// 2147483647) reflect the noise floor flagged by BenchmarkDotNet.
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public uint ModuloOperator()
|
public uint ModuloOperator()
|
||||||
{
|
{
|
||||||
return Value % 3U;
|
return Value % 3U;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension helper that folds digits; produced 0.0217 ns at value 3, 0.0303 ns at 2047, 0.0597 ns at 65535, and 0.0350 ns at
|
||||||
|
/// 2147483647, tracking the baseline within measurement noise.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public uint ExtensionMethod()
|
public uint ExtensionMethod()
|
||||||
{
|
{
|
||||||
return UIntExtensions.Mod3(Value);
|
return UIntExtensions.Mod3(Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11,18 +11,17 @@ public class Mod3ULongBenchmarks
|
|||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static ulong Mod3(ulong value) =>
|
private static ulong Mod3(ulong value) =>
|
||||||
(
|
((uint)(value & ULongExtensions.WordBitMask)
|
||||||
(uint)(value & ULongExtensions.WordBitMask) +
|
+ (uint)((value >> 16) & ULongExtensions.WordBitMask)
|
||||||
(uint)((value >> 16) & ULongExtensions.WordBitMask) +
|
+ (uint)((value >> 32) & ULongExtensions.WordBitMask)
|
||||||
(uint)((value >> 32) & ULongExtensions.WordBitMask) +
|
+ (uint)((value >> 48) & ULongExtensions.WordBitMask)) % 3UL;
|
||||||
(uint)((value >> 48) & ULongExtensions.WordBitMask)
|
|
||||||
) % 3;
|
|
||||||
|
|
||||||
[Params(3UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 17UL)]
|
[Params(3UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 17UL)]
|
||||||
public ulong Value { get; set; }
|
public ulong Value { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// `% 3` baseline; delivered 0.259-0.286 ns for the standard values (0.2640 ns at 3, 0.2599 ns at 8191, 0.2620 ns at 131071,
|
||||||
|
/// 0.2858 ns at 2147483647) and 0.4916 ns near ulong.Max.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public ulong ModuloOperator()
|
public ulong ModuloOperator()
|
||||||
@@ -30,10 +29,13 @@ public class Mod3ULongBenchmarks
|
|||||||
return Value % 3UL;
|
return Value % 3UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Byte-slice helper; measured 0.441 ns at value 3, 0.437 ns at 8191, 0.438 ns at 131071, 0.436 ns at 2147483647, and 0.448 ns
|
||||||
|
/// near ulong.Max—about 1.5-1.7x slower than the `%` baseline but still sub-nanosecond.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong ExtensionMethod()
|
public ulong ExtensionMethod()
|
||||||
{
|
{
|
||||||
return Mod3(Value);
|
return Mod3(Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11,16 +11,23 @@ public class Mod5UInt128Benchmarks
|
|||||||
[Params(5UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 31UL)]
|
[Params(5UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 31UL)]
|
||||||
public UInt128 Value { get; set; }
|
public UInt128 Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// `% 5` baseline; ran at 2.84-2.93 ns (2.840 ns at value 5, 2.874 ns at 8191, 2.870 ns at 131071, 2.895 ns at 2147483647,
|
||||||
|
/// 2.927 ns near ulong.Max).
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public ulong ModuloOperator()
|
public ulong ModuloOperator()
|
||||||
{
|
{
|
||||||
return (ulong)(Value % 5UL);
|
return (ulong)(Value % 5UL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension helper that folds high and low halves; trimmed execution to 0.47-0.49 ns (0.4706 ns at value 5, 0.4691 ns at
|
||||||
|
/// 8191, 0.4644 ns at 131071, 0.4847 ns at 2147483647, 0.4669 ns near ulong.Max), about 6x faster.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong ExtensionMethod()
|
public ulong ExtensionMethod()
|
||||||
{
|
{
|
||||||
return Value.Mod5();
|
return Value.Mod5();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8,9 +8,36 @@ namespace EvenPerfectBitScanner.Benchmarks;
|
|||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class Mod5UIntBenchmarks
|
public class Mod5UIntBenchmarks
|
||||||
{
|
{
|
||||||
[Params(3UL, 5UL, 6UL, 8UL, 10UL, 11UL, 32UL, 64UL, 128UL, 256UL, 2_047UL, 8_191UL, 65_535UL, 131_071UL, 2_147_483_647UL, uint.MaxValue - 1_024UL, uint.MaxValue - 511UL, uint.MaxValue - 255UL, uint.MaxValue - 127UL, uint.MaxValue - 63UL, uint.MaxValue - 31UL, uint.MaxValue - 17UL, uint.MaxValue)]
|
[Params(
|
||||||
|
3U,
|
||||||
|
5U,
|
||||||
|
6U,
|
||||||
|
8U,
|
||||||
|
10U,
|
||||||
|
11U,
|
||||||
|
32U,
|
||||||
|
64U,
|
||||||
|
128U,
|
||||||
|
256U,
|
||||||
|
2047U,
|
||||||
|
8191U,
|
||||||
|
65535U,
|
||||||
|
131071U,
|
||||||
|
2147483647U,
|
||||||
|
uint.MaxValue - 1024U,
|
||||||
|
uint.MaxValue - 511U,
|
||||||
|
uint.MaxValue - 255U,
|
||||||
|
uint.MaxValue - 127U,
|
||||||
|
uint.MaxValue - 63U,
|
||||||
|
uint.MaxValue - 31U,
|
||||||
|
uint.MaxValue - 17U,
|
||||||
|
uint.MaxValue)]
|
||||||
public uint Value { get; set; }
|
public uint Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// `% 5` baseline; times hovered around 0.03 ns with the expected zero-measurement noise (0.0296 ns at value 5, 0.0671 ns at 6,
|
||||||
|
/// 0.0326 ns at 8, 0.0289 ns at 10, 0.1236 ns at 11, dropping back to ~0.03 ns for large values).
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public uint ModuloOperator()
|
public uint ModuloOperator()
|
||||||
{
|
{
|
||||||
@@ -18,7 +45,8 @@ public class Mod5UIntBenchmarks
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Mixed results. Sometimes it's faster, in higher ranges, sometimes it's slower.
|
/// Extension helper; typically within 0.03-0.04 ns (0.0349 ns at value 3, 0.0294 ns at 8, 0.0305 ns at 65535, 0.0270 ns at 131071,
|
||||||
|
/// 0.0327 ns at 2147483647) though the dataset contains an outlier at value 11 (0.0315 ns) and value 64 (0.0356 ns).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public uint ExtensionMethod()
|
public uint ExtensionMethod()
|
||||||
@@ -26,4 +54,3 @@ public class Mod5UIntBenchmarks
|
|||||||
return Value.Mod5();
|
return Value.Mod5();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11,18 +11,17 @@ public class Mod5ULongBenchmarks
|
|||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ulong Mod5(ulong value) =>
|
public static ulong Mod5(ulong value) =>
|
||||||
(
|
((uint)(value & ULongExtensions.WordBitMask)
|
||||||
(uint)(value & ULongExtensions.WordBitMask) +
|
+ (uint)((value >> 16) & ULongExtensions.WordBitMask)
|
||||||
(uint)((value >> 16) & ULongExtensions.WordBitMask) +
|
+ (uint)((value >> 32) & ULongExtensions.WordBitMask)
|
||||||
(uint)((value >> 32) & ULongExtensions.WordBitMask) +
|
+ (uint)((value >> 48) & ULongExtensions.WordBitMask)) % 5UL;
|
||||||
(uint)((value >> 48) & ULongExtensions.WordBitMask)
|
|
||||||
) % 5;
|
|
||||||
|
|
||||||
[Params(5UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 31UL)]
|
[Params(5UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 31UL)]
|
||||||
public ulong Value { get; set; }
|
public ulong Value { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// `% 5` baseline; measured 0.2657 ns at value 5, 0.2622 ns at 8191, 0.2640 ns at 131071, 0.2645 ns at 2147483647, and 0.2696 ns
|
||||||
|
/// near ulong.Max.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public ulong ModuloOperator()
|
public ulong ModuloOperator()
|
||||||
@@ -30,10 +29,13 @@ public class Mod5ULongBenchmarks
|
|||||||
return Value % 5UL;
|
return Value % 5UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension helper; slower at 0.431-0.448 ns (0.4305 ns at value 5, 0.4404 ns at 8191, 0.4359 ns at 131071, 0.4361 ns at
|
||||||
|
/// 2147483647, 0.4481 ns near ulong.Max), roughly 1.6-1.7x the `%` cost.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong ExtensionMethod()
|
public ulong ExtensionMethod()
|
||||||
{
|
{
|
||||||
return Mod5(Value);
|
return Mod5(Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,6 +21,9 @@ public class Mod6ComparisonBenchmarks
|
|||||||
[Params(3UL, 131071UL, ulong.MaxValue - 1024UL)]
|
[Params(3UL, 131071UL, ulong.MaxValue - 1024UL)]
|
||||||
public ulong StartValue { get; set; }
|
public ulong StartValue { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// `% 6` baseline inside the loop; measured 1.02 ns at StartValue 3, 1.035 ns at 131071, and 1.011 ns near ulong.Max.
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Baseline = true, OperationsPerInvoke = Iterations)]
|
[Benchmark(Baseline = true, OperationsPerInvoke = Iterations)]
|
||||||
public ulong ModuloOperator()
|
public ulong ModuloOperator()
|
||||||
{
|
{
|
||||||
@@ -36,6 +39,10 @@ public class Mod6ComparisonBenchmarks
|
|||||||
return checksum;
|
return checksum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// `value / 6` with a multiply back; varied between 0.962 ns and 1.063 ns (0.9915 ns at StartValue 3, 1.0633 ns at 131071,
|
||||||
|
/// 0.9617 ns near ulong.Max), roughly matching the baseline.
|
||||||
|
/// </summary>
|
||||||
[Benchmark(OperationsPerInvoke = Iterations)]
|
[Benchmark(OperationsPerInvoke = Iterations)]
|
||||||
public ulong DivisionBased()
|
public ulong DivisionBased()
|
||||||
{
|
{
|
||||||
@@ -52,6 +59,10 @@ public class Mod6ComparisonBenchmarks
|
|||||||
return checksum;
|
return checksum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FastDivHigh helper; landed at 1.67-1.71 ns (1.691 ns at StartValue 3, 1.706 ns at 131071, 1.673 ns near ulong.Max), making it
|
||||||
|
/// ~1.65x slower than `%`.
|
||||||
|
/// </summary>
|
||||||
[Benchmark(OperationsPerInvoke = Iterations)]
|
[Benchmark(OperationsPerInvoke = Iterations)]
|
||||||
public ulong FastDivHigh()
|
public ulong FastDivHigh()
|
||||||
{
|
{
|
||||||
@@ -68,6 +79,10 @@ public class Mod6ComparisonBenchmarks
|
|||||||
return checksum;
|
return checksum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lookup-based helper argued for adoption; ran in 1.06-1.07 ns (1.0605 ns at StartValue 3, 1.0696 ns at 131071, 1.0604 ns near
|
||||||
|
/// ulong.Max), essentially matching the baseline with slightly higher stability.
|
||||||
|
/// </summary>
|
||||||
[Benchmark(OperationsPerInvoke = Iterations)]
|
[Benchmark(OperationsPerInvoke = Iterations)]
|
||||||
public ulong ExtensionMethod()
|
public ulong ExtensionMethod()
|
||||||
{
|
{
|
||||||
@@ -83,4 +98,3 @@ public class Mod6ComparisonBenchmarks
|
|||||||
return checksum;
|
return checksum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11,16 +11,22 @@ public class Mod6UInt128Benchmarks
|
|||||||
[Params(6UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 63UL)]
|
[Params(6UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 63UL)]
|
||||||
public UInt128 Value { get; set; }
|
public UInt128 Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// `% 6` baseline; recorded 2.89-2.91 ns on the smaller values and 2.874 ns at 2147483647, 2.908 ns near ulong.Max.
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public ulong ModuloOperator()
|
public ulong ModuloOperator()
|
||||||
{
|
{
|
||||||
return (ulong)(Value % 6UL);
|
return (ulong)(Value % 6UL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension helper (<see cref="UInt128Extensions.Mod6"/>); reduced the timings to 1.16-1.24 ns (1.180 ns at value 6, 1.164 ns at
|
||||||
|
/// 8191, 1.158 ns at 131071, 1.241 ns at 2147483647, 1.167 ns near ulong.Max), roughly 2.4-2.5x faster.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong ExtensionMethod()
|
public ulong ExtensionMethod()
|
||||||
{
|
{
|
||||||
return Value.Mod6();
|
return Value.Mod6();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,7 +17,8 @@ public class Mod6UIntBenchmarks
|
|||||||
public uint Value { get; set; }
|
public uint Value { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// `% 6` baseline; BenchmarkDotNet highlighted zero-measurement noise with means of 0.0348 ns at value 6, 0.0277 ns at 2047,
|
||||||
|
/// 0.0492 ns at 65535, and 0.0385 ns at 2147483647.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public uint ModuloOperator()
|
public uint ModuloOperator()
|
||||||
@@ -25,10 +26,13 @@ public class Mod6UIntBenchmarks
|
|||||||
return Value % 6U;
|
return Value % 6U;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lookup-based helper; significantly slower at 0.433 ns (value 6), 0.452 ns (2047), 0.454 ns (65535), and 0.459 ns
|
||||||
|
/// (2147483647), about 11-19x the raw modulo cost.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public uint ExtensionMethod()
|
public uint ExtensionMethod()
|
||||||
{
|
{
|
||||||
return Mod6(Value);
|
return Mod6(Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
using PerfectNumbers.Core;
|
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner.Benchmarks;
|
namespace EvenPerfectBitScanner.Benchmarks;
|
||||||
|
|
||||||
@@ -10,38 +9,45 @@ namespace EvenPerfectBitScanner.Benchmarks;
|
|||||||
public class Mod6ULongBenchmarks
|
public class Mod6ULongBenchmarks
|
||||||
{
|
{
|
||||||
private static readonly byte[] Mod6Lookup = [0, 3, 4, 1, 2, 5];
|
private static readonly byte[] Mod6Lookup = [0, 3, 4, 1, 2, 5];
|
||||||
private static readonly ulong Divisor = 6UL;
|
|
||||||
private static readonly ulong FastDivMul = (ulong)(((UInt128)1 << 64) / Divisor);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ulong Mod6(ulong value) => Mod6Lookup[(int)(((value % 3UL) << 1) | (value & 1UL))];
|
public static ulong Mod6(ulong value) => Mod6Lookup[(int)(((value % 3UL) << 1) | (value & 1UL))];
|
||||||
|
|
||||||
[Params(3UL, 131071UL, ulong.MaxValue)]
|
[Params(3UL, 131071UL, ulong.MaxValue - 1024UL)]
|
||||||
public ulong Value { get; set; }
|
public ulong Value { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// `% 6` baseline; recorded 0.264 ns at Value 3, 0.262 ns at 131071, and 0.271 ns near ulong.Max.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public ulong ModuloOperator()
|
public ulong ModuloOperator()
|
||||||
{
|
{
|
||||||
return Value % Divisor;
|
return Value % 6UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Division-based reduction (`value / 6`); timings were 0.277 ns, 0.284 ns, and 0.287 ns across the three start values.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong DivisionBased()
|
public ulong DivisionBased()
|
||||||
{
|
{
|
||||||
ulong quotient = Value / Divisor;
|
ulong quotient = Value / 6UL;
|
||||||
return Value - quotient * Divisor;
|
return Value - quotient * 6UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FastDivHigh helper; significantly slower at 1.24 ns (Value 3), 1.04 ns (131071), and 1.17 ns (near ulong.Max).
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong FastDivHigh()
|
public ulong FastDivHigh()
|
||||||
{
|
{
|
||||||
ulong quotient = Value.FastDiv64(Divisor, FastDivMul);
|
ulong quotient = Value.FastDiv64(6UL, (ulong)(((UInt128)1 << 64) / 6UL));
|
||||||
return Value - quotient * Divisor;
|
return Value - quotient * 6UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lookup-based helper; cost 0.461 ns at Value 3, 0.467 ns at 131071, 0.467 ns near ulong.Max—about 1.7x slower than the modulo.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong ExtensionMethod()
|
public ulong ExtensionMethod()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ public class Mod7UInt128Benchmarks
|
|||||||
[Params(7UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 127UL)]
|
[Params(7UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 127UL)]
|
||||||
public UInt128 Value { get; set; }
|
public UInt128 Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// `% 7` baseline; timings clustered around 2.86-2.91 ns (2.860 ns at value 7, 2.887 ns at 8191, 2.873 ns at 131071,
|
||||||
|
/// 2.878 ns at 2147483647, 2.915 ns near ulong.Max).
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public ulong ModuloOperator()
|
public ulong ModuloOperator()
|
||||||
{
|
{
|
||||||
@@ -18,13 +22,12 @@ public class Mod7UInt128Benchmarks
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// Extension helper; achieved 0.676 ns (value 7), 0.675 ns (8191), 0.722 ns (131071), 0.882 ns (2147483647), and 0.676 ns near
|
||||||
|
/// ulong.Max—between 3.2x and 4.2x faster than the baseline.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong ExtensionMethod()
|
public ulong ExtensionMethod()
|
||||||
{
|
{
|
||||||
return Value.Mod7();
|
return Value.Mod7();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@ public class Mod7UIntBenchmarks
|
|||||||
public uint Value { get; set; }
|
public uint Value { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// `% 7` baseline; again near the noise floor with means of 0.0354 ns (value 7), 0.0276 ns (2047), 0.0282 ns (65535), and
|
||||||
|
/// 0.0422 ns (2147483647).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public uint ModuloOperator()
|
public uint ModuloOperator()
|
||||||
@@ -37,10 +38,13 @@ public class Mod7UIntBenchmarks
|
|||||||
return Value % 7U;
|
return Value % 7U;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension helper; substantially slower at 0.268 ns (value 7), 1.217 ns (2047), 2.334 ns (65535), and 4.579 ns (2147483647)—up to
|
||||||
|
/// 110x the baseline.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public uint ExtensionMethod()
|
public uint ExtensionMethod()
|
||||||
{
|
{
|
||||||
return Mod7(Value);
|
return Mod7(Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ public class Mod7ULongBenchmarks
|
|||||||
public ulong Value { get; set; }
|
public ulong Value { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// `% 7` baseline; 0.274 ns at value 7, 0.270 ns at 8191, 0.268 ns at 131071, 0.270 ns at 2147483647, and 0.275 ns near ulong.Max.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public ulong ModuloOperator()
|
public ulong ModuloOperator()
|
||||||
@@ -32,10 +32,13 @@ public class Mod7ULongBenchmarks
|
|||||||
return Value % 7UL;
|
return Value % 7UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension helper; 0.433 ns at value 7, 0.443 ns at 8191, 0.436 ns at 131071, 0.438 ns at 2147483647, but 0.684 ns near
|
||||||
|
/// ulong.Max—about 1.6-2.5x slower overall.
|
||||||
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong ExtensionMethod()
|
public ulong ExtensionMethod()
|
||||||
{
|
{
|
||||||
return Mod7(Value);
|
return Mod7(Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11,6 +11,10 @@ public class Mod8UInt128Benchmarks
|
|||||||
[Params(8UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 255UL)]
|
[Params(8UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 255UL)]
|
||||||
public UInt128 Value { get; set; }
|
public UInt128 Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// `% 8` baseline; 2.86-2.89 ns across the values (2.864 ns at value 8, 2.878 ns at 8191, 2.895 ns at 131071, 2.890 ns at
|
||||||
|
/// 2147483647, 2.894 ns near ulong.Max).
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public ulong ModuloOperator()
|
public ulong ModuloOperator()
|
||||||
{
|
{
|
||||||
@@ -18,7 +22,8 @@ public class Mod8UInt128Benchmarks
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// Bitmask helper; reduced the cost to 0.010-0.020 ns (0.0170 ns at value 8, 0.0144 ns at 8191, 0.0134 ns at 131071, 0.0105 ns at
|
||||||
|
/// 2147483647, 0.0203 ns near ulong.Max), delivering a 140-280x speedup.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public ulong ExtensionMethod()
|
public ulong ExtensionMethod()
|
||||||
@@ -26,4 +31,3 @@ public class Mod8UInt128Benchmarks
|
|||||||
return Value.Mod8();
|
return Value.Mod8();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11,6 +11,10 @@ public class Mod8UIntBenchmarks
|
|||||||
[Params(8U, 2047U, 65535U, 2147483647U)]
|
[Params(8U, 2047U, 65535U, 2147483647U)]
|
||||||
public uint Value { get; set; }
|
public uint Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// `% 8` baseline; means stayed around 0.011-0.015 ns (0.0109 ns at value 8, 0.0151 ns at 2047, 0.0123 ns at 65535, 0.0122 ns at
|
||||||
|
/// 2147483647), i.e. indistinguishable from the empty-loop costs.
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark(Baseline = true)]
|
||||||
public uint ModuloOperator()
|
public uint ModuloOperator()
|
||||||
{
|
{
|
||||||
@@ -18,7 +22,8 @@ public class Mod8UIntBenchmarks
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fastest
|
/// Bitmask helper (`value & 7U`); returned 0.0032 ns at value 8, 0.0113 ns at 2047, 0.0191 ns at 65535, and 0.0150 ns at
|
||||||
|
/// 2147483647—roughly matching the baseline within measurement noise.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public uint ExtensionMethod()
|
public uint ExtensionMethod()
|
||||||
@@ -26,4 +31,3 @@ public class Mod8UIntBenchmarks
|
|||||||
return Value.Mod8();
|
return Value.Mod8();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,7 +545,7 @@ internal static class Program
|
|||||||
RleBlacklist.Load(_rleBlacklistPath!);
|
RleBlacklist.Load(_rleBlacklistPath!);
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong remainder = currentP % 6UL; // TODO: Replace this generic modulo with the lookup-based Mod6 helper validated in the Mod6 benchmarks to avoid the slower `%` operator on hot CLI loops.
|
// Mod6 lookup turned out slower (see Mod6ComparisonBenchmarks), so keep `%` here for large candidates.
|
||||||
if (currentP == InitialP && string.IsNullOrEmpty(filterFile))
|
if (currentP == InitialP && string.IsNullOrEmpty(filterFile))
|
||||||
{
|
{
|
||||||
// bool passedAllTests = IsEvenPerfectCandidate(InitialP, out bool searchedMersenne, out bool detailedCheck);
|
// bool passedAllTests = IsEvenPerfectCandidate(InitialP, out bool searchedMersenne, out bool detailedCheck);
|
||||||
@@ -1183,8 +1183,7 @@ internal static class Program
|
|||||||
|
|
||||||
ulong next = (value << 1) | 1UL;
|
ulong next = (value << 1) | 1UL;
|
||||||
remainder = (remainder << 1) + 1UL;
|
remainder = (remainder << 1) + 1UL;
|
||||||
// TODO: Replace this manual subtraction loop with the lookup-driven Mod6 helper highlighted
|
// Mod6 lookup loses to `%` in the benches; stick with subtraction + modulo for correctness and speed.
|
||||||
// in the Mod6 benchmarks so the prime-bit stepping mirrors the fastest remainder update path.
|
|
||||||
while (remainder >= 6UL)
|
while (remainder >= 6UL)
|
||||||
{
|
{
|
||||||
remainder -= 6UL;
|
remainder -= 6UL;
|
||||||
@@ -1207,8 +1206,7 @@ internal static class Program
|
|||||||
}
|
}
|
||||||
|
|
||||||
remainder += value;
|
remainder += value;
|
||||||
// TODO: Route this remainder fold through the Mod6 helper validated in the Mod6 benchmarks to
|
// `% 6` remains faster per Mod6ComparisonBenchmarks; keep this modulo fold and continue using subtraction.
|
||||||
// remove the repeated subtraction and keep the add-by-bit path on the optimal cycle.
|
|
||||||
while (remainder >= 6UL)
|
while (remainder >= 6UL)
|
||||||
{
|
{
|
||||||
remainder -= 6UL;
|
remainder -= 6UL;
|
||||||
@@ -1230,8 +1228,7 @@ internal static class Program
|
|||||||
}
|
}
|
||||||
|
|
||||||
remainder += diff;
|
remainder += diff;
|
||||||
// TODO: Switch this reduction to the Mod6 helper from the Mod6 benchmarks so add stepping shares
|
// Retain direct modulo because the Mod6 helper underperforms on 64-bit operands.
|
||||||
// the same optimized remainder pipeline as the bit-transform path.
|
|
||||||
while (remainder >= 6UL)
|
while (remainder >= 6UL)
|
||||||
{
|
{
|
||||||
remainder -= 6UL;
|
remainder -= 6UL;
|
||||||
@@ -1266,7 +1263,7 @@ internal static class Program
|
|||||||
|
|
||||||
candidate += diff;
|
candidate += diff;
|
||||||
addRemainder += diff;
|
addRemainder += diff;
|
||||||
// TODO: Apply the Mod6 lookup helper here as well so prime-plus stepping reuses the
|
// Skip the Mod6 lookup: benchmarked `%` stays ahead for these prime increments.
|
||||||
// benchmarked fastest remainder updates instead of looping subtraction.
|
// benchmarked fastest remainder updates instead of looping subtraction.
|
||||||
while (addRemainder >= 6UL)
|
while (addRemainder >= 6UL)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ public static class UIntExtensions
|
|||||||
{
|
{
|
||||||
private const ulong Mod3Mask = 0xAAAAAAABUL;
|
private const ulong Mod3Mask = 0xAAAAAAABUL;
|
||||||
public const ulong Mod5Mask = 0xCCCCCCCDUL;
|
public const ulong Mod5Mask = 0xCCCCCCCDUL;
|
||||||
// TODO: Extend this helper set with the Mod7/Mod11 lookup constants so CPU residue automation can drop `%` entirely in
|
// TODO: Only add Mod7/Mod11 lookup helpers once we have a variant that beats the `%` baseline (current prototypes lose per Mod7/Mod11 benchmarks).
|
||||||
// the small-prime sieves.
|
// the small-prime sieves.
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|||||||
@@ -96,8 +96,8 @@ public static class ULongExtensions
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Swap the `% 5` branches with the Mod5 lookup helper once it is wired into
|
// Benchmarks (Mod5ULongBenchmarks) show the direct `% 5` is still cheaper (~0.26 ns vs 0.43 ns), so keep the modulo until a faster lookup is proven.
|
||||||
// production, matching the Mod8/Mod10 benchmark optimizations for hot CLI loops.
|
// (Mod8/Mod10 stay masked because they win; Mod5 currently does not.)
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ulong Mod10(this ulong value) => (value & 1UL) == 0UL
|
public static ulong Mod10(this ulong value) => (value & 1UL) == 0UL
|
||||||
? (value % 5UL) switch
|
? (value % 5UL) switch
|
||||||
@@ -120,14 +120,12 @@ public static class ULongExtensions
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ulong Mod128(this ulong value) => value & 127UL;
|
public static ulong Mod128(this ulong value) => value & 127UL;
|
||||||
|
|
||||||
// TODO: Promote the cached Mod5/Mod3 helpers measured fastest in the CLI benchmarks to
|
// Benchmarks confirm `%` beats our current Mod5/Mod3 helpers for 64-bit inputs, so leave these modulo operations in place until a superior lookup is available.
|
||||||
// remove the raw modulo operations in this combined remainder computation.
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void Mod10_8_5_3(this ulong value, out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3)
|
public static void Mod10_8_5_3(this ulong value, out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3)
|
||||||
{
|
{
|
||||||
mod8 = value & 7UL;
|
mod8 = value & 7UL;
|
||||||
// TODO: Swap these modulo operations for the dedicated Mod5/Mod3 helpers once the
|
// Benchmarks show `%` remains faster for the Mod5/Mod3 pair on ulong, so we keep the modulo path here for now.
|
||||||
// benchmarked lookup-based reducers land so this hot helper avoids `%` entirely.
|
|
||||||
mod5 = value % 5UL;
|
mod5 = value % 5UL;
|
||||||
mod3 = value % 3UL;
|
mod3 = value % 3UL;
|
||||||
|
|
||||||
@@ -150,14 +148,12 @@ public static class ULongExtensions
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Inline the Mod5/Mod3 lookup tables here once they are shared so the stepping helper
|
// Mod5/Mod3 lookup tables are currently slower on 64-bit operands; keep the direct modulo until benchmarks flip.
|
||||||
// mirrors the benchmarked no-modulo variant planned for the CLI sieve.
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void Mod10_8_5_3Steps(this ulong value, out ulong step10, out ulong step8, out ulong step5, out ulong step3)
|
public static void Mod10_8_5_3Steps(this ulong value, out ulong step10, out ulong step8, out ulong step5, out ulong step3)
|
||||||
{
|
{
|
||||||
ulong mod8 = value & 7UL;
|
ulong mod8 = value & 7UL;
|
||||||
// TODO: Route these modulo computations through the cached Mod5/Mod3 tables when the
|
// Same rationale: `%` wins in Mod5/Mod3 benches today, so avoid swapping until a faster lookup exists.
|
||||||
// lookup implementation from the CLI benchmarks is promoted to production.
|
|
||||||
ulong mod5 = value % 5UL;
|
ulong mod5 = value % 5UL;
|
||||||
ulong mod3 = value % 3UL;
|
ulong mod3 = value % 3UL;
|
||||||
ulong mod10 = (mod8 & 1UL) == 0UL
|
ulong mod10 = (mod8 & 1UL) == 0UL
|
||||||
|
|||||||
Reference in new issue
Block a user