Consolidate multiply helpers and guard GPU fallbacks
This commit is contained in:
1 parent
84ebbed98b
commit
6adac6b7a7
10 files changed
+227
-70
No files matched your search
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
@@ -13,6 +15,22 @@ internal enum GpuPow2ModStatus
|
||||
internal static class PrimeOrderGpuHeuristics
|
||||
{
|
||||
private static readonly ConcurrentDictionary<ulong, byte> OverflowedPrimes = new();
|
||||
private static int s_modulusBitLimit = PrimeOrderGpuCapability.Default.ModulusBits;
|
||||
private static int s_exponentBitLimit = PrimeOrderGpuCapability.Default.ExponentBits;
|
||||
|
||||
internal static void OverrideCapabilitiesForTesting(PrimeOrderGpuCapability capability)
|
||||
{
|
||||
Volatile.Write(ref s_modulusBitLimit, capability.ModulusBits);
|
||||
Volatile.Write(ref s_exponentBitLimit, capability.ExponentBits);
|
||||
}
|
||||
|
||||
internal static void ResetCapabilitiesForTesting()
|
||||
{
|
||||
OverrideCapabilitiesForTesting(PrimeOrderGpuCapability.Default);
|
||||
}
|
||||
|
||||
private static PrimeOrderGpuCapability Capabilities
|
||||
=> new(Volatile.Read(ref s_modulusBitLimit), Volatile.Read(ref s_exponentBitLimit));
|
||||
|
||||
internal static void MarkOverflow(ulong prime) => OverflowedPrimes[prime] = 0;
|
||||
|
||||
@@ -33,6 +51,19 @@ internal static class PrimeOrderGpuHeuristics
|
||||
return GpuPow2ModStatus.Overflow;
|
||||
}
|
||||
|
||||
PrimeOrderGpuCapability capability = Capabilities;
|
||||
|
||||
if (!SupportsPrime(prime, capability))
|
||||
{
|
||||
MarkOverflow(prime);
|
||||
return GpuPow2ModStatus.Overflow;
|
||||
}
|
||||
|
||||
if (!SupportsExponent(exponent, capability))
|
||||
{
|
||||
return GpuPow2ModStatus.Overflow;
|
||||
}
|
||||
|
||||
// TODO: Implement the GPU heuristic order calculator so this path mirrors PrimeOrderCalculator before enabling device execution.
|
||||
return GpuPow2ModStatus.Unavailable;
|
||||
}
|
||||
@@ -61,7 +92,49 @@ internal static class PrimeOrderGpuHeuristics
|
||||
return GpuPow2ModStatus.Overflow;
|
||||
}
|
||||
|
||||
PrimeOrderGpuCapability capability = Capabilities;
|
||||
|
||||
if (!SupportsPrime(prime, capability))
|
||||
{
|
||||
MarkOverflow(prime);
|
||||
return GpuPow2ModStatus.Overflow;
|
||||
}
|
||||
|
||||
for (int i = 0; i < exponents.Length; i++)
|
||||
{
|
||||
if (!SupportsExponent(exponents[i], capability))
|
||||
{
|
||||
return GpuPow2ModStatus.Overflow;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement the GPU heuristic order calculator so this path mirrors PrimeOrderCalculator before enabling device execution.
|
||||
return GpuPow2ModStatus.Unavailable;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool SupportsPrime(ulong prime, PrimeOrderGpuCapability capability)
|
||||
{
|
||||
return GetBitLength(prime) <= capability.ModulusBits;
|
||||
}
|
||||
|
||||
private static bool SupportsExponent(ulong exponent, PrimeOrderGpuCapability capability)
|
||||
{
|
||||
return GetBitLength(exponent) <= capability.ExponentBits;
|
||||
}
|
||||
|
||||
private static int GetBitLength(ulong value)
|
||||
{
|
||||
if (value == 0UL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 64 - BitOperations.LeadingZeroCount(value);
|
||||
}
|
||||
|
||||
internal readonly record struct PrimeOrderGpuCapability(int ModulusBits, int ExponentBits)
|
||||
{
|
||||
public static PrimeOrderGpuCapability Default => new(128, 64);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
internal static class HeuristicArithmetic
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong MultiplyShiftRight(ulong value, ulong multiplier, int shift)
|
||||
{
|
||||
if (shift < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(shift), "Shift must be non-negative.");
|
||||
}
|
||||
|
||||
UInt128 product = (UInt128)value * multiplier;
|
||||
return (ulong)(product >> shift);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong MultiplyShiftRightShiftFirst(ulong value, ulong multiplier, int shift)
|
||||
{
|
||||
if (shift < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(shift), "Shift must be non-negative.");
|
||||
}
|
||||
|
||||
if (shift == 0)
|
||||
{
|
||||
return MultiplyShiftRight(value, multiplier, 0);
|
||||
}
|
||||
|
||||
if (shift >= 64)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
ulong high = value >> shift;
|
||||
ulong mask = (1UL << shift) - 1UL;
|
||||
ulong low = value & mask;
|
||||
|
||||
UInt128 highContribution = (UInt128)high * multiplier;
|
||||
UInt128 lowContribution = (UInt128)low * multiplier;
|
||||
|
||||
UInt128 combined = highContribution + (lowContribution >> shift);
|
||||
return (ulong)combined;
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,47 @@ public static class ULongExtensions
|
||||
return order;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong MultiplyShiftRight(ulong value, ulong multiplier, int shift)
|
||||
{
|
||||
if (shift < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(shift), "Shift must be non-negative.");
|
||||
}
|
||||
|
||||
UInt128 product = (UInt128)value * multiplier;
|
||||
return (ulong)(product >> shift);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong MultiplyShiftRightShiftFirst(ulong value, ulong multiplier, int shift)
|
||||
{
|
||||
if (shift < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(shift), "Shift must be non-negative.");
|
||||
}
|
||||
|
||||
if (shift == 0)
|
||||
{
|
||||
return MultiplyShiftRight(value, multiplier, 0);
|
||||
}
|
||||
|
||||
if (shift >= 64)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
ulong high = value >> shift;
|
||||
ulong mask = (1UL << shift) - 1UL;
|
||||
ulong low = value & mask;
|
||||
|
||||
UInt128 highContribution = (UInt128)high * multiplier;
|
||||
UInt128 lowContribution = (UInt128)low * multiplier;
|
||||
|
||||
UInt128 combined = highContribution + (lowContribution >> shift);
|
||||
return (ulong)combined;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong FastDiv64(this ulong value, ulong divisor, ulong mul)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user