Optimizations and tiny cleanup

This commit is contained in:
Marek Lemańczyk committed 2025-10-01 13:02:07 +02:00
1 parent e14cb5df52
commit 513fc7acb0
6 files changed
+44 -41

No files matched your search

+5 -1
View File
@@ -2,6 +2,10 @@
"dotnet.defaultSolution": "FlexiCoder.sln", "dotnet.defaultSolution": "FlexiCoder.sln",
"chatgpt.openOnStartup": true, "chatgpt.openOnStartup": true,
"cSpell.words": [ "cSpell.words": [
"ILGPU" "CCCCCCCDUL",
"ILGPU",
"mers",
"primality",
"suff"
] ]
} }
@@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Jobs;
@@ -29,17 +29,17 @@ public class GpuUInt128BinaryGcdBenchmarks
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public GpuUInt128 ReusedVariables() public GpuUInt128 ReusedVariables()
{ {
return GpuUInt128.BinaryGcd(Input.Left, Input.Right); return BinaryGcdWithReusedVariables(Input.Left, Input.Right);
} }
[Benchmark] [Benchmark]
public GpuUInt128 TemporaryStructPerIteration() public GpuUInt128 TemporaryStructPerIteration()
{ {
return BinaryGcdWithTemporary(Input.Left, Input.Right); return GpuUInt128.BinaryGcd(Input.Left, Input.Right);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static GpuUInt128 BinaryGcdWithTemporary(GpuUInt128 u, GpuUInt128 v) private static GpuUInt128 BinaryGcdWithReusedVariables(GpuUInt128 u, GpuUInt128 v)
{ {
if (u.IsZero) if (u.IsZero)
{ {
@@ -51,7 +51,12 @@ public class GpuUInt128BinaryGcdBenchmarks
return u; return u;
} }
int shift = GpuUInt128.TrailingZeroCount(new GpuUInt128(u.High | v.High, u.Low | v.Low)); ulong combinedLow = u.Low | v.Low;
ulong combinedHigh = u.High | v.High;
int shift = combinedLow != 0UL
? BitOperations.TrailingZeroCount(combinedLow)
: 64 + BitOperations.TrailingZeroCount(combinedHigh);
int zu = GpuUInt128.TrailingZeroCount(u); int zu = GpuUInt128.TrailingZeroCount(u);
u >>= zu; u >>= zu;
@@ -24,7 +24,7 @@ public class GpuUInt128Mul64Benchmarks
public static IEnumerable<Mul64Input> GetInputs() => Inputs; public static IEnumerable<Mul64Input> GetInputs() => Inputs;
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public GpuUInt128 HighProductMaterialisedInLocal() public GpuUInt128 HighProductMaterializedInLocal()
{ {
GpuUInt128 value = Input.Multiplicand; GpuUInt128 value = Input.Multiplicand;
value.Mul64(Input.Multiplier); value.Mul64(Input.Multiplier);
@@ -10,8 +10,8 @@ namespace EvenPerfectBitScanner.Benchmarks;
[MemoryDiagnoser] [MemoryDiagnoser]
public class GpuUInt128MulModBenchmarks public class GpuUInt128MulModBenchmarks
{ {
private static readonly MulModInput[] Inputs = new MulModInput[] private static readonly MulModInput[] Inputs =
{ [
new( new(
new GpuUInt128(0UL, 5UL), new GpuUInt128(0UL, 5UL),
new GpuUInt128(0UL, 7UL), new GpuUInt128(0UL, 7UL),
@@ -32,7 +32,7 @@ public class GpuUInt128MulModBenchmarks
new GpuUInt128(0x7FFF_FFFF_FFFF_FFFFUL, 0x8000_0000_0000_0000UL), new GpuUInt128(0x7FFF_FFFF_FFFF_FFFFUL, 0x8000_0000_0000_0000UL),
new GpuUInt128(0x7FFF_FFFF_FFFF_FFFFUL, 0xFFFF_FFFF_FFFF_FFF1UL), new GpuUInt128(0x7FFF_FFFF_FFFF_FFFFUL, 0xFFFF_FFFF_FFFF_FFF1UL),
"MixedMagnitude"), "MixedMagnitude"),
}; ];
[ParamsSource(nameof(GetInputs))] [ParamsSource(nameof(GetInputs))]
public MulModInput Input { get; set; } public MulModInput Input { get; set; }
@@ -42,6 +42,7 @@ public class GpuUInt128MulModBenchmarks
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public GpuUInt128 InPlaceMulMod() public GpuUInt128 InPlaceMulMod()
{ {
// TODO: Switch the extension method to allocate per iteration, as it's faster. Keep the benchmarks.
GpuUInt128 value = Input.Left; GpuUInt128 value = Input.Left;
value.MulMod(Input.Right, Input.Modulus); value.MulMod(Input.Right, Input.Modulus);
return value; return value;
@@ -56,6 +57,7 @@ public class GpuUInt128MulModBenchmarks
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static GpuUInt128 MulModInline(GpuUInt128 left, GpuUInt128 right, GpuUInt128 modulus) private static GpuUInt128 MulModInline(GpuUInt128 left, GpuUInt128 right, GpuUInt128 modulus)
{ {
// Reuse the variables to new instances and lower registry pressure, if possible.
GpuUInt128 a = left; GpuUInt128 a = left;
GpuUInt128 b = right; GpuUInt128 b = right;
GpuUInt128 result = new(); GpuUInt128 result = new();
@@ -42,6 +42,7 @@ public class GpuUInt128MulModByLimbBenchmarks
[Benchmark] [Benchmark]
public GpuUInt128 InPlaceReduction() public GpuUInt128 InPlaceReduction()
{ {
// TODO: Switch the extension method back to legacy method, as it's faster. Keep the benchmarks.
GpuUInt128 value = Input.Left; GpuUInt128 value = Input.Left;
value.MulModByLimb(Input.Right, Input.Modulus); value.MulModByLimb(Input.Right, Input.Modulus);
return value; return value;
+22 -31
View File
@@ -535,12 +535,7 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
return u; return u;
} }
ulong combinedLow = u.Low | v.Low; int shift = TrailingZeroCount(new GpuUInt128(u.High | v.High, u.Low | v.Low));
ulong combinedHigh = u.High | v.High;
int shift = combinedLow != 0UL
? BitOperations.TrailingZeroCount(combinedLow)
: 64 + BitOperations.TrailingZeroCount(combinedHigh);
int zu = TrailingZeroCount(u); int zu = TrailingZeroCount(u);
u >>= zu; u >>= zu;
@@ -762,7 +757,7 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong MulMod(GpuUInt128 other, ulong modulus) public readonly ulong MulMod(GpuUInt128 other, ulong modulus)
{ {
ulong modulusLocal = modulus; ulong modulusLocal = modulus;
ulong a = Low % modulusLocal; ulong a = Low % modulusLocal;
@@ -779,24 +774,24 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong MulMod(ulong value, ulong modulus) public readonly ulong MulMod(ulong value, ulong modulus)
{ {
ulong modulusLocal = modulus; ulong a = Low % modulus;
ulong a = Low % modulusLocal; ulong b = value % modulus;
ulong b = value % modulusLocal;
if (a == 0UL || b == 0UL) if (a == 0UL || b == 0UL)
{ {
return 0UL; return 0UL;
} }
return b <= ulong.MaxValue / a ulong ulongRange = ulong.MaxValue / a;
? (a * b) % modulusLocal return b <= ulongRange
: MulMod64(a, b, modulusLocal); ? (a * b) % modulus
: MulMod64(a, b, modulus);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong MulModSimplified(ulong value, ulong modulus) public readonly ulong MulModSimplified(ulong value, ulong modulus)
{ {
ulong modulusLocal = modulus; ulong modulusLocal = modulus;
ulong a = Low % modulusLocal; ulong a = Low % modulusLocal;
@@ -811,37 +806,33 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong MulModWithNativeModulo(ulong value, ulong modulus) public readonly ulong MulModWithNativeModulo(ulong value, ulong modulus)
{ {
ulong modulusLocal = modulus; ulong multiplicand = Low % modulus;
ulong multiplicand = Low % modulusLocal; var remainder = value % modulus;
ulong multiplier = value % modulusLocal;
if (multiplicand == 0UL || multiplier == 0UL) if (multiplicand == 0UL || remainder == 0UL)
{ {
return 0UL; return 0UL;
} }
ulong result = 0UL; ulong result = 0UL;
ulong current = multiplicand;
ulong remaining = multiplier;
while (true) while (true)
{ {
ulong chunk = remaining & NativeModuloChunkMask; ulong chunk = remainder & NativeModuloChunkMask;
if (chunk != 0UL) if (chunk != 0UL)
{ {
ulong chunkContribution = MultiplyChunkModulo(current, chunk, modulusLocal); chunk = MultiplyChunkModulo(multiplicand, chunk, modulus);
result = (result + chunkContribution) % modulusLocal; result = (result + chunk) % modulus;
} }
remaining >>= NativeModuloChunkBits; remainder >>= NativeModuloChunkBits;
if (remaining == 0UL) if (remainder == 0UL)
{ {
break; break;
} }
current = ShiftLeftByNativeChunk(current, modulusLocal); multiplicand = ShiftLeftByNativeChunk(multiplicand, modulus);
} }
return result; return result;
@@ -936,7 +927,7 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong MulModMontgomery64(GpuUInt128 other, ulong modulus, ulong nPrime, ulong r2) public readonly ulong MulModMontgomery64(GpuUInt128 other, ulong modulus, ulong nPrime, ulong r2)
{ {
ulong modulusLocal = modulus; ulong modulusLocal = modulus;
ulong a = Low % modulusLocal; ulong a = Low % modulusLocal;
@@ -954,7 +945,7 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong MulModMontgomery64(ulong other, ulong modulus, ulong nPrime, ulong r2) public readonly ulong MulModMontgomery64(ulong other, ulong modulus, ulong nPrime, ulong r2)
{ {
ulong modulusLocal = modulus; ulong modulusLocal = modulus;
ulong a = Low % modulusLocal; ulong a = Low % modulusLocal;