From ad3effa5fa98e3108ad9ed7285d82d0f6f055ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Thu, 25 Sep 2025 02:48:14 +0200 Subject: [PATCH] Throttle GPU cycle generation batches --- PerfectNumbers.Core/DivisorCycleCache.cs | 38 +++++++++++++++++------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/PerfectNumbers.Core/DivisorCycleCache.cs b/PerfectNumbers.Core/DivisorCycleCache.cs index 15e6316..34cac64 100644 --- a/PerfectNumbers.Core/DivisorCycleCache.cs +++ b/PerfectNumbers.Core/DivisorCycleCache.cs @@ -2,6 +2,7 @@ using System; using System.Buffers; using System.Collections.Concurrent; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading.Tasks; using ILGPU; using ILGPU.Runtime; @@ -67,6 +68,8 @@ public sealed class DivisorCycleCache } } + private const int CycleGenerationBatchSize = 262_144; + private readonly object _sync = new(); private readonly ConcurrentDictionary> _pending = new(); private readonly ConcurrentDictionary, ArrayView1D>> _gpuKernelCache = new(AcceleratorReferenceComparer.Instance); @@ -328,22 +331,35 @@ public sealed class DivisorCycleCache Action, ArrayView1D> kernel = _gpuKernelCache.GetOrAdd(accelerator, LoadKernel); int length = destination.Length; - ulong[] divisors = ArrayPool.Shared.Rent(length); + int chunkCapacity = Math.Min(length, CycleGenerationBatchSize); + ulong[] divisors = ArrayPool.Shared.Rent(chunkCapacity); try { - for (int i = 0; i < length; i++) + using MemoryBuffer1D divisorBuffer = accelerator.Allocate1D(chunkCapacity); + using MemoryBuffer1D resultBuffer = accelerator.Allocate1D(chunkCapacity); + + int offset = 0; + while (offset < length) { - divisors[i] = start + (ulong)i; + int batchSize = Math.Min(chunkCapacity, length - offset); + Span divisorsSpan = divisors.AsSpan(0, batchSize); + for (int i = 0; i < batchSize; i++) + { + divisorsSpan[i] = start + (ulong)(offset + i); + } + + ArrayView1D divisorView = divisorBuffer.View.SubView(0, batchSize); + ArrayView1D resultView = resultBuffer.View.SubView(0, batchSize); + + divisorView.CopyFromCPU(ref MemoryMarshal.GetReference(divisorsSpan), batchSize); + kernel(batchSize, divisorView, resultView); + accelerator.Synchronize(); + + Span destinationSpan = destination.AsSpan(offset, batchSize); + resultView.CopyToCPU(ref MemoryMarshal.GetReference(destinationSpan), batchSize); + offset += batchSize; } - - using MemoryBuffer1D divisorBuffer = accelerator.Allocate1D(length); - using MemoryBuffer1D resultBuffer = accelerator.Allocate1D(length); - - divisorBuffer.View.CopyFromCPU(ref divisors[0], length); - kernel(length, divisorBuffer.View, resultBuffer.View); - accelerator.Synchronize(); - resultBuffer.View.CopyToCPU(ref destination[0], length); } finally {