Hybrid device mode implemented as the current --mersenne=bydivisor CPU execution path. CPU path is now limited to CPU calculations, only.

This commit is contained in:
mlemanczyk committed 2025-12-01 03:45:39 +01:00
1 parent 353a121d03
commit 941733905b
49 files changed
+4965 -1726

No files matched your search

@@ -54,7 +54,7 @@ internal static class CandidatesCalculator
return trackers.Value!;
}
internal static bool IsCompositeByResidues(PrimeOrderCalculatorAccelerator gpu, ulong p)
internal static bool IsCompositeByResiduesCpu(ulong p)
{
ModResidueTracker tracker = AcquireResidueTracker();
// Use ModResidueTracker with a small set of primes to pre-filter composite p.
@@ -72,7 +72,57 @@ internal static class CandidatesCalculator
break;
}
if (tracker.MergeOrAppend(gpu, p, primes[primeIndex], out bool divisible) && divisible)
if (tracker.MergeOrAppendCpu(p, primes[primeIndex], out bool divisible) && divisible)
{
return true;
}
}
return false;
}
internal static bool IsCompositeByResiduesGpu(PrimeOrderCalculatorAccelerator gpu, ulong p)
{
ModResidueTracker tracker = AcquireResidueTracker();
// Use ModResidueTracker with a small set of primes to pre-filter composite p.
tracker.BeginMerge(p);
// TODO: Integrate the divisor-cycle cache here so the small-prime sweep reuses precomputed remainders instead of
// running MergeOrAppend for every candidate and missing the cycle-accelerated early exits.
// Use the small prime list from PerfectNumbers.Core to the extent of sqrt(p)
var primes = PrimesGenerator.SmallPrimes;
var primesPow2 = PrimesGenerator.SmallPrimesPow2;
int len = primes.Length;
for (int primeIndex = 0; primeIndex < len; primeIndex++)
{
if (primesPow2[primeIndex] > p)
{
break;
}
if (tracker.MergeOrAppendGpu(gpu, p, primes[primeIndex], out bool divisible) && divisible)
{
return true;
}
}
return false;
}
internal static bool IsCompositeByResiduesHybrid(PrimeOrderCalculatorAccelerator gpu, ulong p)
{
ModResidueTracker tracker = AcquireResidueTracker();
tracker.BeginMerge(p);
var primes = PrimesGenerator.SmallPrimes;
var primesPow2 = PrimesGenerator.SmallPrimesPow2;
int len = primes.Length;
for (int primeIndex = 0; primeIndex < len; primeIndex++)
{
if (primesPow2[primeIndex] > p)
{
break;
}
if (tracker.MergeOrAppendHybrid(gpu, p, primes[primeIndex], out bool divisible) && divisible)
{
return true;
}
+32 -15
View File
@@ -27,8 +27,8 @@ internal readonly struct CliArguments
internal readonly bool UseGcdFilter;
internal readonly bool TestMode;
internal readonly bool UseGpuCycles;
internal readonly bool UseMersenneOnGpu;
internal readonly bool UseOrderOnGpu;
internal readonly ComputationDevice MersenneDevice;
internal readonly ComputationDevice OrderDevice;
internal readonly int ScanBatchSize;
internal readonly int SliceSize;
internal readonly ulong ResidueKMax;
@@ -72,8 +72,8 @@ internal readonly struct CliArguments
bool useGcdFilter,
bool testMode,
bool useGpuCycles,
bool useMersenneOnGpu,
bool useOrderOnGpu,
ComputationDevice mersenneDevice,
ComputationDevice orderDevice,
int scanBatchSize,
int sliceSize,
ulong residueKMax,
@@ -116,8 +116,8 @@ internal readonly struct CliArguments
UseGcdFilter = useGcdFilter;
TestMode = testMode;
UseGpuCycles = useGpuCycles;
UseMersenneOnGpu = useMersenneOnGpu;
UseOrderOnGpu = useOrderOnGpu;
MersenneDevice = mersenneDevice;
OrderDevice = orderDevice;
ScanBatchSize = scanBatchSize;
SliceSize = sliceSize;
ResidueKMax = residueKMax;
@@ -165,8 +165,8 @@ internal readonly struct CliArguments
bool useGcdFilter = false;
bool testMode = false;
bool useGpuCycles = true;
bool useMersenneOnGpu = true;
bool useOrderOnGpu = true;
ComputationDevice mersenneDevice = ComputationDevice.Gpu;
ComputationDevice orderDevice = ComputationDevice.Gpu;
int scanBatchSize = 2_097_152;
int sliceSize = 32;
ulong residueKMax = 5_000_000UL;
@@ -319,7 +319,8 @@ internal readonly struct CliArguments
if (argument.StartsWith("--mersenne-device=", StringComparison.OrdinalIgnoreCase))
{
useMersenneOnGpu = !argument.AsSpan("--mersenne-device=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
ReadOnlySpan<char> value = argument.AsSpan("--mersenne-device=".Length);
mersenneDevice = ParseDevice(value);
continue;
}
@@ -389,7 +390,8 @@ internal readonly struct CliArguments
if (argument.StartsWith("--order-device=", StringComparison.OrdinalIgnoreCase))
{
useOrderOnGpu = !argument.AsSpan("--order-device=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
ReadOnlySpan<char> value = argument.AsSpan("--order-device=".Length);
orderDevice = ParseDevice(value);
continue;
}
@@ -445,7 +447,7 @@ internal readonly struct CliArguments
if (argument.StartsWith("--lucas=", StringComparison.OrdinalIgnoreCase))
{
useMersenneOnGpu = !argument.AsSpan("--lucas=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
mersenneDevice = ParseDevice(argument.AsSpan("--lucas=".Length));
continue;
}
@@ -565,8 +567,8 @@ internal readonly struct CliArguments
useGcdFilter,
testMode,
useGpuCycles,
useMersenneOnGpu,
useOrderOnGpu,
mersenneDevice,
orderDevice,
scanBatchSize,
sliceSize,
residueKMax,
@@ -603,10 +605,10 @@ internal readonly struct CliArguments
Console.WriteLine(" --block-size=<value> values processed per thread batch");
Console.WriteLine(" --mersenne=pow2mod|incremental|lucas|residue|divisor|bydivisor Mersenne test method");
Console.WriteLine(" --residue-max-k=<value> max k for residue Mersenne test (q = 2*p*k + 1)");
Console.WriteLine(" --mersenne-device=cpu|gpu Device for Mersenne method (default gpu)");
Console.WriteLine(" --mersenne-device=cpu|gpu|hybrid Device for Mersenne method (default gpu)");
Console.WriteLine(" --primes-device=cpu|gpu Device for prime-scan kernels (default gpu)");
Console.WriteLine(" --gpu-prime-batch=<n> Batch size for GPU primality sieve (default 262144)");
Console.WriteLine(" --order-device=cpu|gpu Device for order computations (default gpu)");
Console.WriteLine(" --order-device=cpu|gpu|hybrid Device for order computations (default gpu)");
Console.WriteLine(" --ntt=reference|staged GPU NTT backend (default staged)");
Console.WriteLine(" --mod-reduction=auto|uint128|mont64|barrett128 staged NTT reduction (default auto)");
Console.WriteLine(" --gpu-prime-threads=<value> max concurrent GPU prime checks (default 1)");
@@ -632,4 +634,19 @@ internal readonly struct CliArguments
Console.WriteLine(" --min-k=<value> starting k for --mersenne=bydivisor divisor scan (default 1)");
Console.WriteLine(" --help, -help, --?, -?, /? show this help message");
}
private static ComputationDevice ParseDevice(ReadOnlySpan<char> value)
{
if (value.Equals("cpu", StringComparison.OrdinalIgnoreCase))
{
return ComputationDevice.Cpu;
}
if (value.Equals("hybrid", StringComparison.OrdinalIgnoreCase))
{
return ComputationDevice.Hybrid;
}
return ComputationDevice.Gpu;
}
}
+61 -17
View File
@@ -83,8 +83,11 @@ internal static class Program
? PrimeTransformMode.Bit
: (useResidue ? PrimeTransformMode.Add : PrimeTransformMode.AddPrimes);
CandidatesCalculator.Configure(transformMode);
bool mersenneOnGpu = _cliArguments.UseMersenneOnGpu;
bool orderOnGpu = _cliArguments.UseOrderOnGpu;
ComputationDevice mersenneDevice = _cliArguments.MersenneDevice;
ComputationDevice orderDevice = _cliArguments.OrderDevice;
bool mersenneOnGpu = mersenneDevice == ComputationDevice.Gpu;
bool orderOnGpu = orderDevice == ComputationDevice.Gpu;
bool orderOnHybrid = orderDevice == ComputationDevice.Hybrid;
int scanBatchSize = Math.Max(1, _cliArguments.ScanBatchSize);
int sliceSize = Math.Max(1, _cliArguments.SliceSize);
ulong residueKMax = _cliArguments.ResidueKMax;
@@ -165,7 +168,6 @@ internal static class Program
}
DivisorCycleCache.SetDivisorCyclesBatchSize(cyclesBatchSize);
DivisorCycleCache.Shared.ConfigureGeneratorDevice(useGpuCycles);
// TODO: Keep a single cached block loaded from disk and honor the configured device when
// computing ad-hoc cycles for divisors that fall outside that snapshot instead of queuing
// generation of additional blocks.
@@ -194,13 +196,25 @@ internal static class Program
useOrder: useOrder,
useGpuLucas: mersenneOnGpu,
useGpuScan: mersenneOnGpu,
useGpuOrder: orderOnGpu,
useGpuOrder: orderOnGpu || orderOnHybrid,
useResidue: useResidue,
maxK: residueKMax);
maxK: residueKMax,
orderDevice: orderDevice);
if (!useLucas)
{
Console.WriteLine("Warming up orders");
tester.WarmUpOrders(currentP, orderWarmupLimitOverride ?? 5_000_000UL);
if (orderDevice == ComputationDevice.Hybrid)
{
tester.WarmUpOrdersHybrid(currentP, orderWarmupLimitOverride ?? 5_000_000UL);
}
else if (orderDevice == ComputationDevice.Gpu)
{
tester.WarmUpOrdersGpu(currentP, orderWarmupLimitOverride ?? 5_000_000UL);
}
else
{
tester.WarmUpOrdersCpu(currentP, orderWarmupLimitOverride ?? 5_000_000UL);
}
}
return tester;
@@ -216,7 +230,7 @@ internal static class Program
{
_byDivisorTester = mersenneOnGpu
? new MersenneNumberDivisorByDivisorGpuTester()
: new MersenneNumberDivisorByDivisorCpuTester();
: new MersenneNumberDivisorByDivisorCpuTester(orderDevice);
_byDivisorTester.MinK = _cliArguments.MinK;
_byDivisorTester.BatchSize = scanBatchSize;
}
@@ -245,6 +259,20 @@ internal static class Program
Console.WriteLine("Initialization...");
// Compose a results file name that encodes configuration (before opening file)
string mersenneDeviceLabel = mersenneDevice switch
{
ComputationDevice.Hybrid => "hybrid",
ComputationDevice.Gpu => "gpu",
_ => "cpu",
};
string orderDeviceLabel = orderDevice switch
{
ComputationDevice.Hybrid => "hybrid",
ComputationDevice.Gpu => "gpu",
_ => "cpu",
};
var builtName = CalculationResultsFile.BuildFileName(
useBitTransform,
threadCount,
@@ -257,14 +285,14 @@ internal static class Program
useOrder,
_cliArguments.UseGcdFilter,
NttGpuMath.GpuTransformBackend,
gpuPrimeThreads,
sliceSize,
scanBatchSize,
orderWarmupLimitOverride ?? 5_000_000UL,
NttGpuMath.ReductionMode,
mersenneOnGpu ? "gpu" : "cpu",
_runPrimesOnCpu ? "cpu" : "gpu",
orderOnGpu ? "gpu" : "cpu");
gpuPrimeThreads,
sliceSize,
scanBatchSize,
orderWarmupLimitOverride ?? 5_000_000UL,
NttGpuMath.ReductionMode,
mersenneDeviceLabel,
_runPrimesOnCpu ? "cpu" : "gpu",
orderDeviceLabel);
if (!string.IsNullOrEmpty(_cliArguments.ResultsPrefix))
{
@@ -447,7 +475,18 @@ internal static class Program
if (!useLucas)
{
Console.WriteLine("Warming up orders...");
MersenneTesters.Value!.WarmUpOrders(currentP, orderWarmupLimitOverride ?? 5_000_000UL);
if (orderDevice == ComputationDevice.Hybrid)
{
MersenneTesters.Value!.WarmUpOrdersHybrid(currentP, orderWarmupLimitOverride ?? 5_000_000UL);
}
else if (orderDevice == ComputationDevice.Gpu)
{
MersenneTesters.Value!.WarmUpOrdersGpu(currentP, orderWarmupLimitOverride ?? 5_000_000UL);
}
else
{
MersenneTesters.Value!.WarmUpOrdersCpu(currentP, orderWarmupLimitOverride ?? 5_000_000UL);
}
}
}
@@ -619,7 +658,12 @@ internal static class Program
}
// Fast residue-based composite check for p using small primes
if (!_cliArguments.UseDivisor && !_cliArguments.UseByDivisor && CandidatesCalculator.IsCompositeByResidues(gpu, p))
if (!_cliArguments.UseDivisor && !_cliArguments.UseByDivisor &&
(
(_cliArguments.OrderDevice == ComputationDevice.Gpu && CandidatesCalculator.IsCompositeByResiduesGpu(gpu, p)) ||
(_cliArguments.OrderDevice == ComputationDevice.Hybrid && CandidatesCalculator.IsCompositeByResiduesHybrid(gpu, p)) ||
(_cliArguments.OrderDevice == ComputationDevice.Cpu && CandidatesCalculator.IsCompositeByResiduesCpu(p))
))
{
searchedMersenne = false;
detailedCheck = false;