diff --git a/.vscode/launch.json b/.vscode/launch.json
index 5e38301..06c5943 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -7,7 +7,8 @@
"description": "Select project to run",
"options": [
"EvenPerfectBitScanner",
- "EvenPerfectBitScanner.ResultsParser"
+ "EvenPerfectBitScanner.ResultsParser",
+ "EvenPerfectBitScanner.Benchmarks"
],
"default": "EvenPerfectBitScanner"
}
@@ -20,7 +21,7 @@
"preLaunchTask": "Build Debug",
"program": "${workspaceFolder}/${input:projectName}/bin/Debug/net8.0/${input:projectName}.dll",
"args": [
- "--increment=add --mersenne=residue --threads=128 --gpu-scan-batch=512000 --primes-device=gpu --mersenne-device=gpu --mod-reduction=barrett128 --block-size=10000000 --gpu-prime-threads=32 --write-batch-size=10000 --divisor-cycles-device=gpu"
+ "--increment=add --mersenne=residue --threads=128 --gpu-scan-batch=512000 --primes-device=gpu --mersenne-device=gpu --mod-reduction=barrett128 --block-size=10000000 --gpu-prime-threads=32 --write-batch-size=10000 --divisor-cycles-device=gpu"
],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
@@ -93,6 +94,33 @@
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"justMyCode": false
+ },
+ {
+ "name": "Run EvenPerfectBitScanner.Benchmarks",
+ "type": "coreclr",
+ "request": "launch",
+ "noDebug": true,
+ "preLaunchTask": "Build EvenPerfectBitScanner.Benchmarks Release",
+ "program": "${workspaceFolder}/EvenPerfectBitScanner.Benchmarks/bin/Release/net8.0/EvenPerfectBitScanner.Benchmarks.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "integratedTerminal",
+ "internalConsoleOptions": "neverOpen",
+ "justMyCode": false
+ },
+ {
+ "name": "Debug EvenPerfectBitScanner.Benchmarks",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "Build EvenPerfectBitScanner.Benchmarks Debug",
+ "program": "${workspaceFolder}/EvenPerfectBitScanner.Benchmarks/bin/Debug/net8.0/EvenPerfectBitScanner.Benchmarks.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "integratedTerminal",
+ "internalConsoleOptions": "neverOpen",
+ "justMyCode": false
}
]
-}
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 3af1b71..90e6505 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -1,66 +1,66 @@
{
- "version": "2.0.0",
- "tasks": [
- {
- "label": "Build Debug",
- "command": "dotnet",
- "type": "process",
- "args": [
- "build",
- "${workspaceFolder}/EvenPerfectScanner.sln",
- "--configuration",
- "Debug",
- "--framework",
- "net8.0"
- ],
- "group": {
- "kind": "build",
- "isDefault": false
- },
- "problemMatcher": "$msCompile"
- },
- {
- "label": "Build Release",
- "command": "dotnet",
- "type": "process",
- "args": [
- "build",
- "${workspaceFolder}/EvenPerfectScanner.sln",
- "--configuration",
- "Release",
- "--framework",
- "net8.0"
- ],
- "group": "build",
- "problemMatcher": "$msCompile"
- },
- {
- "label": "Build EvenPerfectBitScanner Debug",
- "command": "dotnet",
- "type": "process",
- "args": [
- "build",
- "${workspaceFolder}/EvenPerfectBitScanner/EvenPerfectBitScanner.csproj",
- "--configuration",
- "Debug",
- "--framework",
- "net8.0"
- ],
- "group": "build",
- "problemMatcher": "$msCompile"
- },
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Build Debug",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/EvenPerfectScanner.sln",
+ "--configuration",
+ "Debug",
+ "--framework",
+ "net8.0"
+ ],
+ "group": {
+ "kind": "build",
+ "isDefault": false
+ },
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "Build Release",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/EvenPerfectScanner.sln",
+ "--configuration",
+ "Release",
+ "--framework",
+ "net8.0"
+ ],
+ "group": "build",
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "Build EvenPerfectBitScanner Debug",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/EvenPerfectBitScanner/EvenPerfectBitScanner.csproj",
+ "--configuration",
+ "Debug",
+ "--framework",
+ "net8.0"
+ ],
+ "group": "build",
+ "problemMatcher": "$msCompile"
+ },
{
"label": "Build EvenPerfectBitScanner Release",
"command": "dotnet",
"type": "process",
- "args": [
- "build",
- "${workspaceFolder}/EvenPerfectBitScanner/EvenPerfectBitScanner.csproj",
- "--configuration",
- "Release",
- "--framework",
- "net8.0"
- ],
+ "args": [
+ "build",
+ "${workspaceFolder}/EvenPerfectBitScanner/EvenPerfectBitScanner.csproj",
+ "--configuration",
+ "Release",
+ "--framework",
+ "net8.0"
+ ],
"group": "build",
"problemMatcher": "$msCompile"
},
@@ -97,16 +97,16 @@
{
"label": "Run EvenPerfectBitScanner",
"type": "process",
- "command": "dotnet",
+ "command": "dotnet",
"args": [
"run",
"--project",
"${workspaceFolder}/EvenPerfectBitScanner/EvenPerfectBitScanner.csproj",
- "--configuration",
- "Release",
- "--framework",
- "net8.0"
- ],
+ "--configuration",
+ "Release",
+ "--framework",
+ "net8.0"
+ ],
"problemMatcher": []
},
{
@@ -124,18 +124,63 @@
],
"problemMatcher": []
},
- {
- "label": "Run Release (no debug)",
- "type": "process",
- "command": "dotnet",
- "args": [
- "run",
- "--configuration",
- "Release",
- "--framework",
- "net8.0"
- ],
- "problemMatcher": []
- }
- ]
-}
+ {
+ "label": "Run Release (no debug)",
+ "type": "process",
+ "command": "dotnet",
+ "args": [
+ "run",
+ "--configuration",
+ "Release",
+ "--framework",
+ "net8.0"
+ ],
+ "problemMatcher": []
+ },
+ {
+ "label": "Build EvenPerfectBitScanner.Benchmarks Debug",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/EvenPerfectBitScanner.Benchmarks/EvenPerfectBitScanner.Benchmarks.csproj",
+ "--configuration",
+ "Debug",
+ "--framework",
+ "net8.0"
+ ],
+ "group": "build",
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "Build EvenPerfectBitScanner.Benchmarks Release",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/EvenPerfectBitScanner.Benchmarks/EvenPerfectBitScanner.Benchmarks.csproj",
+ "--configuration",
+ "Release",
+ "--framework",
+ "net8.0"
+ ],
+ "group": "build",
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "Run EvenPerfectBitScanner.Benchmarks",
+ "type": "process",
+ "command": "dotnet",
+ "args": [
+ "run",
+ "--project",
+ "${workspaceFolder}/EvenPerfectBitScanner.Benchmarks/EvenPerfectBitScanner.Benchmarks.csproj",
+ "--configuration",
+ "Release",
+ "--framework",
+ "net8.0"
+ ],
+ "problemMatcher": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/EvenPerfectBitScanner.Benchmarks/EvenPerfectBitScanner.Benchmarks.csproj b/EvenPerfectBitScanner.Benchmarks/EvenPerfectBitScanner.Benchmarks.csproj
new file mode 100644
index 0000000..1c345e4
--- /dev/null
+++ b/EvenPerfectBitScanner.Benchmarks/EvenPerfectBitScanner.Benchmarks.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/EvenPerfectBitScanner.Benchmarks/Program.cs b/EvenPerfectBitScanner.Benchmarks/Program.cs
new file mode 100644
index 0000000..7779cdc
--- /dev/null
+++ b/EvenPerfectBitScanner.Benchmarks/Program.cs
@@ -0,0 +1,12 @@
+using BenchmarkDotNet.Running;
+
+namespace EvenPerfectBitScanner.Benchmarks;
+
+public static class Program
+{
+ public static void Main(string[] args)
+ {
+ BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
+ }
+}
+
diff --git a/EvenPerfectBitScanner.Benchmarks/ResidueComputationBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/ResidueComputationBenchmarks.cs
new file mode 100644
index 0000000..8e49fc0
--- /dev/null
+++ b/EvenPerfectBitScanner.Benchmarks/ResidueComputationBenchmarks.cs
@@ -0,0 +1,36 @@
+using System.Numerics;
+using BenchmarkDotNet.Attributes;
+using PerfectNumbers.Core;
+
+namespace EvenPerfectBitScanner.Benchmarks;
+
+[MemoryDiagnoser]
+public class ResidueComputationBenchmarks
+{
+ [Params(3UL, 8191UL, 131071UL, 2147483647UL)]
+ public ulong Exponent { get; set; }
+
+ [Benchmark(Baseline = true)]
+ public (ulong Step10, ulong Step8, ulong Step3, ulong Step5) LegacyModulo()
+ {
+ ulong exponent = Exponent;
+ ulong step10 = ((exponent % 10UL) << 1) % 10UL;
+ ulong step8 = ((exponent & 7UL) << 1) & 7UL;
+ ulong step3 = ((exponent % 3UL) << 1) % 3UL;
+ ulong step5 = ((exponent % 5UL) << 1) % 5UL;
+ return (step10, step8, step3, step5);
+ }
+
+ [Benchmark]
+ public (ulong Step10, ulong Step8, ulong Step3, ulong Step5) ModMethodModulo()
+ {
+ UInt128 exponent128 = Exponent;
+ exponent128.Mod10_8_5_3(out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3);
+ ulong step10 = (mod10 << 1) % 10UL;
+ ulong step8 = (mod8 << 1) & 7UL;
+ ulong step3 = (mod3 << 1) % 3UL;
+ ulong step5 = (mod5 << 1) % 5UL;
+ return (step10, step8, step3, step5);
+ }
+}
+
diff --git a/EvenPerfectScanner.sln b/EvenPerfectScanner.sln
index 75deba3..6ec9da2 100644
--- a/EvenPerfectScanner.sln
+++ b/EvenPerfectScanner.sln
@@ -12,6 +12,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EvenPerfectBitScanner.Tests
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EvenPerfectBitScanner.ResultsParser", "EvenPerfectBitScanner.ResultsParser\EvenPerfectBitScanner.ResultsParser.csproj", "{B07A718C-A71C-410D-A0D7-FA1674CEB38F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EvenPerfectBitScanner.Benchmarks", "EvenPerfectBitScanner.Benchmarks\EvenPerfectBitScanner.Benchmarks.csproj", "{83551FCC-41FA-4162-B541-AFCB5666A29F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -41,5 +43,9 @@ Global
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {83551FCC-41FA-4162-B541-AFCB5666A29F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {83551FCC-41FA-4162-B541-AFCB5666A29F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {83551FCC-41FA-4162-B541-AFCB5666A29F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {83551FCC-41FA-4162-B541-AFCB5666A29F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/PerfectNumbers.Core/Gpu/GpuKernelPool.cs b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs
index f73a69a..de8b935 100644
--- a/PerfectNumbers.Core/Gpu/GpuKernelPool.cs
+++ b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs
@@ -158,7 +158,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
}
}
- public readonly Action, ArrayView1D> IncrementalOrderKernel
+ public readonly Action, ArrayView1D> IncrementalOrderKernel
{
get
{
@@ -175,7 +175,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
}
}
- public readonly Action, ArrayView1D> Pow2ModOrderKernel
+ public readonly Action, ArrayView1D> Pow2ModOrderKernel
{
get
{
@@ -486,7 +486,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
}
private static void IncrementalOrderKernelScan(Index1D index, ulong exponent, GpuUInt128 twoP, GpuUInt128 kStart, byte lastIsSeven, ulong divMul,
- ResidueAutomatonArgs ra, ArrayView found, ArrayView1D smallCycles)
+ ResidueAutomatonArgs ra, ArrayView found, ArrayView1D smallCycles)
{
ulong idx = (ulong)index.X;
ulong r10 = ra.Q0M10 + (ra.Step10 * idx) % 10UL; r10 -= (r10 >= 10UL) ? 10UL : 0UL;
@@ -570,7 +570,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
}
private static void Pow2ModOrderKernelScan(Index1D index, ulong exponent, GpuUInt128 twoP, GpuUInt128 kStart, byte lastIsSeven, ulong _,
- ResidueAutomatonArgs ra, ArrayView found, ArrayView1D smallCycles)
+ ResidueAutomatonArgs ra, ArrayView found, ArrayView1D smallCycles)
{
ulong idx = (ulong)index.X;
ulong r10 = ra.Q0M10 + (ra.Step10 * idx) % 10UL; r10 -= (r10 >= 10UL) ? 10UL : 0UL;
diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs
index 6058b26..861264a 100644
--- a/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs
+++ b/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs
@@ -23,10 +23,12 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
var pow2Kernel = gpuLease.Pow2ModKernel;
var incKernel = gpuLease.IncrementalKernel;
- ulong step10 = ((exponent % 10UL) << 1) % 10UL;
- ulong step8 = ((exponent & 7UL) << 1) & 7UL;
- ulong step3 = ((exponent % 3UL) << 1) % 3UL;
- ulong step5 = ((exponent % 5UL) << 1) % 5UL;
+ UInt128 exponent128 = exponent;
+ exponent128.Mod10_8_5_3(out ulong exponentMod10, out ulong exponentMod8, out ulong exponentMod5, out ulong exponentMod3);
+ ulong step10 = (exponentMod10 << 1) % 10UL;
+ ulong step8 = (exponentMod8 << 1) & 7UL;
+ ulong step3 = (exponentMod3 << 1) % 3UL;
+ ulong step5 = (exponentMod5 << 1) % 5UL;
GpuUInt128 twoPGpu = (GpuUInt128)twoP;
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
ResiduePrimeViews primeViews = default;
@@ -55,9 +57,9 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
if (_kernelType == GpuKernelType.Pow2Mod)
{
q0.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
- var ra = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
+ var kernelArgs = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
pow2Kernel(stream, currentSize, exponent, twoPGpu, (GpuUInt128)kStart, last, divMul,
- ra, orderBuffer.View, smallCyclesView, primeViews.LastOne, primeViews.LastSeven,
+ kernelArgs, orderBuffer.View, smallCyclesView, primeViews.LastOne, primeViews.LastSeven,
primeViews.LastOnePow2, primeViews.LastSevenPow2);
}
else
diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs
index d0eda4e..9ab8350 100644
--- a/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs
+++ b/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs
@@ -25,6 +25,12 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
};
var foundBuffer = accelerator.Allocate1D(1);
+ UInt128 exponent128 = exponent;
+ exponent128.Mod10_8_5_3(out ulong exponentMod10, out ulong exponentMod8, out ulong exponentMod5, out ulong exponentMod3);
+ ulong step10 = (exponentMod10 << 1) % 10UL;
+ ulong step8 = (exponentMod8 << 1) & 7UL;
+ ulong step3 = (exponentMod3 << 1) % 3UL;
+ ulong step5 = (exponentMod5 << 1) % 5UL;
try
{
UInt128 remaining;
@@ -37,21 +43,14 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
foundBuffer.MemSetToZero();
// Precompute residue automaton bases for this batch
UInt128 q0 = twoP * kStart + 1UL;
- ulong q0m10 = q0.Mod10();
- ulong q0m8 = q0.Mod8();
- ulong q0m3 = q0.Mod3();
- ulong q0m5 = q0.Mod5();
- ulong step10 = ((exponent % 10UL) << 1) % 10UL;
- ulong step8 = ((exponent & 7UL) << 1) & 7UL;
- ulong step3 = ((exponent % 3UL) << 1) % 3UL;
- ulong step5 = ((exponent % 5UL) << 1) % 5UL;
- var ra = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
+ q0.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
+ var kernelArgs = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
// Ensure device has small cycles table for early rejections in order kernels
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
kernel(stream, currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, divMul,
- ra, foundBuffer.View, smallCyclesView);
+ kernelArgs, foundBuffer.View, smallCyclesView);
stream.Synchronize();
foundBuffer.View.CopyToCPU(ref found, 1);
diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs
index 5de5086..6aeefaf 100644
--- a/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs
+++ b/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs
@@ -20,74 +20,89 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
// Ensure device has small cycles and primes tables for in-kernel lookup
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
ResiduePrimeViews primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(accelerator);
- int batchSize = GpuConstants.ScanBatchSize;
- UInt128 kStart = 1UL;
- byte last = lastIsSeven ? (byte)1 : (byte)0;
- var kernel = gpuLease.Pow2ModKernel;
- ulong step10 = ((exponent % 10UL) << 1) % 10UL;
- ulong step8 = ((exponent & 7UL) << 1) & 7UL;
- ulong step3 = ((exponent % 3UL) << 1) % 3UL;
- ulong step5 = ((exponent % 5UL) << 1) % 5UL;
- GpuUInt128 twoPGpu = (GpuUInt128)twoP;
+ int batchSize = GpuConstants.ScanBatchSize;
+ UInt128 kStart = 1UL;
+ byte last = lastIsSeven ? (byte)1 : (byte)0;
+ var kernel = gpuLease.Pow2ModKernel;
+ UInt128 exponent128 = exponent;
+ exponent128.Mod10_8_5_3(out ulong exponentMod10, out ulong exponentMod8, out ulong exponentMod5, out ulong exponentMod3);
+ ulong step10 = (exponentMod10 << 1) % 10UL;
+ ulong step8 = (exponentMod8 << 1) & 7UL;
+ ulong step3 = (exponentMod3 << 1) % 3UL;
+ ulong step5 = (exponentMod5 << 1) % 5UL;
+ GpuUInt128 twoPGpu = (GpuUInt128)twoP;
- var orderBuffer = accelerator.Allocate1D(batchSize);
- // Device memory returned by Allocate1D is not guaranteed to be zeroed.
- // Ensure the buffer starts with a known state so that lanes skipped by the
- // kernel (for example due to early rejections) do not leave stale garbage
- // values that would be interpreted as composite witnesses when copied back
- // to the host. This mirrors the explicit zeroing performed by other GPU
- // testers such as the order kernels.
- orderBuffer.MemSetToZero();
- ulong[] orderArray = ArrayPool.Shared.Rent(batchSize);
- UInt128 remaining;
- int currentSize;
- int i;
- UInt128 batchSize128 = (UInt128)batchSize, q;
- Span orders = orderArray.AsSpan(0, batchSize);
- ref var ordersRef = ref MemoryMarshal.GetReference(orders);
- while (kStart < maxK && Volatile.Read(ref isPrime))
- {
- remaining = maxK - kStart;
- if (remaining > batchSize128)
- {
- currentSize = batchSize;
- }
- else
- {
- currentSize = (int)remaining;
- orders = orderArray.AsSpan(0, currentSize);
- ordersRef = ref MemoryMarshal.GetReference(orders);
- }
+ var orderBuffer = accelerator.Allocate1D(batchSize);
+ // Device memory returned by Allocate1D is not guaranteed to be zeroed.
+ // Ensure the buffer starts with a known state so that lanes skipped by the
+ // kernel (for example due to early rejections) do not leave stale garbage
+ // values that would be interpreted as composite witnesses when copied back
+ // to the host. This mirrors the explicit zeroing performed by other GPU
+ // testers such as the order kernels.
+ orderBuffer.MemSetToZero();
+ ulong[] orderArray = ArrayPool.Shared.Rent(batchSize);
+ UInt128 batchSize128 = (UInt128)batchSize;
+ UInt128 fullBatchStep = twoP * batchSize128;
+ UInt128 q = twoP * kStart + UInt128.One;
- q = twoP * kStart + UInt128.One;
- q.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
- var ra = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
+ try
+ {
+ while (kStart < maxK && Volatile.Read(ref isPrime))
+ {
+ UInt128 remaining = maxK - kStart;
+ int currentSize = remaining > batchSize128 ? batchSize : (int)remaining;
+ Span orders = orderArray.AsSpan(0, currentSize);
+ ref ulong ordersRef = ref MemoryMarshal.GetReference(orders);
- kernel(stream, currentSize, exponent, twoPGpu, (GpuUInt128)kStart, last, 0UL,
- ra, orderBuffer.View, smallCyclesView, primeViews.LastOne, primeViews.LastSeven, primeViews.LastOnePow2, primeViews.LastSevenPow2);
+ q.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
+ var kernelArgs = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
- accelerator.Synchronize();
- orderBuffer.View.CopyToCPU(ref ordersRef, currentSize);
- if (!Volatile.Read(ref isPrime))
- {
- break;
- }
+ kernel(stream, currentSize, exponent, twoPGpu, (GpuUInt128)kStart, last, 0UL,
+ kernelArgs, orderBuffer.View, smallCyclesView, primeViews.LastOne, primeViews.LastSeven, primeViews.LastOnePow2, primeViews.LastSevenPow2);
- for (i = 0; i < currentSize; i++)
- {
- if (orders[i] != 0UL)
- {
- Volatile.Write(ref isPrime, false);
- goto cleanup;
- }
- }
+ accelerator.Synchronize();
+ orderBuffer.View.CopyToCPU(ref ordersRef, currentSize);
+ if (!Volatile.Read(ref isPrime))
+ {
+ break;
+ }
- kStart += batchSize128;
- }
+ bool compositeFound = false;
+ for (int i = 0; i < currentSize; i++)
+ {
+ if (orders[i] != 0UL)
+ {
+ Volatile.Write(ref isPrime, false);
+ compositeFound = true;
+ break;
+ }
+ }
- cleanup:
- ArrayPool.Shared.Return(orderArray);
- orderBuffer.Dispose();
- gpuLease.Dispose();
- }
+ if (compositeFound)
+ {
+ break;
+ }
+
+ UInt128 processed = (UInt128)currentSize;
+ kStart += processed;
+ if (kStart < maxK)
+ {
+ if (processed == batchSize128)
+ {
+ q += fullBatchStep;
+ }
+ else
+ {
+ q += twoP * processed;
+ }
+ }
+ }
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(orderArray);
+ orderBuffer.Dispose();
+ gpuLease.Dispose();
+ }
+ }
}