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(); + } + } }