BitContradiction filtering changes to cut invalid divisors during checks.

This commit is contained in:
mlemanczyk committed 2026-01-31 11:48:56 +01:00
1 parent b31aab4d89
commit f1eaab8582
19 files changed
+3912 -2361

No files matched your search

-12
View File
@@ -5,18 +5,6 @@ using PerfectNumbers.Core.Gpu;
namespace EvenPerfectBitScanner;
internal enum ByDivisorKIncrementMode
{
Sequential,
Pow2Groups,
Predictive,
Percentile,
Additive,
TopDown,
BitContradiction,
BitTree,
}
internal readonly struct CliArguments
{
private const string DefaultCyclesPath = "divisor_cycles.bin";
+73 -76
View File
@@ -42,7 +42,7 @@ namespace EvenPerfectBitScanner;
internal static class Program
{
// private static ThreadLocal<PrimeTester> PrimeTesters = null!;
private static ThreadLocal<object>? MersenneTesters;
private static ThreadLocal<object>? MersenneTesters;
private const ulong InitialP = PerfectNumberConstants.BiggestKnownEvenPerfectP;
private static MersenneNumberDivisorGpuTester? _divisorTester;
private static UInt128 _divisor;
@@ -111,37 +111,31 @@ private static ThreadLocal<object>? MersenneTesters;
return;
}
Console.WriteLine("Initializing");
int gpuPrimeThreads = Math.Max(1, _cliArguments.GpuPrimeThreads * PerfectNumberConstants.ThreadsByAccelerator);
int threadCount = Math.Max(1, _cliArguments.ThreadCount);
Console.WriteLine("Initializing");
EnvironmentConfiguration.MinK = _cliArguments.MinK;
EnvironmentConfiguration.GpuBatchSize = Math.Max(1, _cliArguments.ScanBatchSize);
EnvironmentConfiguration.GpuRatio = _cliArguments.GpuRatio;
EnvironmentConfiguration.MersenneDevice = _cliArguments.MersenneDevice;
ComputationDevice orderDevice = _cliArguments.OrderDevice;
if (_cliArguments.MersenneDevice == ComputationDevice.Gpu &&
_cliArguments.ByDivisorKIncrementMode == ByDivisorKIncrementMode.BitContradiction)
{
orderDevice = ComputationDevice.Gpu;
}
EnvironmentConfiguration.ByDivisorKIncrementMode = _cliArguments.ByDivisorKIncrementMode;
EnvironmentConfiguration.ByDivisorSpecialRange = _cliArguments.ByDivisorSpecialRange;
EnvironmentConfiguration.CalculationMethod =
_cliArguments.UseByDivisor ? CalculationMethod.ByDivisor
: _cliArguments.UseDivisor ? CalculationMethod.Divisor
: _cliArguments.UseResidue ? CalculationMethod.Residue
: _cliArguments.UseLucas ? CalculationMethod.LucasLehmer
: _cliArguments.KernelType == GpuKernelType.Pow2Mod ? CalculationMethod.Pow2Mod
: CalculationMethod.Incremental;
EnvironmentConfiguration.GpuBatchSize = Math.Max(1, _cliArguments.ScanBatchSize);
EnvironmentConfiguration.GpuPrimeThreads = Math.Max(1, _cliArguments.GpuPrimeThreads * PerfectNumberConstants.ThreadsByAccelerator);
EnvironmentConfiguration.GpuRatio = _cliArguments.GpuRatio;
EnvironmentConfiguration.MersenneDevice = _cliArguments.MersenneDevice;
EnvironmentConfiguration.MinK = _cliArguments.MinK;
EnvironmentConfiguration.OrderDevice = _cliArguments.OrderDevice;
EnvironmentConfiguration.ThreadCount = Math.Max(1, _cliArguments.ThreadCount);
EnvironmentConfiguration.OrderDevice = orderDevice;
EnvironmentConfiguration.UsePow2GroupDivisors = _cliArguments.ByDivisorKIncrementMode == ByDivisorKIncrementMode.Pow2Groups;
EnvironmentConfiguration.ByDivisorSpecialRange = _cliArguments.ByDivisorSpecialRange;
EnvironmentConfiguration.Initialize();
bool useGpu = orderDevice == ComputationDevice.Hybrid || orderDevice == ComputationDevice.Gpu ||
_cliArguments.MersenneDevice == ComputationDevice.Hybrid || _cliArguments.MersenneDevice == ComputationDevice.Gpu;
EnvironmentConfiguration.RollingAccelerators = useGpu
? Math.Min(Math.Min(PerfectNumberConstants.RollingAccelerators, gpuPrimeThreads), threadCount)
: 0;
EnvironmentConfiguration.Initialize();
PrimeOrderCalculatorAccelerator.SkipOrderTablesAndKernels = _cliArguments.MersenneDevice == ComputationDevice.Gpu &&
_cliArguments.UseByDivisor &&
_cliArguments.ByDivisorKIncrementMode == ByDivisorKIncrementMode.BitContradiction;
PrimeOrderCalculatorAccelerator.SkipOrderTablesAndKernels =
EnvironmentConfiguration.MersenneDevice == ComputationDevice.Gpu &&
EnvironmentConfiguration.CalculationMethod == CalculationMethod.ByDivisor &&
EnvironmentConfiguration.ByDivisorKIncrementMode == ByDivisorKIncrementMode.BitContradiction;
// NttGpuMath.GpuTransformBackend = _cliArguments.NttBackend;
// NttGpuMath.ReductionMode = _cliArguments.ModReductionMode;
@@ -151,6 +145,9 @@ private static ThreadLocal<object>? MersenneTesters;
ulong remainder = currentP % 6UL;
bool startPrimeProvided = _cliArguments.StartPrimeProvided;
int gpuPrimeBatch = Math.Max(1, _cliArguments.GpuPrimeBatch);
bool useGpu = EnvironmentConfiguration.UseGpu;
int threadCount = EnvironmentConfiguration.ThreadCount;
int gpuPrimeThreads = EnvironmentConfiguration.GpuPrimeThreads;
// int gpuPrimeThreads = SharedGpuContext.Device.MaxNumThreads;
Console.WriteLine("Setting up parameters");
UnboundedTaskScheduler.ConfigureThreadCount(threadCount);
@@ -606,16 +603,16 @@ private static ThreadLocal<object>? MersenneTesters;
EnvironmentConfiguration.ByDivisorPow2Minus1Repository ??= Pow2Minus1StateRepository.Open(Path.Combine(checksDir, "pow2minus1.faster"), pow2MinusLmdb, checksDir);
EnvironmentConfiguration.BitContradictionStateRepository ??= BitContradictionStateRepository.Open(checksDir);
if (kIncrementMode == ByDivisorKIncrementMode.Pow2Groups)
{
EnvironmentConfiguration.ByDivisorSpecialStateRepository = KStateRepository.Open(checksDir, "pow2groups.special.kstate.faster", Path.Combine(checksDir, "pow2groups.special.kstate.lmdb"), checksDir);
EnvironmentConfiguration.ByDivisorGroupsStateRepository = KStateRepository.Open(checksDir, "pow2groups.groups.kstate.faster", Path.Combine(checksDir, "pow2groups.groups.kstate.lmdb"), checksDir);
}
else
{
EnvironmentConfiguration.ByDivisorSpecialStateRepository = null;
EnvironmentConfiguration.ByDivisorGroupsStateRepository = null;
}
if (kIncrementMode == ByDivisorKIncrementMode.Pow2Groups)
{
EnvironmentConfiguration.ByDivisorSpecialStateRepository = KStateRepository.Open(checksDir, "pow2groups.special.kstate.faster", Path.Combine(checksDir, "pow2groups.special.kstate.lmdb"), checksDir);
EnvironmentConfiguration.ByDivisorGroupsStateRepository = KStateRepository.Open(checksDir, "pow2groups.groups.kstate.faster", Path.Combine(checksDir, "pow2groups.groups.kstate.lmdb"), checksDir);
}
else
{
EnvironmentConfiguration.ByDivisorSpecialStateRepository = null;
EnvironmentConfiguration.ByDivisorGroupsStateRepository = null;
}
}
bool useFilter = !testMode && !string.IsNullOrEmpty(filterFile);
@@ -1409,48 +1406,48 @@ private static ThreadLocal<object>? MersenneTesters;
// Windowed pow2mod kernel handles by-divisor scans.
ByDivisorKIncrementMode mode = _cliArguments.ByDivisorKIncrementMode;
if (EnvironmentConfiguration.UseCpuOrder)
{
return mode switch
{
ByDivisorKIncrementMode.Pow2Groups => _byDivisorCpuOrderTesterPow2.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Predictive => _byDivisorCpuOrderTesterPredictive.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Percentile => _byDivisorCpuOrderTesterPercentile.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Additive => _byDivisorCpuOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.TopDown => _byDivisorCpuOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitContradiction => _byDivisorCpuOrderTesterBitContradiction.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitTree => _byDivisorCpuOrderTesterBitTree.IsPrime(p, out detailedCheck, out _),
_ => _byDivisorCpuOrderTester.IsPrime(p, out detailedCheck, out _),
};
}
if (EnvironmentConfiguration.UseHybridOrder)
{
return mode switch
{
ByDivisorKIncrementMode.Pow2Groups => _byDivisorHybridOrderTesterPow2.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Predictive => _byDivisorHybridOrderTesterPredictive.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Percentile => _byDivisorHybridOrderTesterPercentile.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Additive => _byDivisorHybridOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.TopDown => _byDivisorHybridOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitContradiction => _byDivisorHybridOrderTesterBitContradiction.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitTree => _byDivisorHybridOrderTesterBitTree.IsPrime(p, out detailedCheck, out _),
_ => _byDivisorHybridOrderTester.IsPrime(p, out detailedCheck, out _),
};
}
{
return mode switch
{
ByDivisorKIncrementMode.Pow2Groups => _byDivisorGpuOrderTesterPow2.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Predictive => _byDivisorGpuOrderTesterPredictive.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Percentile => _byDivisorGpuOrderTesterPercentile.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Additive => _byDivisorGpuOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.TopDown => _byDivisorGpuOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitContradiction => _byDivisorGpuOrderTesterBitContradiction.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitTree => _byDivisorGpuOrderTesterBitTree.IsPrime(p, out detailedCheck, out _),
_ => _byDivisorGpuOrderTester.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Pow2Groups => _byDivisorCpuOrderTesterPow2.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Predictive => _byDivisorCpuOrderTesterPredictive.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Percentile => _byDivisorCpuOrderTesterPercentile.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Additive => _byDivisorCpuOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.TopDown => _byDivisorCpuOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitContradiction => _byDivisorCpuOrderTesterBitContradiction.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitTree => _byDivisorCpuOrderTesterBitTree.IsPrime(p, out detailedCheck, out _),
_ => _byDivisorCpuOrderTester.IsPrime(p, out detailedCheck, out _),
};
}
if (EnvironmentConfiguration.UseHybridOrder)
{
return mode switch
{
ByDivisorKIncrementMode.Pow2Groups => _byDivisorHybridOrderTesterPow2.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Predictive => _byDivisorHybridOrderTesterPredictive.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Percentile => _byDivisorHybridOrderTesterPercentile.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Additive => _byDivisorHybridOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.TopDown => _byDivisorHybridOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitContradiction => _byDivisorHybridOrderTesterBitContradiction.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitTree => _byDivisorHybridOrderTesterBitTree.IsPrime(p, out detailedCheck, out _),
_ => _byDivisorHybridOrderTester.IsPrime(p, out detailedCheck, out _),
};
}
return mode switch
{
ByDivisorKIncrementMode.Pow2Groups => _byDivisorGpuOrderTesterPow2.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Predictive => _byDivisorGpuOrderTesterPredictive.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Percentile => _byDivisorGpuOrderTesterPercentile.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.Additive => _byDivisorGpuOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.TopDown => _byDivisorGpuOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitContradiction => _byDivisorGpuOrderTesterBitContradiction.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitTree => _byDivisorGpuOrderTesterBitTree.IsPrime(p, out detailedCheck, out _),
_ => _byDivisorGpuOrderTester.IsPrime(p, out detailedCheck, out _),
};
}
detailedCheck = IsMersennePrime(gpu, p);
return detailedCheck;
}
File diff suppressed because it is too large. Load diff
@@ -9,13 +9,19 @@ public readonly struct ColumnBounds(int forcedOnes, int possibleOnes)
public readonly int PossibleOnes = possibleOnes;
}
public readonly struct CarryRange(long min, long max)
public readonly struct CarryRange(long min, long max, byte mod4Mask = byte.MinValue)
{
public readonly long Min = min;
public readonly long Max = max;
public readonly byte Mod4Mask = mod4Mask;
public static CarryRange Single(long value) => new(value, value);
public static readonly CarryRange Zero = Single(0);
public static CarryRange Single(long value) => new(value, value, byte.MinValue);
public static CarryRange Single(long value, byte mod4Mask) => new(value, value, mod4Mask);
public static readonly CarryRange Zero = Single(0, 0);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsEmpty() => Min > Max || Mod4Mask == 0;
}
public enum ContradictionReason
@@ -0,0 +1,16 @@
namespace PerfectNumbers.Core;
#pragma warning disable CA2211 // The fields are settable on purpose for the best performance
public enum ByDivisorKIncrementMode
{
Sequential,
Pow2Groups,
Predictive,
Percentile,
Additive,
TopDown,
BitContradiction,
BitTree,
}
#pragma warning restore CA2211 // Non-constant fields should not be visible
+7
View File
@@ -0,0 +1,7 @@
namespace PerfectNumbers.Core;
public enum CacheStatus
{
Disabled,
Enabled,
}
+11
View File
@@ -0,0 +1,11 @@
namespace PerfectNumbers.Core;
public enum CalculationMethod
{
ByDivisor,
Divisor,
Incremental,
LucasLehmer,
Pow2Mod,
Residue,
}
@@ -63,7 +63,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
private static readonly ConcurrentDictionary<ulong, byte> _checkedPow2MinusOne = new();
private ulong _currentPrime;
#if DEVICE_GPU
#if DEVICE_GPU && DivisorSet_BitContradiction
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
private bool CheckDivisorsBitContradictionGpu(
ulong prime,
@@ -78,6 +78,17 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
foundDivisor = BigInteger.Zero;
BigInteger currentK = minK;
#if OVERRIDE_MIN_K
// Regression test convenience: ensure we actually scan the range that contains the known divisor for p=138000001.
if (currentK > 1)
{
Console.WriteLine($"[GPU OVERRIDE] p={prime} forcing minK from {currentK} to 1 for known-divisor regression test.");
currentK = BigInteger.One;
}
#endif
const int TargetBatchIndex = 273;
bool hasLimit = !allowedMax.IsZero;
BigInteger maxK = hasLimit ? (allowedMax - BigInteger.One) / step : BigInteger.Zero;
@@ -100,8 +111,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
BigInteger batchSizeBig = batchSize;
int wordCount = BitContradictionKernels.MaxQWordCount;
int inputCapacity = maxThreadCount * wordCount;
gpu.EnsureCapacity(PerfectNumberConstants.DefaultFactorsBuffer, inputCapacity);
gpu.EnsurePartialFactorCapacity(inputCapacity);
var buffers = gpu.EnsureBitContradictionCapacity(inputCapacity, inputCapacity, maxThreadCount);
var stream = AcceleratorStreamPool.Rent(gpu.AcceleratorIndex);
var kernels = GpuKernelPool.GetOrAddKernels(gpu.AcceleratorIndex, stream, KernelType.BitContradictionScan);
@@ -109,13 +119,158 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
int[] found = new int[maxThreadCount];
ulong[] batchIndexWords = new ulong[inputCapacity];
using MemoryBuffer1D<int, Stride1D.Dense> delta8Keys = gpu.Accelerator.Allocate1D<int>(maxThreadCount * BitContradictionKernels.DeltaCacheSlots);
using MemoryBuffer1D<int, Stride1D.Dense> delta8Cache = gpu.Accelerator.Allocate1D<int>(maxThreadCount * BitContradictionKernels.DeltaCacheSlots * 4194304);
#if DETAILED_LOG
int debugWordsPerSlot = BitContradictionKernels.DebugWordCountPerSlot;
using MemoryBuffer1D<ulong, Stride1D.Dense> debugBuffer = gpu.Accelerator.Allocate1D<ulong>(maxThreadCount * debugWordsPerSlot);
ArrayView1D<ulong, Stride1D.Dense> debugView = debugBuffer.View;
#endif
ArrayView1D<int, Stride1D.Dense> delta8KeysView = buffers.Delta8CacheKeysView;
ArrayView1D<int, Stride1D.Dense> delta8CacheView = buffers.Delta8CacheValuesView;
Span<int> temp = stackalloc int[(int)delta8KeysView.Length];
temp.Fill(int.MinValue);
delta8KeysView.CopyFromCPU(stream, temp);
#if INVERSION_VALIDATION
///
/// INVERSION VALIDATION
///
{
// --- Inputs (known test case)
ulong p = 138000001UL;
ulong k = 4383UL;
// --- ILGPU setup (minimal)
var accelerator = gpu.Accelerator;
// --- Compile kernel (Extent=1)
// Kernel signature must be: (Index1D, ulong p, ulong k, ArrayView1D<ulong, Stride1D.Dense> outBuf)
var kernel2 = accelerator.LoadAutoGroupedStreamKernel<
Index1D,
ulong,
ulong,
ArrayView1D<ulong, Stride1D.Dense>
>(BitContradictionKernels.BitContradictionKernelExportInverse1024);
// --- Output buffer (64 ulongs is enough: 16 qPrefix + 16 inv + 16 aLow + meta)
using var outBufGpu = accelerator.Allocate1D<ulong>(64);
var outCpu = new ulong[64];
// Optional: clear buffer
for (int i = 0; i < outCpu.Length; i++) outCpu[i] = 0;
outBufGpu.View.CopyFromCPU(stream, outCpu);
// --- Run kernel
kernel2(1, p, k, outBufGpu.View);
stream.Synchronize();
// --- Copy back
outBufGpu.View.CopyToCPU(stream, outCpu);
stream.Synchronize();
// --- Parse meta
ulong pOut = outCpu[48];
ulong kOut = outCpu[49];
ulong qOut = outCpu[50];
ulong marker = outCpu[51];
Console.WriteLine($"[GPU EXPORT] pOut={pOut} kOut={kOut} qOut={qOut} marker=0x{marker:X}");
// --- Helpers: build BigInteger from 16x ulong limbs (little-endian)
BigInteger FromU64Le(int startIndex)
{
// +1 byte to force positive sign for BigInteger ctor
byte[] bytes = new byte[16 * 8 + 1];
for (int i = 0; i < 16; i++)
{
ulong w = outCpu[startIndex + i];
int o = i * 8;
bytes[o + 0] = (byte)(w);
bytes[o + 1] = (byte)(w >> 8);
bytes[o + 2] = (byte)(w >> 16);
bytes[o + 3] = (byte)(w >> 24);
bytes[o + 4] = (byte)(w >> 32);
bytes[o + 5] = (byte)(w >> 40);
bytes[o + 6] = (byte)(w >> 48);
bytes[o + 7] = (byte)(w >> 56);
}
return new BigInteger(bytes);
}
var qPrefix = FromU64Le(0);
var inv = FromU64Le(16);
var aLow = FromU64Le(32);
var mask = (BigInteger.One << 1024) - 1;
// --- Validate inverse: (qPrefix * inv) mod 2^1024 == 1
var prod = (qPrefix * inv) & mask;
bool invOk = prod == BigInteger.One;
// --- Validate aLow: aLow == (-inv) mod 2^1024
var expectedALow = (mask + BigInteger.One - (inv & mask)) & mask;
bool aLowOk = ((aLow & mask) == expectedALow);
// --- Print concise diagnostics
Console.WriteLine($"[CPU VALIDATION] invOk={invOk} aLowOk={aLowOk}");
// Print low 64 bits (quick sanity)
ulong prodLow64 = (ulong)(prod & ((BigInteger.One << 64) - 1));
ulong invLow64 = (ulong)(inv & ((BigInteger.One << 64) - 1));
ulong aLowLow64 = (ulong)(aLow & ((BigInteger.One << 64) - 1));
Console.WriteLine($"[LOW64] qPrefix=0x{(ulong)(qPrefix & ((System.Numerics.BigInteger.One<<64)-1)):X}");
Console.WriteLine($"[LOW64] inv=0x{invLow64:X} aLow=0x{aLowLow64:X} prod=0x{prodLow64:X}");
// Optional: hard fail for CI/regression
if (!invOk || !aLowOk)
throw new System.InvalidOperationException($"Inverse export validation failed: invOk={invOk}, aLowOk={aLowOk}");
}
#endif
#if SELF_TEST
///
/// SELF-TEST
///
{
ulong p = 138000001UL;
ulong k = 4383UL;
var accelerator = gpu.Accelerator;
Console.WriteLine($"Compiling kernel for {prime}");
var kernel3 = accelerator.LoadAutoGroupedStreamKernel<
Index1D,
ulong,
ulong,
int,
ArrayView1D<ulong, Stride1D.Dense>
>(BitContradictionKernels.BitContradictionKernelDebugDpSingleQ);
var launcher3 = KernelUtil.GetKernel(kernel3).CreateLauncherDelegate<
Action<AcceleratorStream, Index1D, ulong, ulong, int, ArrayView1D<ulong, Stride1D.Dense>>>();
Console.WriteLine($"Preparing buffers for {prime}");
using var outGpu = accelerator.Allocate1D<ulong>(128);
var outCpu = new ulong[128];
Array.Clear(outCpu, 0, outCpu.Length);
outGpu.View.CopyFromCPU(stream, outCpu);
// First run: disable batching (scalar DP)
Console.WriteLine($"Launching GPU kernel for {prime}");
launcher3(stream, 1, p, k, 1, outGpu.View);
outGpu.View.CopyToCPU(stream, outCpu);
stream.Synchronize();
Console.WriteLine($"[DP DEBUG] marker=0x{outCpu[0]:X} p={outCpu[1]} k={outCpu[2]} q={outCpu[3]} qBitLen={outCpu[4]} offsetCount={outCpu[6]} ok={outCpu[9]} carryMin={outCpu[10]} carryMax={outCpu[11]}");
// Print first 10 step records
Console.WriteLine($"[DP DEBUG] marker=0x{outCpu[0]:X} p={outCpu[1]} k={outCpu[2]} q={outCpu[3]} qBitLen={outCpu[4]} offsetCount={outCpu[6]} ok={outCpu[9]} carryMin={outCpu[10]} carryMax={outCpu[11]} disableBatching={outCpu[12]}");
Console.WriteLine($"[DP EXTRA] maxAllowedA={outCpu[14]} maxFixed={outCpu[15]} windowSize={outCpu[16]} aWordCount={outCpu[17]}");
Console.WriteLine($"[DP FIRST] firstForced={unchecked((long)outCpu[18])} firstRemaining={unchecked((long)outCpu[19])} firstQWordsEff={outCpu[20]} firstQLastEff=0x{outCpu[21]:X} firstCarryMin={unchecked((long)outCpu[22])} firstCarryMax={unchecked((long)outCpu[23])}");
Console.WriteLine($"[DP SNAP] aLow0=0x{outCpu[24]:X} knownOne0=0x{outCpu[25]:X} knownZero0=0x{outCpu[26]:X} aOneWin0=0x{outCpu[27]:X} aZeroWin0=0x{outCpu[28]:X} qMask0=0x{outCpu[29]:X} lastMask=0x{outCpu[30]:X} off0={outCpu[31]} offLast={outCpu[32]}");
if (outCpu[0] != 0xD00DF00DUL)
throw new Exception("DP debug kernel did not run as expected.");
}
#endif
BigInteger currentBatch = (currentK - BigInteger.One) / batchSizeBig;
currentK = (currentBatch * batchSizeBig) + BigInteger.One;
@@ -123,7 +278,6 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
while (true)
{
if (hasLimit && currentBatch > maxBatch)
{
processedAll = true;
@@ -137,6 +291,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
break;
}
Console.WriteLine($"Preparing data for{prime}, batch {currentBatch}");
int threadCount = maxThreadCount;
if (remainingBatches < threadCount)
{
@@ -162,15 +317,28 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
WriteBigIntegerToWords(batchIndex, batchIndexWords.AsSpan(t * wordCount, wordCount));
}
gpu.InputView.CopyFromCPU(stream, batchIndexWords.AsSpan(0, threadCount * wordCount));
kernel(stream, threadCount, prime, gpu.InputView, countQ, gpu.OutputIntView,
#if DETAILED_LOG
debugView,
#endif
delta8Keys.View, delta8Cache.View);
Console.WriteLine($"Copying data to GPU for {prime}, batch {currentBatch}");
buffers.BatchIndexWordsView.CopyFromCPU(stream, batchIndexWords.AsSpan(0, threadCount * wordCount));
// #if DETAILED_LOG
Console.WriteLine($"Launching GPU kernel for {prime}, batch {currentBatch}");
// #endif
kernel(stream, 1, prime, buffers.BatchIndexWordsView, countQ, buffers.FoundQWordsView,
// kernel(stream, threadCount, prime, buffers.BatchIndexWords, countQ, buffers.FoundQWords,
delta8KeysView, delta8CacheView);
buffers.FoundQWordsView.CopyToCPU(stream, found.AsSpan(0, threadCount));
stream.Synchronize();
gpu.OutputIntView.CopyToCPU(stream, found.AsSpan(0, threadCount));
_consolePrintOut++;
if (_consolePrintOut >= PerfectNumberConstants.ConsoleInterval)
{
Console.WriteLine($"GPU kernel for {prime} complete, batch {currentBatch}");
Console.WriteLine($"[GPU FOUND RAW] batch={currentBatch} found[0]={found[0]}");
_consolePrintOut = 0;
}
#if DETAILED_LOG || SELF_TEST
#endif
// TODO: This loop is incorrect. We have a loop over the threads outside from here
for (int i = 0; i < threadCount; i++)
{
int foundIndex = found[i];
@@ -217,49 +385,92 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
// if (!dividesExact)
// {
// throw new InvalidOperationException($"BitContradiction CPU/GPU false positive for p={prime}, q={candidateQ}, k={candidateK}, batch={batchIndex}");
#if DETAILED_LOG
#if DETAILED_LOG
if (prime == 138000001UL)
// --- Fast self-check for known divisor: run scan kernel with ONE thread and SMALL countQ.
// This avoids waiting for other threads that would scan huge ranges and never hit the target.
{
ulong[] debugWords = new ulong[debugWordsPerSlot];
debugView.SubView(i * debugWordsPerSlot, debugWordsPerSlot).CopyToCPU(stream, debugWords);
BigInteger gpuQ = ReadBigIntegerFromWords(debugWords.AsSpan(0, 16));
BigInteger gpuInv = ReadBigIntegerFromWords(debugWords.AsSpan(16, 16));
BigInteger gpuALow = ReadBigIntegerFromWords(debugWords.AsSpan(32, 16));
ulong gpuQBits = debugWords[48];
ulong gpuQWordCount = debugWords[49];
ulong gpuQMask0 = debugWords[50];
ulong gpuQMask1 = debugWords[51];
ulong gpuKnownOne0 = debugWords[52];
ulong gpuKnownZero0 = debugWords[53];
ulong gpuAOneWin0 = debugWords[54];
ulong gpuAZeroWin0 = debugWords[55];
long gpuCarryMin = unchecked((long)debugWords[56]);
long gpuCarryMax = unchecked((long)debugWords[57]);
long gpuMaxAllowedA = unchecked((long)debugWords[58]);
long gpuMaxFixed = unchecked((long)debugWords[59]);
long gpuWindowSize = unchecked((long)debugWords[60]);
long gpuAWordCount = unchecked((long)debugWords[61]);
long gpuMaxKnownA = unchecked((long)debugWords[62]);
long gpuMaxFixedPrefilter = unchecked((long)debugWords[63]);
ulong preKnownOne0 = debugWords[64];
ulong preKnownZero0 = debugWords[65];
ulong preAOneWin0 = debugWords[66];
ulong preAZeroWin0 = debugWords[67];
long preCarryMin = unchecked((long)debugWords[68]);
long preCarryMax = unchecked((long)debugWords[69]);
long preSegmentBase = unchecked((long)debugWords[70]);
long preWindowColumn = unchecked((long)debugWords[71]);
long gpuOffsetCount = unchecked((long)debugWords[72]);
long gpuMaxOffset = unchecked((long)debugWords[73]);
long gpuFirstForced = unchecked((long)debugWords[74]);
long gpuFirstRemaining = unchecked((long)debugWords[75]);
long gpuFirstQWordsEff = unchecked((long)debugWords[76]);
ulong gpuFirstQLastEff = debugWords[77];
long gpuFirstCarryMin = unchecked((long)debugWords[78]);
long gpuFirstCarryMax = unchecked((long)debugWords[79]);
throw new InvalidOperationException($"BitContradiction GPU debug for p={prime}, q={candidateQ}, k={candidateK}, qBits={gpuQBits}, qWordCount={gpuQWordCount}, qMask0=0x{gpuQMask0:X16}, qMask1=0x{gpuQMask1:X16}, knownOne0=0x{gpuKnownOne0:X16}, knownZero0=0x{gpuKnownZero0:X16}, aOneWin0=0x{gpuAOneWin0:X16}, aZeroWin0=0x{gpuAZeroWin0:X16}, carryMin={gpuCarryMin}, carryMax={gpuCarryMax}, maxAllowedA={gpuMaxAllowedA}, maxFixed={gpuMaxFixed}, windowSize={gpuWindowSize}, aWordCount={gpuAWordCount}, maxKnownA={gpuMaxKnownA}, maxFixedPrefilter={gpuMaxFixedPrefilter}, preKnownOne0=0x{preKnownOne0:X16}, preKnownZero0=0x{preKnownZero0:X16}, preAOneWin0=0x{preAOneWin0:X16}, preAZeroWin0=0x{preAZeroWin0:X16}, preCarryMin={preCarryMin}, preCarryMax={preCarryMax}, preSegmentBase={preSegmentBase}, preWindowColumn={preWindowColumn}, offsetCount={gpuOffsetCount}, maxOffset={gpuMaxOffset}, firstForced={gpuFirstForced}, firstRemaining={gpuFirstRemaining}, firstQWordsEff={gpuFirstQWordsEff}, firstQLastEff=0x{gpuFirstQLastEff:X16}, firstCarryMin={gpuFirstCarryMin}, firstCarryMax={gpuFirstCarryMax}, gpuQ={gpuQ}, gpuInv={gpuInv}, gpuALow={gpuALow}");
const int expectedFoundIndex = 4382; // k=4383 => index=k-1 in batch 0
countQ = 6000; // must be > 4382, keep small to finish quickly
Array.Clear(found, 0, found.Length);
Array.Clear(batchIndexWords, 0, batchIndexWords.Length);
// batchIndex = 0
WriteBigIntegerToWords(BigInteger.Zero, batchIndexWords.AsSpan(0, wordCount));
buffers.BatchIndexWords.CopyFromCPU(stream, batchIndexWords.AsSpan(0, threadCount * wordCount));
Console.WriteLine($"[GPU SELFTEST] launching scan kernel (threads={threadCount}, countQ={countQ})");
kernel(
stream,
threadCount,
prime,
buffers.BatchIndexWords,
countQ,
buffers.FoundQWords,
#if DETAILED_LOG
debugView,
#endif
delta8Keys.View,
delta8Cache.View);
stream.Synchronize();
buffers.FoundQWords.CopyToCPU(stream, found.AsSpan(0, threadCount));
stream.Synchronize();
Console.WriteLine($"[GPU SELFTEST] found[0]={found[0]} (expected {expectedFoundIndex})");
if (found[0] != expectedFoundIndex)
{
throw new InvalidOperationException($"[GPU SELFTEST FAILED] got={found[0]} expected={expectedFoundIndex}");
}
// Self-test succeeded => stop here (do not start full scan which may hit TDR during debugging).
processedAll = false;
foundDivisor = BigInteger.Zero;
AcceleratorStreamPool.Return(gpu.AcceleratorIndex, stream);
return false;
}
#endif
// {
// ulong[] debugWords = new ulong[debugWordsPerSlot];
// debugView.SubView(i * debugWordsPerSlot, debugWordsPerSlot).CopyToCPU(stream, debugWords);
// BigInteger gpuQ = ReadBigIntegerFromWords(debugWords.AsSpan(0, 16));
// BigInteger gpuInv = ReadBigIntegerFromWords(debugWords.AsSpan(16, 16));
// BigInteger gpuALow = ReadBigIntegerFromWords(debugWords.AsSpan(32, 16));
// ulong gpuQBits = debugWords[48];
// ulong gpuQWordCount = debugWords[49];
// ulong gpuQMask0 = debugWords[50];
// ulong gpuQMask1 = debugWords[51];
// ulong gpuKnownOne0 = debugWords[52];
// ulong gpuKnownZero0 = debugWords[53];
// ulong gpuAOneWin0 = debugWords[54];
// ulong gpuAZeroWin0 = debugWords[55];
// long gpuCarryMin = unchecked((long)debugWords[56]);
// long gpuCarryMax = unchecked((long)debugWords[57]);
// long gpuMaxAllowedA = unchecked((long)debugWords[58]);
// long gpuMaxFixed = unchecked((long)debugWords[59]);
// long gpuWindowSize = unchecked((long)debugWords[60]);
// long gpuAWordCount = unchecked((long)debugWords[61]);
// long gpuMaxKnownA = unchecked((long)debugWords[62]);
// long gpuMaxFixedPrefilter = unchecked((long)debugWords[63]);
// ulong preKnownOne0 = debugWords[64];
// ulong preKnownZero0 = debugWords[65];
// ulong preAOneWin0 = debugWords[66];
// ulong preAZeroWin0 = debugWords[67];
// long preCarryMin = unchecked((long)debugWords[68]);
// long preCarryMax = unchecked((long)debugWords[69]);
// long preSegmentBase = unchecked((long)debugWords[70]);
// long preWindowColumn = unchecked((long)debugWords[71]);
// long gpuOffsetCount = unchecked((long)debugWords[72]);
// long gpuMaxOffset = unchecked((long)debugWords[73]);
// long gpuFirstForced = unchecked((long)debugWords[74]);
// long gpuFirstRemaining = unchecked((long)debugWords[75]);
// long gpuFirstQWordsEff = unchecked((long)debugWords[76]);
// ulong gpuFirstQLastEff = debugWords[77];
// long gpuFirstCarryMin = unchecked((long)debugWords[78]);
// long gpuFirstCarryMax = unchecked((long)debugWords[79]);
// throw new InvalidOperationException($"BitContradiction GPU debug for p={prime}, q={candidateQ}, k={candidateK}, qBits={gpuQBits}, qWordCount={gpuQWordCount}, qMask0=0x{gpuQMask0:X16}, qMask1=0x{gpuQMask1:X16}, knownOne0=0x{gpuKnownOne0:X16}, knownZero0=0x{gpuKnownZero0:X16}, aOneWin0=0x{gpuAOneWin0:X16}, aZeroWin0=0x{gpuAZeroWin0:X16}, carryMin={gpuCarryMin}, carryMax={gpuCarryMax}, maxAllowedA={gpuMaxAllowedA}, maxFixed={gpuMaxFixed}, windowSize={gpuWindowSize}, aWordCount={gpuAWordCount}, maxKnownA={gpuMaxKnownA}, maxFixedPrefilter={gpuMaxFixedPrefilter}, preKnownOne0=0x{preKnownOne0:X16}, preKnownZero0=0x{preKnownZero0:X16}, preAOneWin0=0x{preAOneWin0:X16}, preAZeroWin0=0x{preAZeroWin0:X16}, preCarryMin={preCarryMin}, preCarryMax={preCarryMax}, preSegmentBase={preSegmentBase}, preWindowColumn={preWindowColumn}, offsetCount={gpuOffsetCount}, maxOffset={gpuMaxOffset}, firstForced={gpuFirstForced}, firstRemaining={gpuFirstRemaining}, firstQWordsEff={gpuFirstQWordsEff}, firstQLastEff=0x{gpuFirstQLastEff:X16}, firstCarryMin={gpuFirstCarryMin}, firstCarryMax={gpuFirstCarryMax}, gpuQ={gpuQ}, gpuInv={gpuInv}, gpuALow={gpuALow}");
// }
#endif
foundDivisor = candidateQ;
AcceleratorStreamPool.Return(gpu.AcceleratorIndex, stream);
@@ -267,6 +478,12 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
}
}
if (currentBatch == TargetBatchIndex)
{
Console.WriteLine($"No divisor found up to batch index = {currentBatch}, with the target batch index = {TargetBatchIndex}");
Environment.Exit(1);
}
ContinueOuter:
RecordState(currentK);
if (hasLimit && currentK > maxK)
@@ -3039,7 +3256,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
}
ContinueOuter:
RecordState(currentK);
RecordState(currentK);
// if (divisorCycle == prime && !IsMersenneValue(prime, candidate))
if (divisorCycle == prime)
{
@@ -3160,7 +3377,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
{
// Stopwatch sw = Stopwatch.StartNew();
ContinueOuter:
RecordState(currentK);
RecordState(currentK);
ulong powMod;
bool isPow2 = IsPowerOfTwo(currentK);
#if DETAILED_LOG
@@ -3262,7 +3479,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
if (remainder8 == 1 || remainder8 == 7)
{
ContinueOuter:
RecordState(currentK);
RecordState(currentK);
BigInteger powResult = BigInteger.ModPow(BigIntegerNumbers.Two, primeBig, divisor);
if (powResult.IsOne)
{
@@ -3962,7 +4179,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
if (remainder8 == 1 || remainder8 == 7)
{
ContinueOuter:
RecordState(currentK);
RecordState(currentK);
BigInteger powResult = BigInteger.ModPow(BigIntegerNumbers.Two, primeBig, divisor);
// if (powResult.IsOne && !IsMersenneValue(prime, divisor))
if (powResult.IsOne)
@@ -4317,7 +4534,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
}
ContinueOuter:
RecordState(currentK);
RecordState(currentK);
if (divisorCycle == prime && !IsMersenneValue(prime, divisor))
{
foundDivisor = divisor;
@@ -4891,6 +5108,9 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
while (true)
{
int remainder8 = (stepMod8 * kRem8 + 1) & 7;
if (prime == 138000001UL && divisor == 1209708008767)
Console.WriteLine("[HIT] enumerated target q before remainder8 check");
if (remainder8 != 1 && remainder8 != 7)
{
goto MOVE_NEXT;
@@ -4932,6 +5152,12 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
}
}
if (prime == 138000001UL && divisor == 1209708008767)
{
Console.WriteLine("[HIT] processed target q. Stopping.");
Environment.Exit(1);
}
MOVE_NEXT:
ContinueOuter:
RecordState(currentK);
@@ -5090,6 +5316,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
{
oneOffsets[i + 1] = pow2Offsets[i] + 1;
}
oneCount = needed;
usedPow2Offsets = true;
}
@@ -5114,6 +5341,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
pow2OffsetsTarget = new int[oneCount];
_pow2Offsets = pow2OffsetsTarget;
}
Array.Copy(oneOffsets, pow2OffsetsTarget, oneCount);
_pow2OffsetCount = oneCount;
_pow2K = currentK;
@@ -5128,23 +5356,25 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
ReadOnlySpan<int> oneOffsetsSlice = oneOffsets[..oneCount];
// BitContradictionSolverWithQMultiplier.SetDebugEnabled(true);
#if DETAILED_LOG
#if DETAILED_LOG
bool decided = BitContradictionSolverWithAMultiplier.TryCheckDivisibilityFromOneOffsets(oneOffsetsSlice, prime, currentK, isPowerOfTwo, out divides, out var reason);
#else
#else
bool decided = BitContradictionSolverWithAMultiplier.TryCheckDivisibilityFromOneOffsets(oneOffsetsSlice, prime, currentK, isPowerOfTwo, out divides);
#endif
#endif
// BitContradictionSolverWithQMultiplier.SetDebugEnabled(false);
if (decided && divides)
{
{
// Verify to avoid false positives on structurally admissible but non-dividing q.
bool structuralOk = ((divisor - BigInteger.One) % (((BigInteger)prime) << 1)) == BigInteger.Zero;
bool dividesExact = structuralOk && BigInteger.ModPow(2, prime, divisor) == BigInteger.One;
if (!dividesExact)
{
throw new InvalidOperationException($"We reported divisor q={divisor} as dividing {prime} but it doesn't divide it");
divides = false;
#if DETAILED_LOG
#if DETAILED_LOG
reason = ContradictionReason.ParityUnreachable;
#endif
#endif
}
}
if (decided && !divides)
@@ -4,18 +4,21 @@ using PerfectNumbers.Core.Gpu;
namespace PerfectNumbers.Core;
#pragma warning disable CA2211 // The fields are settable on purpose for the best performance
#pragma warning disable CA2211 // Non-constant fields should not be visible
public static class EnvironmentConfiguration
{
public static ulong DivisorLimit = ulong.MaxValue;
public static int GpuBatchSize = GpuConstants.ScanBatchSize;
public static int GpuPrimeThreads = 1;
public static int GpuRatio = PerfectNumberConstants.GpuRatio;
public static ComputationDevice MersenneDevice = ComputationDevice.Cpu;
public static BigInteger MinK = BigInteger.One;
public static ComputationDevice OrderDevice = ComputationDevice.Cpu;
public static int RollingAccelerators = PerfectNumberConstants.RollingAccelerators;
public static string? StateFilePath;
public static int ThreadCount;
public static bool UseGpu;
public static bool UseGpuOrder;
public static bool UseHybridOrder;
public static bool UseCpuOrder;
@@ -24,6 +27,7 @@ public static class EnvironmentConfiguration
public static ByDivisor.ByDivisorClassModel? ByDivisorClassModel;
public static ByDivisor.ByDivisorScanTuning ByDivisorScanTuning = ByDivisor.ByDivisorScanTuning.Default;
public static double ByDivisorCheapKLimit = 10_000d;
public static ByDivisorKIncrementMode ByDivisorKIncrementMode = ByDivisorKIncrementMode.Sequential;
public static Persistence.ByDivisorClassModelRepository? ByDivisorClassModelRepository;
public static Persistence.KStateRepository? ByDivisorKStateRepository;
public static Persistence.KStateRepository? ByDivisorCheapKStateRepository;
@@ -31,13 +35,31 @@ public static class EnvironmentConfiguration
public static Persistence.KStateRepository? ByDivisorGroupsStateRepository;
public static Persistence.Pow2Minus1StateRepository? ByDivisorPow2Minus1Repository;
public static Persistence.BitContradictionStateRepository? BitContradictionStateRepository;
public static CalculationMethod CalculationMethod = CalculationMethod.Residue;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Initialize()
{
if (MersenneDevice == ComputationDevice.Gpu &&
ByDivisorKIncrementMode == ByDivisorKIncrementMode.BitContradiction)
{
OrderDevice = ComputationDevice.Gpu;
}
UseGpuOrder = OrderDevice == ComputationDevice.Gpu;
UseHybridOrder = OrderDevice == ComputationDevice.Hybrid;
UseCpuOrder = OrderDevice == ComputationDevice.Cpu;
UseGpu =
UseGpuOrder || UseHybridOrder ||
MersenneDevice is ComputationDevice.Hybrid or ComputationDevice.Gpu;
RollingAccelerators = UseGpu
? Math.Min(Math.Min(
PerfectNumberConstants.RollingAccelerators, GpuPrimeThreads), ThreadCount)
: 0;
UsePow2GroupDivisors = ByDivisorKIncrementMode == ByDivisorKIncrementMode.Pow2Groups;
}
}
@@ -5,6 +5,238 @@ using PerfectNumbers.Core.Gpu.Kernels;
namespace PerfectNumbers.Core.Gpu.Accelerators;
public static class PrimeOrderCalculatorAcceleratorCapacityProviders
{
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static void EnsureCalculateOrderWideCapacity(this PrimeOrderCalculatorAccelerator gpu, int newSize)
{
if (newSize > gpu.Input.Length)
{
gpu.CalculateOrderWideExponentBuffer.Dispose();
gpu.CalculateOrderWideRemainderBuffer.Dispose();
var accelerator = gpu.Accelerator;
// lock (accelerator)
// {
gpu.CalculateOrderWideExponentBuffer = accelerator.Allocate1D<GpuUInt128>(newSize);
gpu.CalculateOrderWideRemainderBuffer = accelerator.Allocate1D<GpuUInt128>(newSize);
gpu.CalculateOrderWideExponentBufferView = gpu.CalculateOrderWideExponentBuffer.View;
gpu.CalculateOrderWideRemainderBufferView = gpu.CalculateOrderWideRemainderBuffer.View;
// }
}
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static void EnsurePrimeOrderCalculatorCapacity(this PrimeOrderCalculatorAccelerator gpu, int factorsCount, int primeTesterCapacity)
{
Accelerator accelerator = gpu.Accelerator;
MemoryBuffer1D<KeyValuePair<ulong, int>, Stride1D.Dense> pow2ModEntriesToTestOnDevice = gpu.Pow2ModEntriesToTestOnDevice;
if (pow2ModEntriesToTestOnDevice.Length < factorsCount)
{
// lock (accelerator)
// {
pow2ModEntriesToTestOnDevice.Dispose();
pow2ModEntriesToTestOnDevice = accelerator.Allocate1D<KeyValuePair<ulong, int>>(factorsCount);
gpu.Pow2ModEntriesToTestOnDevice = pow2ModEntriesToTestOnDevice;
gpu.Pow2ModEntriesToTestOnDeviceView = pow2ModEntriesToTestOnDevice.View;
// }
}
MemoryBuffer1D<ulong, Stride1D.Dense> input = gpu.Input;
if (input.Length < primeTesterCapacity)
{
// lock (accelerator)
// {
input.Dispose();
input = accelerator.Allocate1D<ulong>(primeTesterCapacity);
gpu.Input = input;
gpu.InputView = input.View;
// }
}
MemoryBuffer1D<byte, Stride1D.Dense> outputByte = gpu.OutputByte;
if (outputByte.Length < primeTesterCapacity)
{
// lock (accelerator)
// {
outputByte.Dispose();
outputByte = accelerator.Allocate1D<byte>(primeTesterCapacity);
gpu.OutputByte = outputByte;
gpu.OutputByteView = outputByte.View;
// }
}
MemoryBuffer1D<int, Stride1D.Dense> outputInt = gpu.OutputInt;
if (outputInt.Length < primeTesterCapacity)
{
// lock (accelerator)
// {
outputInt.Dispose();
outputInt = accelerator.Allocate1D<int>(primeTesterCapacity);
gpu.OutputInt = outputInt;
gpu.OutputIntView = outputInt.View;
// }
}
}
public static void EnsurePartialFactorCapacity(this PrimeOrderCalculatorAccelerator gpu, int newSize)
{
var accelerator = gpu.Accelerator;
var outputUlong = gpu.OutputUlong;
if (newSize > outputUlong.Length)
{
outputUlong.Dispose();
outputUlong = accelerator.Allocate1D<ulong>(newSize);
gpu.OutputUlong = outputUlong;
gpu.OutputUlongView = outputUlong.View;
}
var outputInt = gpu.OutputInt;
if (newSize > outputInt.Length)
{
outputInt.Dispose();
outputInt = accelerator.Allocate1D<int>(newSize);
gpu.OutputInt = outputInt;
gpu.OutputIntView = outputInt.View;
}
}
public static void EnsureSmallPrimeFactorSlotsCapacity(this PrimeOrderCalculatorAccelerator gpu, int newSize)
{
// Console.WriteLine($"Resizing GPU scratch buffer from pool ({buffer.SmallPrimeFactorPrimeSlots.Length} / {smallPrimeFactorSlotCount}), ({buffer.SpecialMaxFactors.Length}/{specialMaxFactorCapacity})");
var accelerator = gpu.Accelerator;
MemoryBuffer1D<ulong, Stride1D.Dense> outputUlong = gpu.OutputUlong;
if (outputUlong.Length < newSize)
{
// lock (accelerator)
// {
outputUlong.Dispose();
outputUlong = accelerator.Allocate1D<ulong>(newSize);
gpu.OutputUlong = outputUlong;
gpu.OutputUlongView = outputUlong.View;
// }
}
MemoryBuffer1D<int, Stride1D.Dense> outputInt = gpu.OutputInt;
if (outputInt.Length < newSize)
{
// lock (accelerator)
// {
outputInt.Dispose();
outputInt = accelerator.Allocate1D<int>(newSize);
gpu.OutputInt = outputInt;
gpu.OutputIntView = outputInt.View;
// }
}
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static void EnsureUlongInputOutputCapacity(this PrimeOrderCalculatorAccelerator gpu, int newSize)
{
var accelerator = gpu.Accelerator;
MemoryBuffer1D<ulong, Stride1D.Dense> buffer = gpu.Input;
if (buffer.Length < newSize)
{
// lock (accelerator)
// {
buffer.Dispose();
buffer = accelerator.Allocate1D<ulong>(newSize);
gpu.Input = buffer;
gpu.InputView = buffer.View;
// }
}
buffer = gpu.OutputUlong;
if (buffer.Length < newSize)
{
// lock (accelerator)
// {
buffer.Dispose();
buffer = accelerator.Allocate1D<ulong>(newSize);
gpu.OutputUlong = buffer;
gpu.OutputUlongView = buffer.View;
// }
}
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static BitContradictionBuffers EnsureBitContradictionCapacity(this PrimeOrderCalculatorAccelerator gpu, int maxBatchIndexWords, int maxQWords, int delta8CacheConcurrentThreads)
{
var accelerator = gpu.Accelerator;
MemoryBuffer1D<ulong, Stride1D.Dense> bufferUlong = gpu.Input;
if (bufferUlong.Length < maxBatchIndexWords)
{
// lock (accelerator)
// {
bufferUlong.Dispose();
bufferUlong = accelerator.Allocate1D<ulong>(maxBatchIndexWords);
gpu.Input = bufferUlong;
// }
}
MemoryBuffer1D<int, Stride1D.Dense> bufferInt = gpu.OutputInt;
maxQWords <<= 1;
if (bufferUlong.Length < maxQWords)
{
// lock (accelerator)
// {
bufferInt.Dispose();
bufferInt = accelerator.Allocate1D<int>(maxQWords);
gpu.OutputInt = bufferInt;
// }
}
bufferInt = gpu.StackIndexBuffer;
int requiredCapacity = BitContradictionKernels.GetDeltaCacheKeyLength(delta8CacheConcurrentThreads);
if (bufferInt.Length < requiredCapacity)
{
bufferInt.Dispose();
bufferInt = accelerator.Allocate1D<int>(requiredCapacity);
gpu.StackIndexBuffer = bufferInt;
bufferInt = gpu.StackExponentBuffer;
bufferInt.Dispose();
bufferInt = accelerator.Allocate1D<int>(BitContradictionKernels.GetDeltaCacheIntLength(delta8CacheConcurrentThreads));
gpu.StackExponentBuffer = bufferInt;
}
return new(
gpu.Input.View,
gpu.OutputInt.View,
gpu.StackIndexBuffer.View,
gpu.StackExponentBuffer.View
);
}
}
public readonly struct BitContradictionBuffers(
in ArrayView1D<ulong, Stride1D.Dense> batchIndexWords,
in ArrayView1D<int, Stride1D.Dense> foundQWords,
in ArrayView1D<int, Stride1D.Dense> delta8CacheKeys,
in ArrayView1D<int, Stride1D.Dense> delta8CacheValues
)
{
public readonly ArrayView1D<ulong, Stride1D.Dense> BatchIndexWordsView = batchIndexWords;
public readonly ArrayView1D<int, Stride1D.Dense> FoundQWordsView = foundQWords;
public readonly ArrayView1D<int, Stride1D.Dense> Delta8CacheKeysView = delta8CacheKeys;
public readonly ArrayView1D<int, Stride1D.Dense> Delta8CacheValuesView = delta8CacheValues;
}
public sealed class PrimeOrderCalculatorAccelerator
{
#region Pool
@@ -18,7 +250,7 @@ public sealed class PrimeOrderCalculatorAccelerator
var pool = _pool ??= new(PerfectNumberConstants.DefaultThreadPoolCapacity);
if (pool.TryPop(out var gpu))
{
gpu.EnsureCapacity(PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity);
gpu.EnsurePrimeOrderCalculatorCapacity(PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity);
return gpu;
}
@@ -137,8 +369,8 @@ public sealed class PrimeOrderCalculatorAccelerator
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> WorkFactorBuffer;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> CandidateBuffer;
public readonly MemoryBuffer1D<int, Stride1D.Dense> StackIndexBuffer;
public readonly MemoryBuffer1D<int, Stride1D.Dense> StackExponentBuffer;
public MemoryBuffer1D<int, Stride1D.Dense> StackIndexBuffer;
public MemoryBuffer1D<int, Stride1D.Dense> StackExponentBuffer;
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> StackProductBuffer;
public OrderKernelBuffers CalculateOrderKernelBuffers;
@@ -148,15 +380,11 @@ public sealed class PrimeOrderCalculatorAccelerator
public readonly ArrayView1D<int, Stride1D.Dense> StackExponentBufferView;
public readonly ArrayView1D<ulong, Stride1D.Dense> StackProductBufferView;
public MemoryBuffer1D<GpuUInt128, Stride1D.Dense> CalculateOrderWideExponentBuffer;
public MemoryBuffer1D<GpuUInt128, Stride1D.Dense> CalculateOrderWideRemainderBuffer;
public ArrayView1D<GpuUInt128, Stride1D.Dense> CalculateOrderWideExponentBufferView;
public ArrayView1D<GpuUInt128, Stride1D.Dense> CalculateOrderWideRemainderBufferView;
public Dictionary<ulong, int> Pow2ModEntriesToTestOnHost = [];
private static readonly CalculatorKernels[] _kernels = new CalculatorKernels[_accelerators.Length];
@@ -205,169 +433,6 @@ public sealed class PrimeOrderCalculatorAccelerator
public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> SmallPrimeSieveKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ulong, ArrayView<ulong>, int, ulong, ArrayView<ulong>> SpecialMaxKernelLauncher;
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public void EnsureCalculateOrderWideCapacity(int newSize)
{
if (newSize > Input.Length)
{
CalculateOrderWideExponentBuffer.Dispose();
CalculateOrderWideRemainderBuffer.Dispose();
var accelerator = Accelerator;
// lock (accelerator)
// {
CalculateOrderWideExponentBuffer = accelerator.Allocate1D<GpuUInt128>(newSize);
CalculateOrderWideRemainderBuffer = accelerator.Allocate1D<GpuUInt128>(newSize);
CalculateOrderWideExponentBufferView = CalculateOrderWideExponentBuffer.View;
CalculateOrderWideRemainderBufferView = CalculateOrderWideRemainderBuffer.View;
// }
}
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public void EnsureCapacity(int factorsCount, int primeTesterCapacity)
{
Accelerator accelerator = Accelerator;
MemoryBuffer1D<KeyValuePair<ulong, int>, Stride1D.Dense> pow2ModEntriesToTestOnDevice = Pow2ModEntriesToTestOnDevice;
if (pow2ModEntriesToTestOnDevice.Length < factorsCount)
{
// lock (accelerator)
// {
pow2ModEntriesToTestOnDevice.Dispose();
pow2ModEntriesToTestOnDevice = accelerator.Allocate1D<KeyValuePair<ulong, int>>(factorsCount);
Pow2ModEntriesToTestOnDevice = pow2ModEntriesToTestOnDevice;
Pow2ModEntriesToTestOnDeviceView = pow2ModEntriesToTestOnDevice.View;
// }
}
MemoryBuffer1D<ulong, Stride1D.Dense> input = Input;
if (input.Length < primeTesterCapacity)
{
// lock (accelerator)
// {
input.Dispose();
input = accelerator.Allocate1D<ulong>(primeTesterCapacity);
Input = input;
InputView = input.View;
// }
}
MemoryBuffer1D<byte, Stride1D.Dense> outputByte = OutputByte;
if (outputByte.Length < primeTesterCapacity)
{
// lock (accelerator)
// {
outputByte.Dispose();
outputByte = accelerator.Allocate1D<byte>(primeTesterCapacity);
OutputByte = outputByte;
OutputByteView = outputByte.View;
// }
}
MemoryBuffer1D<int, Stride1D.Dense> outputInt = OutputInt;
if (outputInt.Length < primeTesterCapacity)
{
// lock (accelerator)
// {
outputInt.Dispose();
outputInt = accelerator.Allocate1D<int>(primeTesterCapacity);
OutputInt = outputInt;
OutputIntView = outputInt.View;
// }
}
}
public void EnsurePartialFactorCapacity(int newSize)
{
var accelerator = Accelerator;
var outputUlong = OutputUlong;
if (newSize > outputUlong.Length)
{
outputUlong.Dispose();
outputUlong = accelerator.Allocate1D<ulong>(newSize);
OutputUlong = outputUlong;
OutputUlongView = outputUlong.View;
}
var outputInt = OutputInt;
if (newSize > outputInt.Length)
{
outputInt.Dispose();
outputInt = accelerator.Allocate1D<int>(newSize);
OutputInt = outputInt;
OutputIntView = outputInt.View;
}
}
public void EnsureSmallPrimeFactorSlotsCapacity(int newSize)
{
// Console.WriteLine($"Resizing GPU scratch buffer from pool ({buffer.SmallPrimeFactorPrimeSlots.Length} / {smallPrimeFactorSlotCount}), ({buffer.SpecialMaxFactors.Length}/{specialMaxFactorCapacity})");
var accelerator = Accelerator;
MemoryBuffer1D<ulong, Stride1D.Dense> outputUlong = OutputUlong;
if (outputUlong.Length < newSize)
{
// lock (accelerator)
// {
outputUlong.Dispose();
outputUlong = accelerator.Allocate1D<ulong>(newSize);
OutputUlong = outputUlong;
OutputUlongView = outputUlong.View;
// }
}
MemoryBuffer1D<int, Stride1D.Dense> outputInt = OutputInt;
if (outputInt.Length < newSize)
{
// lock (accelerator)
// {
outputInt.Dispose();
outputInt = accelerator.Allocate1D<int>(newSize);
OutputInt = outputInt;
OutputIntView = outputInt.View;
// }
}
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public void EnsureUlongInputOutputCapacity(int newSize)
{
var accelerator = Accelerator;
MemoryBuffer1D<ulong, Stride1D.Dense> buffer = Input;
if (buffer.Length < newSize)
{
// lock (accelerator)
// {
buffer.Dispose();
buffer = accelerator.Allocate1D<ulong>(newSize);
Input = buffer;
InputView = buffer.View;
// }
}
buffer = OutputUlong;
if (buffer.Length < newSize)
{
// lock (accelerator)
// {
buffer.Dispose();
buffer = accelerator.Allocate1D<ulong>(newSize);
OutputUlong = buffer;
OutputUlongView = buffer.View;
// }
}
}
public PrimeOrderCalculatorAccelerator(int acceleratorIndex, int factorsCount, int primeTesterCapacity, int smallPrimeFactorSlotCount, int specialMaxFactorCapacity)
{
var accelerator = _accelerators[acceleratorIndex];
+2 -2
View File
@@ -125,7 +125,7 @@ public class GpuKernelPool
{
var loaded = accelerator.LoadAutoGroupedStreamKernel<
Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<int, Stride1D.Dense>,
#if DETAILED_LOG
#if DETAILED_LOG || SELF_TEST
ArrayView1D<ulong, Stride1D.Dense>,
#endif
ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>
@@ -135,7 +135,7 @@ public class GpuKernelPool
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int,
ArrayView1D<int, Stride1D.Dense>,
#if DETAILED_LOG
#if DETAILED_LOG || SELF_TEST
ArrayView1D<ulong, Stride1D.Dense>,
#endif
ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>>>();
+1 -1
View File
@@ -23,7 +23,7 @@ public sealed class KernelContainer
ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2ModOrder;
public Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<int, Stride1D.Dense>,
#if DETAILED_LOG
#if DETAILED_LOG || SELF_TEST
ArrayView1D<ulong, Stride1D.Dense>,
#endif
ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>>? BitContradiction;
File diff suppressed because it is too large. Load diff
+7
View File
@@ -0,0 +1,7 @@
namespace PerfectNumbers.Core;
public enum GpuKernelType
{
Incremental,
Pow2Mod,
}
@@ -11,35 +11,6 @@ using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core;
public enum GpuKernelType
{
Incremental,
Pow2Mod,
}
public enum CalculationMethod
{
ByDivisor,
Divisor,
Incremental,
LucasLehmer,
Pow2Mod,
Residue,
}
public enum CacheStatus
{
Disabled,
Enabled,
}
public enum OrderDevice
{
Cpu,
Hybrid,
Gpu,
}
[EnumDependentTemplate(typeof(CalculationMethod))]
[EnumDependentTemplate(typeof(CacheStatus))]
[EnumDependentTemplate(typeof(OrderDevice))]
+8
View File
@@ -0,0 +1,8 @@
namespace PerfectNumbers.Core;
public enum OrderDevice
{
Cpu,
Hybrid,
Gpu,
}
@@ -5,6 +5,7 @@ namespace PerfectNumbers.Core;
public static class PerfectNumberConstants
{
public const ulong BiggestKnownEvenPerfectP = 136279841UL;
// public const int ConsoleInterval = 1;
public const int ConsoleInterval = 100_000;
public const int DefaultFactorsBuffer = 32;
public const int DefaultPoolCapacity = 10_240;
@@ -26,7 +27,10 @@ public static class PerfectNumberConstants
public const int PollardRhoMilliseconds = 100;
public const int ByDivisorMersenneTailCount = 8;
public const int ByDivisorMersenneBitLimit = 4096;
public const int BitContradictionGpuBatchCount = 134217728;
public const int BitContradictionGpuBatchCount = 16;
// public const int BitContradictionGpuBatchCount = 512;
// public const int BitContradictionGpuBatchCount = 131_072;
// public const int BitContradictionGpuBatchCount = 2_097_152;
public const int BitContradictionGpuThreads = 1;
// TODO: Load these limits from the benchmark-driven configuration so CPU and GPU scans stay aligned with the optimal
// divisor-cycle datasets we generate offline.
+1 -1
View File
@@ -303,7 +303,7 @@ public sealed class PrimeTester
int length = values.Length;
int acceleratorIndex = gpu.AcceleratorIndex;
gpu.EnsureCapacity(0, length);
gpu.EnsurePrimeOrderCalculatorCapacity(0, length);
var inputBufferView = gpu.InputView;
var resultBufferView = gpu.OutputByteView;
var kernelLauncher = gpu.SharesFactorKernelLauncher;
@@ -223,7 +223,7 @@ public sealed class PrimeTesterSeverOrOthers
int length = values.Length;
int acceleratorIndex = gpu.AcceleratorIndex;
gpu.EnsureCapacity(0, length);
gpu.EnsurePrimeOrderCalculatorCapacity(0, length);
var inputBufferView = gpu.InputView;
var resultBufferView = gpu.OutputByteView;
var kernelLauncher = gpu.SharesFactorKernelLauncher;