Initial BitContradiction implementation on GPU
This commit is contained in:
1 parent
e61d166eb1
commit
17763033e1
13 files changed
+1969
-23
No files matched your search
@@ -112,18 +112,25 @@ private static ThreadLocal<object>? MersenneTesters;
|
||||
}
|
||||
|
||||
Console.WriteLine("Initializing");
|
||||
int gpuPrimeThreads = Math.Max(1, _cliArguments.GpuPrimeThreads);
|
||||
int gpuPrimeThreads = Math.Max(1, _cliArguments.GpuPrimeThreads * PerfectNumberConstants.ThreadsByAccelerator);
|
||||
int threadCount = Math.Max(1, _cliArguments.ThreadCount);
|
||||
|
||||
EnvironmentConfiguration.MinK = _cliArguments.MinK;
|
||||
EnvironmentConfiguration.GpuBatchSize = Math.Max(1, _cliArguments.ScanBatchSize);
|
||||
EnvironmentConfiguration.GpuRatio = _cliArguments.GpuRatio;
|
||||
EnvironmentConfiguration.MersenneDevice = _cliArguments.MersenneDevice;
|
||||
EnvironmentConfiguration.OrderDevice = _cliArguments.OrderDevice;
|
||||
ComputationDevice orderDevice = _cliArguments.OrderDevice;
|
||||
if (_cliArguments.MersenneDevice == ComputationDevice.Gpu &&
|
||||
_cliArguments.ByDivisorKIncrementMode == ByDivisorKIncrementMode.BitContradiction)
|
||||
{
|
||||
orderDevice = ComputationDevice.Gpu;
|
||||
}
|
||||
|
||||
EnvironmentConfiguration.OrderDevice = orderDevice;
|
||||
EnvironmentConfiguration.UsePow2GroupDivisors = _cliArguments.ByDivisorKIncrementMode == ByDivisorKIncrementMode.Pow2Groups;
|
||||
EnvironmentConfiguration.ByDivisorSpecialRange = _cliArguments.ByDivisorSpecialRange;
|
||||
|
||||
bool useGpu = _cliArguments.OrderDevice == ComputationDevice.Hybrid || _cliArguments.OrderDevice == ComputationDevice.Gpu ||
|
||||
bool useGpu = orderDevice == ComputationDevice.Hybrid || orderDevice == ComputationDevice.Gpu ||
|
||||
_cliArguments.MersenneDevice == ComputationDevice.Hybrid || _cliArguments.MersenneDevice == ComputationDevice.Gpu;
|
||||
|
||||
EnvironmentConfiguration.RollingAccelerators = useGpu
|
||||
@@ -132,6 +139,10 @@ private static ThreadLocal<object>? MersenneTesters;
|
||||
|
||||
EnvironmentConfiguration.Initialize();
|
||||
|
||||
PrimeOrderCalculatorAccelerator.SkipOrderTablesAndKernels = _cliArguments.MersenneDevice == ComputationDevice.Gpu &&
|
||||
_cliArguments.UseByDivisor &&
|
||||
_cliArguments.ByDivisorKIncrementMode == ByDivisorKIncrementMode.BitContradiction;
|
||||
|
||||
// NttGpuMath.GpuTransformBackend = _cliArguments.NttBackend;
|
||||
// NttGpuMath.ReductionMode = _cliArguments.ModReductionMode;
|
||||
_runPrimesOnCpu = _cliArguments.ForcePrimeKernelsOnCpu;
|
||||
@@ -148,7 +159,16 @@ private static ThreadLocal<object>? MersenneTesters;
|
||||
if (useGpu)
|
||||
{
|
||||
Console.WriteLine("Warming up GPU kernels");
|
||||
PrimeTester.WarmUpGpuKernels(EnvironmentConfiguration.RollingAccelerators);
|
||||
if (_cliArguments.UseByDivisor &&
|
||||
_cliArguments.ByDivisorKIncrementMode == ByDivisorKIncrementMode.BitContradiction &&
|
||||
_cliArguments.MersenneDevice == ComputationDevice.Gpu)
|
||||
{
|
||||
PrimeTester.WarmUpGpuKernelsForBitContradiction(EnvironmentConfiguration.RollingAccelerators);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrimeTester.WarmUpGpuKernels(EnvironmentConfiguration.RollingAccelerators);
|
||||
}
|
||||
}
|
||||
|
||||
// PrimeTester.WarmUpGpuKernels(gpuPrimeThreads >> 4);
|
||||
@@ -328,11 +348,6 @@ private static ThreadLocal<object>? MersenneTesters;
|
||||
}
|
||||
else if (useByDivisor)
|
||||
{
|
||||
if (mersenneOnGpu)
|
||||
{
|
||||
throw new NotSupportedException("--mersenne-device=gpu is not supported for --mersenne=bydivisor on this execution path.");
|
||||
}
|
||||
|
||||
ByDivisorKIncrementMode kIncrementMode = _cliArguments.ByDivisorKIncrementMode;
|
||||
if (EnvironmentConfiguration.UseCpuOrder)
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
[Trait("Category", "Fast")]
|
||||
public class BitContradictionIntegrationTests
|
||||
{
|
||||
// 2^11 - 1 = 2047 = 23 * 89
|
||||
@@ -39,11 +40,21 @@ public class BitContradictionIntegrationTests
|
||||
divides.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BitContradictionAcceptsKnownLargeDivisor()
|
||||
{
|
||||
BigInteger divisor = BigInteger.Parse("1209708008767");
|
||||
ulong p = 138000001;
|
||||
bool decided = Invoke(divisor, p, out bool divides);
|
||||
decided.Should().BeTrue();
|
||||
divides.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BitContradictionRejectsNonDivisorWithDecision()
|
||||
{
|
||||
// 2^11 - 1 is not divisible by 25
|
||||
BigInteger divisor = 25;
|
||||
// 2^11 - 1 is not divisible by 45 (k=2, q=22*2+1).
|
||||
BigInteger divisor = 45;
|
||||
ulong p = 11;
|
||||
bool decided = Invoke(divisor, p, out bool divides);
|
||||
decided.Should().BeTrue("small p should be decided exactly");
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Kernels;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class BitContradictionKernelsTests
|
||||
{
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(31UL, 0UL, 0)]
|
||||
[InlineData(31UL, 0UL, 5)]
|
||||
[InlineData(61UL, 7UL, 3)]
|
||||
[InlineData(36721UL, 1UL, 0)]
|
||||
public void TryBuildQWordsFromBatchIndex_matches_BigInteger(ulong exponent, ulong batchIndex, int batchOffset)
|
||||
{
|
||||
int wordCount = BitContradictionKernels.MaxQWordCount;
|
||||
ulong[] batchIndexWords = new ulong[wordCount];
|
||||
batchIndexWords[0] = batchIndex;
|
||||
|
||||
ulong[] qWords = new ulong[wordCount];
|
||||
bool ok = BitContradictionKernels.TryBuildQWordsFromBatchIndex(
|
||||
exponent,
|
||||
batchIndexWords,
|
||||
wordCount,
|
||||
batchOffset,
|
||||
qWords,
|
||||
out int qBitLen);
|
||||
|
||||
ok.Should().BeTrue();
|
||||
|
||||
BigInteger expectedK = ((BigInteger)batchIndex * BitContradictionKernels.BatchCount) + BigInteger.One + batchOffset;
|
||||
BigInteger expectedQ = (expectedK * (exponent << 1)) + BigInteger.One;
|
||||
BigInteger actualQ = ReadBigIntegerFromWords(qWords);
|
||||
|
||||
actualQ.Should().Be(expectedQ);
|
||||
qBitLen.Should().Be(GetBitLength(expectedQ));
|
||||
}
|
||||
|
||||
private static BigInteger ReadBigIntegerFromWords(ReadOnlySpan<ulong> words)
|
||||
{
|
||||
byte[] bytes = new byte[words.Length * sizeof(ulong)];
|
||||
for (int w = 0; w < words.Length; w++)
|
||||
{
|
||||
ulong word = words[w];
|
||||
int byteIndex = w << 3;
|
||||
bytes[byteIndex] = (byte)word;
|
||||
bytes[byteIndex + 1] = (byte)(word >> 8);
|
||||
bytes[byteIndex + 2] = (byte)(word >> 16);
|
||||
bytes[byteIndex + 3] = (byte)(word >> 24);
|
||||
bytes[byteIndex + 4] = (byte)(word >> 32);
|
||||
bytes[byteIndex + 5] = (byte)(word >> 40);
|
||||
bytes[byteIndex + 6] = (byte)(word >> 48);
|
||||
bytes[byteIndex + 7] = (byte)(word >> 56);
|
||||
}
|
||||
|
||||
return new BigInteger(bytes, isUnsigned: true, isBigEndian: false);
|
||||
}
|
||||
|
||||
private static int GetBitLength(BigInteger value)
|
||||
{
|
||||
if (value.IsZero)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
byte[] bytes = value.ToByteArray(isUnsigned: true, isBigEndian: false);
|
||||
int last = bytes[^1];
|
||||
int bits = (bytes.Length - 1) * 8;
|
||||
while (last != 0)
|
||||
{
|
||||
bits++;
|
||||
last >>= 1;
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public class BitContradictionSolverTests
|
||||
ColumnBounds bounds = ComputeColumnBounds(a, q, 1);
|
||||
|
||||
bounds.ForcedOnes.Should().Be(1);
|
||||
bounds.PossibleOnes.Should().Be(1);
|
||||
bounds.PossibleOnes.Should().Be(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Numerics;
|
||||
using PerfectNumbers.Core;
|
||||
|
||||
namespace PerfectNumbers.Core.ByDivisor;
|
||||
|
||||
@@ -119,6 +120,7 @@ internal sealed class PrivateWorkSet
|
||||
/// interval-based carry propagation described in the BitContradictions plan document.
|
||||
/// The solver itself will build on top of these primitives.
|
||||
/// </summary>
|
||||
[DeviceDependentTemplate(typeof(ComputationDevice))]
|
||||
internal static class BitContradictionSolverWithAMultiplier
|
||||
{
|
||||
private const int PingPongBlockColumns = 4096;
|
||||
@@ -1510,6 +1512,11 @@ internal static class BitContradictionSolverWithAMultiplier
|
||||
out bool divides)
|
||||
#endif
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
PrivateWorkSet workSet = _privateWorkSet;
|
||||
|
||||
divides = false;
|
||||
@@ -2070,6 +2077,22 @@ internal static class BitContradictionSolverWithAMultiplier
|
||||
// }
|
||||
|
||||
divides = true;
|
||||
if (p <= 63 && divides)
|
||||
{
|
||||
ulong qLow = BuildQLow64(qOneOffsets);
|
||||
if ((qLow & 1UL) == 0UL)
|
||||
{
|
||||
divides = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
BigInteger qExact = new BigInteger(qLow);
|
||||
if (BigInteger.ModPow(2, (int)p, qExact) != BigInteger.One)
|
||||
{
|
||||
divides = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if DETAILED_LOG
|
||||
reason = ContradictionReason.None;
|
||||
PrintStats();
|
||||
|
||||
@@ -11,7 +11,9 @@ using System.Globalization;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Buffers;
|
||||
using ILGPU.Runtime;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Kernels;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using PerfectNumbers.Core.ByDivisor;
|
||||
using PerfectNumbers.Core.Persistence;
|
||||
@@ -58,6 +60,226 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
private GpuUInt128WorkSet _divisorScanGpuWorkSet;
|
||||
private static readonly ConcurrentDictionary<ulong, byte> _checkedPow2MinusOne = new();
|
||||
private ulong _currentPrime;
|
||||
|
||||
#if DEVICE_GPU
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
private bool CheckDivisorsBitContradictionGpu(
|
||||
ulong prime,
|
||||
ushort primeDecimalMask,
|
||||
BigInteger allowedMax,
|
||||
BigInteger minK,
|
||||
BigInteger step,
|
||||
out bool processedAll,
|
||||
out BigInteger foundDivisor)
|
||||
{
|
||||
processedAll = false;
|
||||
foundDivisor = BigInteger.Zero;
|
||||
|
||||
BigInteger currentK = minK;
|
||||
bool hasLimit = !allowedMax.IsZero;
|
||||
BigInteger maxK = hasLimit ? (allowedMax - BigInteger.One) / step : BigInteger.Zero;
|
||||
|
||||
BigInteger maxQ = (BigInteger.One << BitContradictionKernels.MaxQBitLength) - BigInteger.One;
|
||||
BigInteger maxKByBits = (maxQ - BigInteger.One) / step;
|
||||
if (!hasLimit || maxKByBits < maxK)
|
||||
{
|
||||
maxK = maxKByBits;
|
||||
}
|
||||
|
||||
if (maxK < currentK)
|
||||
{
|
||||
processedAll = hasLimit;
|
||||
return false;
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator gpu = Accelerator;
|
||||
int maxThreadCount = PerfectNumberConstants.BitContradictionGpuThreads;
|
||||
int batchSize = PerfectNumberConstants.BitContradictionGpuBatchCount;
|
||||
BigInteger batchSizeBig = batchSize;
|
||||
int wordCount = BitContradictionKernels.MaxQWordCount;
|
||||
int inputCapacity = maxThreadCount * wordCount;
|
||||
gpu.EnsureCapacity(PerfectNumberConstants.DefaultFactorsBuffer, inputCapacity);
|
||||
gpu.EnsurePartialFactorCapacity(inputCapacity);
|
||||
|
||||
var stream = AcceleratorStreamPool.Rent(gpu.AcceleratorIndex);
|
||||
var kernels = GpuKernelPool.GetOrAddKernels(gpu.AcceleratorIndex, stream, KernelType.BitContradictionScan);
|
||||
var kernel = kernels.BitContradiction!;
|
||||
|
||||
int[] found = new int[maxThreadCount];
|
||||
ulong[] batchIndexWords = new ulong[inputCapacity];
|
||||
|
||||
BigInteger currentBatch = (currentK - BigInteger.One) / batchSizeBig;
|
||||
currentK = (currentBatch * batchSizeBig) + BigInteger.One;
|
||||
BigInteger maxBatch = (maxK - BigInteger.One) / batchSizeBig;
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
||||
if (hasLimit && currentBatch > maxBatch)
|
||||
{
|
||||
processedAll = true;
|
||||
break;
|
||||
}
|
||||
|
||||
BigInteger remainingBatches = maxBatch - currentBatch + BigInteger.One;
|
||||
if (remainingBatches <= BigInteger.Zero)
|
||||
{
|
||||
processedAll = hasLimit;
|
||||
break;
|
||||
}
|
||||
|
||||
int threadCount = maxThreadCount;
|
||||
if (remainingBatches < threadCount)
|
||||
{
|
||||
threadCount = (int)remainingBatches;
|
||||
}
|
||||
|
||||
if (currentBatch > ulong.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException($"BitContradiction GPU batch index overflow for p={prime}, batch={currentBatch}");
|
||||
}
|
||||
|
||||
BigInteger maxBatchIndex = currentBatch + (threadCount - 1);
|
||||
if (maxBatchIndex > ulong.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException($"BitContradiction GPU batch index overflow for p={prime}, batch={currentBatch}, threads={threadCount}");
|
||||
}
|
||||
|
||||
int countQ = batchSize;
|
||||
|
||||
for (int t = 0; t < threadCount; t++)
|
||||
{
|
||||
BigInteger batchIndex = currentBatch + t;
|
||||
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);
|
||||
stream.Synchronize();
|
||||
gpu.OutputIntView.CopyToCPU(stream, found.AsSpan(0, threadCount));
|
||||
|
||||
for (int i = 0; i < threadCount; i++)
|
||||
{
|
||||
int foundIndex = found[i];
|
||||
if (foundIndex >= 0)
|
||||
{
|
||||
BigInteger batchIndex = currentBatch + i;
|
||||
BigInteger candidateK = (batchIndex * batchSizeBig) + BigInteger.One + foundIndex;
|
||||
if (hasLimit && candidateK > maxK)
|
||||
{
|
||||
processedAll = true;
|
||||
AcceleratorStreamPool.Return(gpu.AcceleratorIndex, stream);
|
||||
return false;
|
||||
}
|
||||
|
||||
BigInteger candidateQ = (step * candidateK) + BigInteger.One;
|
||||
Span<ulong> debugQWords = stackalloc ulong[BitContradictionKernels.MaxQWordCount];
|
||||
if (!BitContradictionKernels.TryBuildQWordsFromBatchIndex(prime, batchIndexWords.AsSpan(i * wordCount, wordCount), wordCount, foundIndex, debugQWords, out int debugQBitLen))
|
||||
{
|
||||
throw new InvalidOperationException($"BitContradiction GPU qWords overflow for p={prime}, k={candidateK}, batch={batchIndex}, index={foundIndex}");
|
||||
}
|
||||
BigInteger debugQ = ReadBigIntegerFromWords(debugQWords);
|
||||
if (debugQ != candidateQ)
|
||||
{
|
||||
throw new InvalidOperationException($"BitContradiction GPU qWords mismatch for p={prime}, k={candidateK}, batch={batchIndex}, index={foundIndex}, cpuQ={candidateQ}, gpuQ={debugQ}, gpuQBits={debugQBitLen}");
|
||||
}
|
||||
#if DivisorSet_BitContradiction
|
||||
int[]? qBits = _qBits;
|
||||
if (qBits == null || qBits.Length < (int)prime)
|
||||
{
|
||||
qBits = new int[(int)prime];
|
||||
_qBits = qBits;
|
||||
}
|
||||
bool cpuDecided = TryExactBitContradictionCheck(prime, candidateQ, candidateK, out bool cpuDivides);
|
||||
if (!cpuDecided)
|
||||
{
|
||||
throw new InvalidOperationException($"BitContradiction CPU undecided for p={prime}, q={candidateQ}, k={candidateK}, batch={batchIndex}");
|
||||
}
|
||||
if (!cpuDivides)
|
||||
{
|
||||
throw new InvalidOperationException($"BitContradiction CPU/GPU mismatch for p={prime}, q={candidateQ}, k={candidateK}, batch={batchIndex}");
|
||||
}
|
||||
#endif
|
||||
bool dividesExact = BigInteger.ModPow(2, prime, candidateQ) == BigInteger.One;
|
||||
if (!dividesExact)
|
||||
{
|
||||
throw new InvalidOperationException($"BitContradiction CPU/GPU false positive for p={prime}, q={candidateQ}, k={candidateK}, batch={batchIndex}");
|
||||
}
|
||||
foundDivisor = candidateQ;
|
||||
AcceleratorStreamPool.Return(gpu.AcceleratorIndex, stream);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ContinueOuter:
|
||||
RecordState(currentK);
|
||||
if (hasLimit && currentK > maxK)
|
||||
{
|
||||
processedAll = true;
|
||||
break;
|
||||
}
|
||||
|
||||
currentBatch += threadCount;
|
||||
currentK = (currentBatch * batchSizeBig) + BigInteger.One;
|
||||
}
|
||||
|
||||
AcceleratorStreamPool.Return(gpu.AcceleratorIndex, stream);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void WriteBigIntegerToWords(BigInteger value, Span<ulong> words)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[BitContradictionKernels.MaxQWordCount * 8];
|
||||
if (!value.TryWriteBytes(bytes, out int written, isUnsigned: true, isBigEndian: false))
|
||||
{
|
||||
written = 0;
|
||||
}
|
||||
|
||||
int wordCount = words.Length;
|
||||
for (int w = 0; w < wordCount; w++)
|
||||
{
|
||||
ulong word = 0UL;
|
||||
int byteIndex = w << 3;
|
||||
int limit = written - byteIndex;
|
||||
if (limit > 0)
|
||||
{
|
||||
if (limit > 8)
|
||||
{
|
||||
limit = 8;
|
||||
}
|
||||
|
||||
for (int b = 0; b < limit; b++)
|
||||
{
|
||||
word |= (ulong)bytes[byteIndex + b] << (b << 3);
|
||||
}
|
||||
}
|
||||
|
||||
words[w] = word;
|
||||
}
|
||||
}
|
||||
|
||||
private static BigInteger ReadBigIntegerFromWords(ReadOnlySpan<ulong> words)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[BitContradictionKernels.MaxQWordCount * 8];
|
||||
int wordCount = words.Length;
|
||||
for (int w = 0; w < wordCount; w++)
|
||||
{
|
||||
ulong word = words[w];
|
||||
int byteIndex = w << 3;
|
||||
bytes[byteIndex] = (byte)word;
|
||||
bytes[byteIndex + 1] = (byte)(word >> 8);
|
||||
bytes[byteIndex + 2] = (byte)(word >> 16);
|
||||
bytes[byteIndex + 3] = (byte)(word >> 24);
|
||||
bytes[byteIndex + 4] = (byte)(word >> 32);
|
||||
bytes[byteIndex + 5] = (byte)(word >> 40);
|
||||
bytes[byteIndex + 6] = (byte)(word >> 48);
|
||||
bytes[byteIndex + 7] = (byte)(word >> 56);
|
||||
}
|
||||
|
||||
return new BigInteger(bytes, isUnsigned: true, isBigEndian: false);
|
||||
}
|
||||
#endif
|
||||
|
||||
[ThreadStatic]
|
||||
private static Dictionary<ulong, ulong>? _pow2DivisorPowModCache;
|
||||
#if DETAILED_LOG
|
||||
@@ -578,7 +800,6 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
out processedAll,
|
||||
out foundDivisor);
|
||||
}
|
||||
#endif
|
||||
|
||||
// return CheckDivisorsLarge(
|
||||
// prime,
|
||||
@@ -596,6 +817,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
step,
|
||||
out processedAll,
|
||||
out foundDivisor);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if DivisorSet_Pow2Groups
|
||||
@@ -1421,6 +1643,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
return true;
|
||||
}
|
||||
|
||||
ContinueOuter:
|
||||
RecordState(currentK);
|
||||
|
||||
MOVE_NEXT:
|
||||
@@ -1732,6 +1955,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
{
|
||||
processedAll = false;
|
||||
foundDivisor = BigInteger.Zero;
|
||||
bool coversRange = true;
|
||||
|
||||
if (minK > ulong.MaxValue)
|
||||
{
|
||||
@@ -2130,7 +2354,8 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
{
|
||||
if (((primeDecimalMask >> remainder10) & 1) != 0)
|
||||
{
|
||||
RecordState(currentK);
|
||||
ContinueOuter:
|
||||
RecordState(currentK);
|
||||
BigInteger powResult = BigInteger.ModPow(BigIntegerNumbers.Two, primeBig, divisor);
|
||||
// if (powResult.IsOne && !IsMersenneValue(prime, divisor))
|
||||
if (powResult.IsOne)
|
||||
@@ -2757,7 +2982,8 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
#endif
|
||||
}
|
||||
|
||||
RecordState(currentK);
|
||||
ContinueOuter:
|
||||
RecordState(currentK);
|
||||
// if (divisorCycle == prime && !IsMersenneValue(prime, candidate))
|
||||
if (divisorCycle == prime)
|
||||
{
|
||||
@@ -2877,7 +3103,8 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
if (residueStepper.IsAdmissible())
|
||||
{
|
||||
// Stopwatch sw = Stopwatch.StartNew();
|
||||
RecordState(currentK);
|
||||
ContinueOuter:
|
||||
RecordState(currentK);
|
||||
ulong powMod;
|
||||
bool isPow2 = IsPowerOfTwo(currentK);
|
||||
#if DETAILED_LOG
|
||||
@@ -2978,7 +3205,8 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
byte remainder8 = rem8.ComputeNext(divisor);
|
||||
if (remainder8 == 1 || remainder8 == 7)
|
||||
{
|
||||
RecordState(currentK);
|
||||
ContinueOuter:
|
||||
RecordState(currentK);
|
||||
BigInteger powResult = BigInteger.ModPow(BigIntegerNumbers.Two, primeBig, divisor);
|
||||
if (powResult.IsOne)
|
||||
{
|
||||
@@ -3677,7 +3905,8 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
byte remainder8 = rem8.ComputeNext(divisor);
|
||||
if (remainder8 == 1 || remainder8 == 7)
|
||||
{
|
||||
RecordState(currentK);
|
||||
ContinueOuter:
|
||||
RecordState(currentK);
|
||||
BigInteger powResult = BigInteger.ModPow(BigIntegerNumbers.Two, primeBig, divisor);
|
||||
// if (powResult.IsOne && !IsMersenneValue(prime, divisor))
|
||||
if (powResult.IsOne)
|
||||
@@ -4031,7 +4260,8 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
divisorCycle = computedCycle;
|
||||
}
|
||||
|
||||
RecordState(currentK);
|
||||
ContinueOuter:
|
||||
RecordState(currentK);
|
||||
if (divisorCycle == prime && !IsMersenneValue(prime, divisor))
|
||||
{
|
||||
foundDivisor = divisor;
|
||||
@@ -4585,6 +4815,10 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
out bool processedAll,
|
||||
out BigInteger foundDivisor)
|
||||
{
|
||||
#if DEVICE_GPU
|
||||
return CheckDivisorsBitContradictionGpu(prime, primeDecimalMask, allowedMax, minK, step, out processedAll, out foundDivisor);
|
||||
#else
|
||||
|
||||
processedAll = true;
|
||||
foundDivisor = BigInteger.Zero;
|
||||
|
||||
@@ -4643,6 +4877,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
}
|
||||
|
||||
MOVE_NEXT:
|
||||
ContinueOuter:
|
||||
RecordState(currentK);
|
||||
if (hasLimit && currentK >= maxK)
|
||||
{
|
||||
@@ -4707,6 +4942,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
// step,
|
||||
// out processedAll,
|
||||
// out foundDivisor);
|
||||
#endif
|
||||
}
|
||||
|
||||
[ThreadStatic]
|
||||
|
||||
@@ -56,7 +56,11 @@ public sealed class PrimeOrderCalculatorAccelerator
|
||||
internal static void WarmUp()
|
||||
{
|
||||
Accelerator[] accelerators = AcceleratorPool.Shared.Accelerators;
|
||||
int acceleratorCount = accelerators.Length;
|
||||
int acceleratorCount = EnvironmentConfiguration.RollingAccelerators;
|
||||
if (acceleratorCount > accelerators.Length)
|
||||
{
|
||||
acceleratorCount = accelerators.Length;
|
||||
}
|
||||
for (var i = 0; i < acceleratorCount; i++)
|
||||
{
|
||||
Console.WriteLine($"Preparing accelerator {i}...");
|
||||
@@ -64,6 +68,13 @@ public sealed class PrimeOrderCalculatorAccelerator
|
||||
AcceleratorStreamPool.WarmUp(i);
|
||||
// Don't take this from the pool as quick uploads of data to the accelerator consumes much of GPU's memory and throws.
|
||||
AcceleratorStream stream = accelerator.CreateStream();
|
||||
if (SkipOrderTablesAndKernels)
|
||||
{
|
||||
stream.Synchronize();
|
||||
stream.Dispose();
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: Review which tables are needed when final execution path is defined.
|
||||
LastDigitGpuTables.WarmUp(i, stream);
|
||||
_kernels[i] = new CalculatorKernels(accelerator);
|
||||
@@ -99,6 +110,9 @@ public sealed class PrimeOrderCalculatorAccelerator
|
||||
|
||||
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||
|
||||
public static bool SkipOrderTablesAndKernels;
|
||||
|
||||
|
||||
public readonly Accelerator Accelerator;
|
||||
public readonly int AcceleratorIndex;
|
||||
|
||||
@@ -411,6 +425,28 @@ public sealed class PrimeOrderCalculatorAccelerator
|
||||
CalculateOrderWideExponentBufferView = CalculateOrderWideExponentBuffer.View;
|
||||
CalculateOrderWideRemainderBufferView = CalculateOrderWideRemainderBuffer.View;
|
||||
|
||||
if (SkipOrderTablesAndKernels)
|
||||
{
|
||||
DevicePrimesLastOne = default;
|
||||
DevicePrimesLastSeven = default;
|
||||
DevicePrimesLastThree = default;
|
||||
DevicePrimesLastNine = default;
|
||||
DevicePrimesPow2LastOne = default;
|
||||
DevicePrimesPow2LastSeven = default;
|
||||
DevicePrimesPow2LastThree = default;
|
||||
DevicePrimesPow2LastNine = default;
|
||||
SmallPrimeFactorPrimes = default;
|
||||
SmallPrimeFactorSquares = default;
|
||||
CheckFactorsKernel = default;
|
||||
ConvertToStandardKernelLauncher = default;
|
||||
HeuristicCombinedTrialDivisionKernelLauncher = default;
|
||||
Pow2ModKernelLauncher = default;
|
||||
Pow2ModWideKernelLauncher = default;
|
||||
SmallPrimeFactorKernelLauncher = default;
|
||||
SpecialMaxKernelLauncher = default;
|
||||
return;
|
||||
}
|
||||
|
||||
LastDigitGpuTables lastDigitSharedTables = LastDigitGpuTables.GetStaticTables(acceleratorIndex);
|
||||
DevicePrimesLastOne = lastDigitSharedTables.DevicePrimesLastOne.View;
|
||||
DevicePrimesLastSeven = lastDigitSharedTables.DevicePrimesLastSeven.View;
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Runtime.CompilerServices;
|
||||
using ILGPU;
|
||||
using ILGPU.Runtime;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using PerfectNumbers.Core.Gpu.Kernels;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
@@ -26,7 +27,7 @@ public class GpuKernelPool
|
||||
|
||||
internal static KernelContainer GetOrAddKernels(int acceleratorIndex, AcceleratorStream stream, KernelType kernelType)
|
||||
{
|
||||
var pool = _pool ??= [];
|
||||
var pool = _pool ??= new KernelContainer[_accelerators.Length];
|
||||
if (pool[acceleratorIndex] is not { } kernels)
|
||||
{
|
||||
kernels = new();
|
||||
@@ -58,6 +59,11 @@ public class GpuKernelPool
|
||||
InitPow2ModOrderKernelScan(accelerator, kernels);
|
||||
}
|
||||
|
||||
if (kernelType.HasFlag(KernelType.BitContradictionScan))
|
||||
{
|
||||
InitBitContradictionKernel(accelerator, kernels);
|
||||
}
|
||||
|
||||
PreloadStaticTables(accelerator, kernels, stream, kernelType);
|
||||
|
||||
pool[acceleratorIndex] = kernels;
|
||||
@@ -113,6 +119,18 @@ public class GpuKernelPool
|
||||
});
|
||||
}
|
||||
|
||||
static void InitBitContradictionKernel(Accelerator accelerator, KernelContainer kernels)
|
||||
{
|
||||
InitOnce(ref kernels.BitContradiction, () =>
|
||||
{
|
||||
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<int, Stride1D.Dense>>(BitContradictionKernels.BitContradictionKernelScan);
|
||||
|
||||
var kernel = KernelUtil.GetKernel(loaded);
|
||||
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<int, Stride1D.Dense>>>();
|
||||
});
|
||||
}
|
||||
|
||||
static void InitPow2ModOrderKernelScan(Accelerator accelerator, KernelContainer kernels)
|
||||
{
|
||||
InitOnce(ref kernels.Pow2ModOrder, () =>
|
||||
|
||||
@@ -22,6 +22,9 @@ public sealed class KernelContainer
|
||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
||||
ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2ModOrder;
|
||||
|
||||
public Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int,
|
||||
ArrayView1D<int, Stride1D.Dense>>? BitContradiction;
|
||||
|
||||
public Kernel? SmallPrimeFactor;
|
||||
|
||||
public Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>? SpecialMax;
|
||||
|
||||
@@ -10,5 +10,6 @@ public enum KernelType
|
||||
OrderKernelScan,
|
||||
IncrementalKernelScan,
|
||||
IncrementalOrderKernelScan,
|
||||
Pow2ModOrderKernelScan
|
||||
Pow2ModOrderKernelScan,
|
||||
BitContradictionScan
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -20,12 +20,14 @@ public static class PerfectNumberConstants
|
||||
public const int PooledArrayThreshold = 64;
|
||||
public const int Pow2WindowSize = 8;
|
||||
public const int RollingAccelerators = 298;
|
||||
public const int ThreadsByAccelerator = 1;
|
||||
public const int ThreadsByAccelerator = 128;
|
||||
public const int ByDivisorStateSaveInterval = 1_000;
|
||||
public const string ByDivisorStateDirectory = "Checks";
|
||||
public const int PollardRhoMilliseconds = 100;
|
||||
public const int ByDivisorMersenneTailCount = 8;
|
||||
public const int ByDivisorMersenneBitLimit = 4096;
|
||||
public const int BitContradictionGpuBatchCount = 16_777_216;
|
||||
public const int BitContradictionGpuThreads = 4096;
|
||||
// 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.
|
||||
// TODO: Promote these magic numbers into a runtime profile derived from EvenPerfectBitScanner.Benchmarks so we can retune
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using ILGPU.Runtime;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
@@ -157,6 +158,45 @@ public sealed class PrimeTester
|
||||
|
||||
private static readonly object GpuWarmUpLock = new();
|
||||
private static int WarmedGpuLeaseCount;
|
||||
private static int WarmedBitContradictionGpuLeaseCount;
|
||||
|
||||
public static void WarmUpGpuKernelsForBitContradiction(int threadCount)
|
||||
{
|
||||
int target = threadCount;
|
||||
if (target == 0)
|
||||
{
|
||||
target = threadCount;
|
||||
}
|
||||
|
||||
lock (GpuWarmUpLock)
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.SkipOrderTablesAndKernels = true;
|
||||
|
||||
if (target <= WarmedBitContradictionGpuLeaseCount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Accelerator[] accelerators = AcceleratorPool.Shared.Accelerators;
|
||||
int acceleratorCount = EnvironmentConfiguration.RollingAccelerators;
|
||||
if (acceleratorCount > accelerators.Length)
|
||||
{
|
||||
acceleratorCount = accelerators.Length;
|
||||
}
|
||||
for (var i = 0; i < acceleratorCount; i++)
|
||||
{
|
||||
Console.WriteLine($"Preparing accelerator {i}...");
|
||||
AcceleratorStreamPool.WarmUp(i);
|
||||
Accelerator accelerator = accelerators[i];
|
||||
AcceleratorStream stream = accelerator.CreateStream();
|
||||
_ = GpuKernelPool.GetOrAddKernels(i, stream, KernelType.BitContradictionScan);
|
||||
stream.Synchronize();
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
WarmedBitContradictionGpuLeaseCount = target;
|
||||
}
|
||||
}
|
||||
|
||||
public static void WarmUpGpuKernels(int threadCount)
|
||||
{
|
||||
@@ -173,6 +213,7 @@ public sealed class PrimeTester
|
||||
return;
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.SkipOrderTablesAndKernels = false;
|
||||
PrimeOrderCalculatorAccelerator.WarmUp();
|
||||
WarmedGpuLeaseCount = target;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user