diff --git a/PerfectNumbers.Core.Tests/Gpu/GpuKernelCompilationTests.cs b/PerfectNumbers.Core.Tests/Gpu/GpuKernelCompilationTests.cs index 0d10462..1a8b1ae 100644 --- a/PerfectNumbers.Core.Tests/Gpu/GpuKernelCompilationTests.cs +++ b/PerfectNumbers.Core.Tests/Gpu/GpuKernelCompilationTests.cs @@ -74,10 +74,6 @@ public class GpuKernelCompilationTests typeof(MersenneNumberDivisorByDivisorGpuTester).GetMethod("GetKernel", BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new InvalidOperationException("MersenneNumberDivisorByDivisorGpuTester.GetKernel not found."); - private static readonly MethodInfo DivisorByDivisorGetExponentKernelMethod = - typeof(MersenneNumberDivisorByDivisorGpuTester).GetMethod("GetStepperKernel", BindingFlags.NonPublic | BindingFlags.Instance) - ?? throw new InvalidOperationException("MersenneNumberDivisorByDivisorGpuTester.GetKernelByPrimeExponent not found."); - private static readonly MethodInfo DivisorTesterGetKernelMethod = typeof(MersenneNumberDivisorGpuTester).GetMethod("GetKernel", BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new InvalidOperationException("MersenneNumberDivisorGpuTester.GetKernel not found."); @@ -149,7 +145,6 @@ public class GpuKernelCompilationTests yield return Loader("NttGpuMath.ForwardKernel", CompileNttForwardKernel); yield return Loader("NttGpuMath.InverseKernel", CompileNttInverseKernel); yield return Loader("MersenneNumberDivisorByDivisorGpuTester.CheckKernel", CompileDivisorByDivisorCheckKernel); - yield return Loader("MersenneNumberDivisorByDivisorGpuTester.EvaluateDivisorWithStepperKernel", CompileDivisorByDivisorExponentKernel); yield return Loader("MersenneNumberDivisorGpuTester.Kernel", CompileDivisorTesterKernel); yield return Loader("MersenneNumberLucasLehmerGpuTester.Kernel", CompileLucasLehmerKernel); yield return Loader("MersenneNumberLucasLehmerGpuTester.AddSmallKernel", CompileLucasLehmerAddSmallKernel); @@ -271,12 +266,6 @@ public class GpuKernelCompilationTests _ = DivisorByDivisorGetKernelMethod.Invoke(tester, new object[] { accelerator }); } - private static void CompileDivisorByDivisorExponentKernel(Accelerator accelerator) - { - var tester = new MersenneNumberDivisorByDivisorGpuTester(); - _ = DivisorByDivisorGetExponentKernelMethod.Invoke(tester, new object[] { accelerator }); - } - private static void CompileDivisorTesterKernel(Accelerator accelerator) { var tester = new MersenneNumberDivisorGpuTester(); diff --git a/PerfectNumbers.Core/Gpu/Kernels/DivisorByDivisorKernels.cs b/PerfectNumbers.Core/Gpu/Kernels/DivisorByDivisorKernels.cs index b5f48ab..accaa5f 100644 --- a/PerfectNumbers.Core/Gpu/Kernels/DivisorByDivisorKernels.cs +++ b/PerfectNumbers.Core/Gpu/Kernels/DivisorByDivisorKernels.cs @@ -2,7 +2,6 @@ using ILGPU; using ILGPU.Algorithms; using ILGPU.Runtime; using PerfectNumbers.Core; -using PerfectNumbers.Core.Gpu; namespace PerfectNumbers.Core.Gpu; @@ -10,77 +9,80 @@ internal static class DivisorByDivisorKernels { public static void CheckKernel( Index1D index, - ArrayView divisors, + ArrayView divisors, + ArrayView exponentOffsets, + ArrayView exponentCounts, ArrayView exponents, + ArrayView divisorCycles, + ArrayView firstCycleRemainders, ArrayView hits, ArrayView firstHit) { int globalIndex = index; - ulong modulus = divisors[globalIndex]; - ulong exponent = exponents[globalIndex]; - ulong result = ULongExtensions.Pow2ModWindowedGpuKernel(exponent, modulus); - byte hit = result == 1UL ? (byte)1 : (byte)0; - hits[globalIndex] = hit; + int count = exponentCounts[globalIndex]; + if (count <= 0) + { + return; + } - if (hit != 0) + int offset = exponentOffsets[globalIndex]; + GpuDivisorPartialData divisor = divisors[globalIndex]; + ulong cycleLength = divisorCycles[globalIndex]; + ulong firstCycleRemainder = firstCycleRemainders[globalIndex]; + + var stepper = new ExponentRemainderStepperGpu(divisor); + + ulong firstExponent = exponents[offset]; + bool firstUnity = stepper.InitializeIsUnityGpu(firstExponent); + + byte firstHitValue; + ulong previousExponent = firstExponent; + ulong cycleRemainder = 0UL; + if (cycleLength == 0UL) + { + firstHitValue = firstUnity ? (byte)1 : (byte)0; + } + else + { + cycleRemainder = firstCycleRemainder; + firstHitValue = cycleRemainder == 0UL && firstUnity ? (byte)1 : (byte)0; + } + + hits[offset] = firstHitValue; + byte anyHit = firstHitValue; + + for (int i = 1; i < count; i++) + { + ulong exponent = exponents[offset + i]; + ulong delta = exponent - previousExponent; + previousExponent = exponent; + + if (cycleLength != 0UL) + { + var remainderValue = new GpuUInt128(cycleRemainder); + remainderValue.AddMod(delta, cycleLength); + cycleRemainder = remainderValue.Low; + + byte hit = 0; + if (cycleRemainder == 0UL) + { + hit = stepper.ComputeNextIsUnityGpu(exponent) ? (byte)1 : (byte)0; + } + + hits[offset + i] = hit; + anyHit |= hit; + } + else + { + byte hit = stepper.ComputeNextIsUnityGpu(exponent) ? (byte)1 : (byte)0; + hits[offset + i] = hit; + anyHit |= hit; + } + } + + if (anyHit != 0 && firstHit.Length > 0) { Atomic.Min(ref firstHit[0], globalIndex); } } - - public static void EvaluateDivisorWithStepperKernel( - Index1D _, - GpuDivisorPartialData divisor, - ulong divisorCycle, - ulong firstCycleRemainder, - ArrayView exponents, - ArrayView hits) - { - int length = (int)exponents.Length; - // EvenPerfectBitScanner always supplies at least one exponent per GPU divisor check, - // so the guard stays commented out to keep the kernel branch-free. - // if (length == 0) - // { - // return; - // } - - var exponentStepper = new ExponentRemainderStepperGpu(divisor); - ulong firstPrime = exponents[0]; - bool firstUnity = exponentStepper.InitializeIsUnityGpu(firstPrime); - // EvenPerfectBitScanner only schedules divisors with non-zero cycle lengths (equal to the tested prime), - // so the no-cycle path remains commented out to keep the kernel branch-free. - // if (divisorCycle == 0UL) - // { - // hits[0] = firstUnity ? (byte)1 : (byte)0; - // for (int i = 1; i < length; i++) - // { - // ulong primeNoCycle = exponents[i]; - // bool unityNoCycle = exponentStepper.ComputeNextIsUnityGpu(primeNoCycle); - // hits[i] = unityNoCycle ? (byte)1 : (byte)0; - // } - // return; - // } - - ulong cycleLength = divisorCycle; - ulong previousPrime = firstPrime; - // The host precomputes the initial cycle remainder to keep the kernel free of modulo operations. - ulong cycleRemainder = firstCycleRemainder; - hits[0] = cycleRemainder == 0UL && firstUnity ? (byte)1 : (byte)0; - - for (int i = 1; i < length; i++) - { - ulong prime = exponents[i]; - ulong delta = prime - previousPrime; - previousPrime = prime; - - var remainderValue = new GpuUInt128(cycleRemainder); - remainderValue.AddMod(delta, cycleLength); - cycleRemainder = remainderValue.Low; - - hits[i] = cycleRemainder == 0UL - ? (exponentStepper.ComputeNextIsUnityGpu(prime) ? (byte)1 : (byte)0) - : (byte)0; - } - } - } diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs index f201995..1a2e728 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs @@ -23,26 +23,14 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv private ulong _divisorLimit; // private bool _isConfigured; - private delegate void StepperKernel(Index1D index, in GpuDivisorPartialData divisor, ulong divisorCycle, ulong firstCycleRemainder, ArrayView exponents, ArrayView hits); - - private readonly ConcurrentDictionary, ArrayView, ArrayView, ArrayView>> _kernelCache = new(); - private readonly ConcurrentDictionary _stepperKernelCache = new(AcceleratorReferenceComparer.Instance); + private readonly ConcurrentDictionary, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>> _kernelCache = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentDictionary> _resourcePools = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentBag _acceleratorPool = new(); private readonly ConcurrentBag _sessionPool = new(); - private Action, ArrayView, ArrayView, ArrayView> GetKernel(Accelerator accelerator) => - _kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel, ArrayView, ArrayView, ArrayView>(DivisorByDivisorKernels.CheckKernel)); - - private StepperKernel GetStepperKernel(Accelerator accelerator) => - _stepperKernelCache.GetOrAdd(accelerator, acc => - { - var kernel = acc.LoadAutoGroupedStreamKernel, ArrayView>(DivisorByDivisorKernels.EvaluateDivisorWithStepperKernel); - return (Index1D extent, in GpuDivisorPartialData divisor, ulong divisorCycle, ulong firstCycleRemainder, ArrayView exponents, ArrayView hits) => - { - kernel(extent, divisor, divisorCycle, firstCycleRemainder, exponents, hits); - }; - }); + private Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> GetKernel(Accelerator accelerator) => + _kernelCache.GetOrAdd(accelerator, acc => + acc.LoadAutoGroupedStreamKernel, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>(DivisorByDivisorKernels.CheckKernel)); public int GpuBatchSize @@ -115,12 +103,21 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv prime, allowedMax, kernel, - resources.DivisorsBuffer, + resources.DivisorDataBuffer, + resources.OffsetBuffer, + resources.CountBuffer, + resources.CycleBuffer, + resources.FirstRemainderBuffer, resources.ExponentBuffer, resources.HitsBuffer, resources.HitIndexBuffer, resources.Divisors, resources.Exponents, + resources.DivisorData, + resources.Offsets, + resources.Counts, + resources.Cycles, + resources.FirstRemainders, out lastProcessed, out coveredRange, out processedCount); @@ -171,18 +168,27 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv private static bool CheckDivisors( ulong prime, ulong allowedMax, - Action, ArrayView, ArrayView, ArrayView> kernel, - MemoryBuffer1D divisorsBuffer, + Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> kernel, + MemoryBuffer1D divisorDataBuffer, + MemoryBuffer1D offsetBuffer, + MemoryBuffer1D countBuffer, + MemoryBuffer1D cycleBuffer, + MemoryBuffer1D firstRemainderBuffer, MemoryBuffer1D exponentBuffer, MemoryBuffer1D hitsBuffer, MemoryBuffer1D hitIndexBuffer, in ulong[] divisors, ulong[] exponents, + GpuDivisorPartialData[] divisorData, + int[] offsets, + int[] counts, + ulong[] cycles, + ulong[] firstRemainders, out ulong lastProcessed, out bool coveredRange, out ulong processedCount) { - int batchCapacity = (int)divisorsBuffer.Length; + int batchCapacity = (int)divisorDataBuffer.Length; bool composite = false; bool processedAll = false; processedCount = 0UL; @@ -228,6 +234,11 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv Span filteredStorage = filteredDivisors.AsSpan(); Span divisorStorage = divisors.AsSpan(); Span exponentStorage = exponents.AsSpan(); + Span divisorDataStorage = divisorData.AsSpan(); + Span offsetStorage = offsets.AsSpan(); + Span countStorage = counts.AsSpan(); + Span cycleStorage = cycles.AsSpan(); + Span remainderStorage = firstRemainders.AsSpan(); while (currentK <= maxK && !composite) { @@ -297,8 +308,14 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv Span divisorSpan = divisorStorage; Span exponentSpan = exponentStorage; + Span divisorDataSpan = divisorDataStorage; + Span offsetSpan = offsetStorage; + Span countSpan = countStorage; + Span cycleSpan = cycleStorage; + Span remainderSpan = remainderStorage; int admissibleCount = 0; + int exponentIndex = 0; for (int i = 0; i < filteredCount; i++) { ulong divisorValue = filteredDivisorsSpan[i]; @@ -310,8 +327,15 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv } divisorSpan[admissibleCount] = divisorValue; - exponentSpan[admissibleCount] = prime; + exponentSpan[exponentIndex] = prime; + divisorDataSpan[admissibleCount] = new GpuDivisorPartialData(divisorValue); + offsetSpan[admissibleCount] = exponentIndex; + countSpan[admissibleCount] = 1; + cycleSpan[admissibleCount] = divisorCycle; + remainderSpan[admissibleCount] = 0UL; + admissibleCount++; + exponentIndex++; } if (admissibleCount == 0) @@ -319,27 +343,34 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv continue; } - Span activeDivisors = divisorSpan[..admissibleCount]; - Span activeExponents = exponentSpan[..admissibleCount]; - ArrayView1D divisorView = divisorsBuffer.View.SubView(0, admissibleCount); - ArrayView1D exponentView = exponentBuffer.View.SubView(0, admissibleCount); - ArrayView1D hitsView = hitsBuffer.View.SubView(0, admissibleCount); + ArrayView1D divisorView = divisorDataBuffer.View.SubView(0, admissibleCount); + ArrayView1D offsetView = offsetBuffer.View.SubView(0, admissibleCount); + ArrayView1D countView = countBuffer.View.SubView(0, admissibleCount); + ArrayView1D cycleView = cycleBuffer.View.SubView(0, admissibleCount); + ArrayView1D remainderView = firstRemainderBuffer.View.SubView(0, admissibleCount); + ArrayView1D exponentView = exponentBuffer.View.SubView(0, exponentIndex); + ArrayView1D hitsView = hitsBuffer.View.SubView(0, exponentIndex); ArrayView1D hitIndexView = hitIndexBuffer.View.SubView(0, 1); - divisorView.CopyFromCPU(ref MemoryMarshal.GetReference(activeDivisors), admissibleCount); - exponentView.CopyFromCPU(ref MemoryMarshal.GetReference(activeExponents), admissibleCount); - int sentinel = int.MaxValue; - hitIndexView.SubView(0, 1).CopyFromCPU(ref sentinel, 1); + divisorView.CopyFromCPU(ref MemoryMarshal.GetReference(divisorDataSpan), admissibleCount); + offsetView.CopyFromCPU(ref MemoryMarshal.GetReference(offsetSpan), admissibleCount); + countView.CopyFromCPU(ref MemoryMarshal.GetReference(countSpan), admissibleCount); + cycleView.CopyFromCPU(ref MemoryMarshal.GetReference(cycleSpan), admissibleCount); + remainderView.CopyFromCPU(ref MemoryMarshal.GetReference(remainderSpan), admissibleCount); + exponentView.CopyFromCPU(ref MemoryMarshal.GetReference(exponentSpan), exponentIndex); - kernel(admissibleCount, divisorView, exponentView, hitsView, hitIndexView); + int sentinel = int.MaxValue; + hitIndexView.CopyFromCPU(ref sentinel, 1); + + kernel(admissibleCount, divisorView, offsetView, countView, exponentView, cycleView, remainderView, hitsView, hitIndexView); int firstHit = sentinel; - hitIndexView.SubView(0, 1).CopyToCPU(ref firstHit, 1); + hitIndexView.CopyToCPU(ref firstHit, 1); int hitIndex = firstHit >= admissibleCount ? -1 : firstHit; bool hitFound = hitIndex >= 0; composite = hitFound; int lastIndex = admissibleCount - 1; - lastProcessed = hitFound ? activeDivisors[hitIndex] : activeDivisors[lastIndex]; + lastProcessed = hitFound ? divisorSpan[hitIndex] : divisorSpan[lastIndex]; } ulongPool.Return(filteredDivisors, clearArray: false); @@ -348,6 +379,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv return composite; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong ResolveDivisorCycle(ulong divisor, ulong prime, in MontgomeryDivisorData divisorData) { @@ -448,7 +480,13 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv private readonly MersenneNumberDivisorByDivisorGpuTester _owner; private readonly GpuContextPool.GpuContextLease _lease; private readonly Accelerator _accelerator; - private StepperKernel _stepperKernel = null!; + private Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> _kernel = null!; + private MemoryBuffer1D _divisorBuffer = null!; + private MemoryBuffer1D _offsetBuffer = null!; + private MemoryBuffer1D _countBuffer = null!; + private MemoryBuffer1D _cycleBuffer = null!; + private MemoryBuffer1D _firstRemainderBuffer = null!; + private MemoryBuffer1D _firstHitBuffer = null!; private MemoryBuffer1D _exponentsBuffer = null!; private MemoryBuffer1D _hitBuffer = null!; private ulong[] _hostBuffer = null!; @@ -470,9 +508,19 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv private void EnsureExecutionResourcesLocked(int requiredCapacity) { - if (_stepperKernel == null) + if (_kernel == null) { - _stepperKernel = _owner.GetStepperKernel(_accelerator); + _kernel = _owner.GetKernel(_accelerator); + } + + if (_divisorBuffer == null) + { + _divisorBuffer = _accelerator.Allocate1D(1); + _offsetBuffer = _accelerator.Allocate1D(1); + _countBuffer = _accelerator.Allocate1D(1); + _cycleBuffer = _accelerator.Allocate1D(1); + _firstRemainderBuffer = _accelerator.Allocate1D(1); + _firstHitBuffer = _accelerator.Allocate1D(1); } bool allocateBuffers = _exponentsBuffer == null; @@ -523,8 +571,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv // return; // } - GpuDivisorPartialData cachedData = new GpuDivisorPartialData(divisor); - ulong cycle = divisorCycle; ulong firstPrime = primes[0]; @@ -545,16 +591,36 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv } } - // Precompute the initial cycle remainder on the host to keep the GPU kernel branch-free. - ulong firstRemainder = firstPrime % cycle; + ulong firstRemainder = cycle == 0UL ? 0UL : firstPrime % cycle; Monitor.Enter(_lease.ExecutionLock); EnsureExecutionResourcesLocked(length); + ArrayView1D divisorView = _divisorBuffer.View; + ArrayView1D offsetView = _offsetBuffer.View; + ArrayView1D countView = _countBuffer.View; + ArrayView1D cycleView = _cycleBuffer.View; + ArrayView1D remainderView = _firstRemainderBuffer.View; + ArrayView1D firstHitView = _firstHitBuffer.View; ArrayView1D exponentView = _exponentsBuffer.View; ArrayView1D hitView = _hitBuffer.View; + GpuDivisorPartialData partialData = new GpuDivisorPartialData(divisor); + divisorView.SubView(0, 1).CopyFromCPU(ref partialData, 1); + + int offsetValue = 0; + offsetView.SubView(0, 1).CopyFromCPU(ref offsetValue, 1); + + int countValue = length; + countView.SubView(0, 1).CopyFromCPU(ref countValue, 1); + + ulong cycleValue = cycle; + cycleView.SubView(0, 1).CopyFromCPU(ref cycleValue, 1); + + ulong remainderValue = firstRemainder; + remainderView.SubView(0, 1).CopyFromCPU(ref remainderValue, 1); + Span hostSpan = _hostBuffer.AsSpan(0, length); primes.CopyTo(hostSpan); @@ -562,16 +628,18 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv exponentSlice.CopyFromCPU(ref MemoryMarshal.GetReference(hostSpan), length); ArrayView1D hitSliceView = hitView.SubView(0, length); - var stepperKernel = _stepperKernel!; - // EnsureExecutionResourcesLocked binds the GPU stepper kernel during the first session launch, so the null check - // remains commented out to avoid a redundant branch on the production path. - // if (stepperKernel is null) - // { - // Monitor.Exit(_lease.ExecutionLock); - // throw new InvalidOperationException("GPU stepper kernel was not initialized."); - // } - stepperKernel(1, in cachedData, cycle, firstRemainder, exponentSlice, hitSliceView); + int sentinel = int.MaxValue; + firstHitView.SubView(0, 1).CopyFromCPU(ref sentinel, 1); + + var kernel = _kernel; + if (kernel is null) + { + Monitor.Exit(_lease.ExecutionLock); + throw new InvalidOperationException("GPU divisor kernel was not initialized."); + } + + kernel(1, divisorView, offsetView, countView, exponentSlice, cycleView, remainderView, hitSliceView, firstHitView); Span hitSlice = hits.Slice(0, length); hitSliceView.CopyToCPU(ref MemoryMarshal.GetReference(hitSlice), length); @@ -591,6 +659,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv } } + public ulong DivisorLimit { get @@ -641,17 +710,38 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv { int actualCapacity = Math.Max(1, capacity); - DivisorsBuffer = accelerator.Allocate1D(actualCapacity); + DivisorDataBuffer = accelerator.Allocate1D(actualCapacity); + OffsetBuffer = accelerator.Allocate1D(actualCapacity); + CountBuffer = accelerator.Allocate1D(actualCapacity); + CycleBuffer = accelerator.Allocate1D(actualCapacity); + FirstRemainderBuffer = accelerator.Allocate1D(actualCapacity); ExponentBuffer = accelerator.Allocate1D(actualCapacity); HitsBuffer = accelerator.Allocate1D(actualCapacity); HitIndexBuffer = accelerator.Allocate1D(1); + ArrayPool ulongPool = ThreadStaticPools.UlongPool; + ArrayPool intPool = ThreadStaticPools.IntPool; + ArrayPool partialPool = ThreadStaticPools.GpuDivisorPartialDataPool; + Divisors = ulongPool.Rent(actualCapacity); Exponents = ulongPool.Rent(actualCapacity); + DivisorData = partialPool.Rent(actualCapacity); + Offsets = intPool.Rent(actualCapacity); + Counts = intPool.Rent(actualCapacity); + Cycles = ulongPool.Rent(actualCapacity); + FirstRemainders = ulongPool.Rent(actualCapacity); Capacity = actualCapacity; } - internal readonly MemoryBuffer1D DivisorsBuffer; + internal readonly MemoryBuffer1D DivisorDataBuffer; + + internal readonly MemoryBuffer1D OffsetBuffer; + + internal readonly MemoryBuffer1D CountBuffer; + + internal readonly MemoryBuffer1D CycleBuffer; + + internal readonly MemoryBuffer1D FirstRemainderBuffer; internal readonly MemoryBuffer1D ExponentBuffer; @@ -663,21 +753,43 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv internal readonly ulong[] Exponents; + internal readonly GpuDivisorPartialData[] DivisorData; + + internal readonly int[] Offsets; + + internal readonly int[] Counts; + + internal readonly ulong[] Cycles; + + internal readonly ulong[] FirstRemainders; + internal readonly int Capacity; public void Dispose() { - DivisorsBuffer.Dispose(); + DivisorDataBuffer.Dispose(); + OffsetBuffer.Dispose(); + CountBuffer.Dispose(); + CycleBuffer.Dispose(); + FirstRemainderBuffer.Dispose(); ExponentBuffer.Dispose(); HitsBuffer.Dispose(); HitIndexBuffer.Dispose(); ArrayPool ulongPool = ThreadStaticPools.UlongPool; + ArrayPool intPool = ThreadStaticPools.IntPool; + ArrayPool partialPool = ThreadStaticPools.GpuDivisorPartialDataPool; ulongPool.Return(Divisors, clearArray: false); ulongPool.Return(Exponents, clearArray: false); + partialPool.Return(DivisorData, clearArray: false); + intPool.Return(Offsets, clearArray: false); + intPool.Return(Counts, clearArray: false); + ulongPool.Return(Cycles, clearArray: false); + ulongPool.Return(FirstRemainders, clearArray: false); } } + private sealed class AcceleratorReferenceComparer : IEqualityComparer { public static AcceleratorReferenceComparer Instance { get; } = new(); diff --git a/PerfectNumbers.Core/ThreadStaticPools.cs b/PerfectNumbers.Core/ThreadStaticPools.cs index 477f8cf..b019cf8 100644 --- a/PerfectNumbers.Core/ThreadStaticPools.cs +++ b/PerfectNumbers.Core/ThreadStaticPools.cs @@ -2,6 +2,7 @@ using System.Buffers; using System.Collections.Generic; using System.Runtime.CompilerServices; using PerfectNumbers.Core.Cpu; +using PerfectNumbers.Core.Gpu; namespace PerfectNumbers.Core { @@ -102,6 +103,18 @@ namespace PerfectNumbers.Core } } + [ThreadStatic] + private static ArrayPool? _gpuDivisorPartialDataPool; + + internal static ArrayPool GpuDivisorPartialDataPool + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return _gpuDivisorPartialDataPool ??= ArrayPool.Create(); + } + } + [ThreadStatic] private static ArrayPool? _intPool;