diff --git a/PerfectNumbers.Core/Gpu/Kernels/DivisorByDivisorKernels.cs b/PerfectNumbers.Core/Gpu/Kernels/DivisorByDivisorKernels.cs index 557a624..4f679c0 100644 --- a/PerfectNumbers.Core/Gpu/Kernels/DivisorByDivisorKernels.cs +++ b/PerfectNumbers.Core/Gpu/Kernels/DivisorByDivisorKernels.cs @@ -8,15 +8,27 @@ namespace PerfectNumbers.Core.Gpu; internal static class DivisorByDivisorKernels { - public static void CheckKernel(Index1D index, ArrayView divisors, ArrayView exponents, ArrayView hits) + public static void CheckKernel( + Index1D index, + ArrayView divisors, + ArrayView exponents, + ArrayView hits, + ArrayView firstHit) { - MontgomeryDivisorData divisor = divisors[index]; + int globalIndex = index; + MontgomeryDivisorData divisor = divisors[globalIndex]; ulong modulus = divisor.Modulus; // The GPU by-divisor pipeline only materializes odd moduli greater than one (q = 2kp + 1), // so the defensive guard for invalid values stays disabled here to keep the kernel branch-free. - ulong exponent = exponents[index]; + ulong exponent = exponents[globalIndex]; ulong montgomeryResult = ULongExtensions.Pow2MontgomeryModWindowedGpuConvertToStandard(divisor, exponent); - hits[index] = montgomeryResult == 1UL ? (byte)1 : (byte)0; + byte hit = montgomeryResult == 1UL ? (byte)1 : (byte)0; + hits[globalIndex] = hit; + + if (hit != 0) + { + Atomic.Min(ref firstHit[0], globalIndex); + } } public static void EvaluateDivisorWithStepperKernel( @@ -173,25 +185,6 @@ internal static class DivisorByDivisorKernels } } - public static void PublishFirstHitKernel( - Index1D index, - ArrayView hits, - ArrayView firstHit) - { - int globalIndex = index; - // EvenPerfectBitScanner launches this auto-grouped kernel with an extent matching the hit view length, - // so the defensive guard stays commented out to keep the hot path branch-free. - // int length = (int)hits.Length; - // if (globalIndex >= length) - // { - // return; - // } - - byte hitValue = hits[globalIndex]; - int candidate = hitValue != 0 ? globalIndex : int.MaxValue; - Atomic.Min(ref firstHit[0], candidate); - } - public static void ConvertMontgomeryResultsToHitsKernel( Index1D index, ArrayView results, diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs index 90df7b0..f651089 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs @@ -23,16 +23,15 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv private ulong _divisorLimit; // private bool _isConfigured; - private readonly ConcurrentDictionary, ArrayView, ArrayView>> _kernelCache = new(); + private readonly ConcurrentDictionary, ArrayView, ArrayView, ArrayView>> _kernelCache = new(); private readonly ConcurrentDictionary, ArrayView>> _stepperKernelCache = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentDictionary, ArrayView, ArrayView, GpuUInt128, GpuUInt128, byte>> _filteredCandidateKernelCache = new(AcceleratorReferenceComparer.Instance); - private readonly ConcurrentDictionary, ArrayView>> _hitDetectionKernelCache = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentDictionary> _resourcePools = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentBag _acceleratorPool = new(); private readonly ConcurrentBag _sessionPool = new(); - private Action, ArrayView, ArrayView> GetKernel(Accelerator accelerator) => - _kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel, ArrayView, ArrayView>(DivisorByDivisorKernels.CheckKernel)); + private Action, ArrayView, ArrayView, ArrayView> GetKernel(Accelerator accelerator) => + _kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel, ArrayView, ArrayView, ArrayView>(DivisorByDivisorKernels.CheckKernel)); private Action, ArrayView> GetStepperKernel(Accelerator accelerator) => _stepperKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel, ArrayView>(DivisorByDivisorKernels.EvaluateDivisorWithStepperKernel)); @@ -42,11 +41,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv accelerator, acc => acc.LoadStreamKernel, ArrayView, ArrayView, GpuUInt128, GpuUInt128, byte>(DivisorByDivisorKernels.GenerateFilteredCandidatesKernel)); - private Action, ArrayView> GetHitDetectionKernel(Accelerator accelerator) => - _hitDetectionKernelCache.GetOrAdd( - accelerator, - acc => acc.LoadAutoGroupedStreamKernel, ArrayView>(DivisorByDivisorKernels.PublishFirstHitKernel)); - public int GpuBatchSize { get => _gpuBatchSize; @@ -126,7 +120,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv resources.NextStartBuffer, resources.FilterCountBuffer, resources.FilteredCandidateKernel, - resources.HitDetectionKernel, resources.Divisors, resources.Exponents, resources.DivisorData, @@ -183,7 +176,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv ulong prime, ulong allowedMax, Accelerator accelerator, - Action, ArrayView, ArrayView> kernel, + Action, ArrayView, ArrayView, ArrayView> kernel, MemoryBuffer1D divisorsBuffer, MemoryBuffer1D exponentBuffer, MemoryBuffer1D hitsBuffer, @@ -192,7 +185,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv MemoryBuffer1D nextStartBuffer, MemoryBuffer1D filterCountBuffer, Action, ArrayView, ArrayView, GpuUInt128, GpuUInt128, byte> filteredCandidateKernel, - Action, ArrayView> hitDetectionKernel, in ulong[] divisors, ulong[] exponents, MontgomeryDivisorData[] divisorData, @@ -336,9 +328,14 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv divisorView.CopyFromCPU(ref MemoryMarshal.GetReference(divisorDataSpan), admissibleCount); exponentView.CopyFromCPU(ref MemoryMarshal.GetReference(exponentSpan), admissibleCount); - kernel(admissibleCount, divisorView, exponentView, hitsView); + int sentinel = int.MaxValue; + hitIndexView.SubView(0, 1).CopyFromCPU(ref sentinel, 1); - int hitIndex = FindFirstHitOnGpu(admissibleCount, hitsView, hitIndexView, hitDetectionKernel); + kernel(admissibleCount, divisorView, exponentView, hitsView, hitIndexView); + + int firstHit = sentinel; + hitIndexView.SubView(0, 1).CopyToCPU(ref firstHit, 1); + int hitIndex = firstHit >= admissibleCount ? -1 : firstHit; bool hitFound = hitIndex >= 0; composite = hitFound; int lastIndex = admissibleCount - 1; @@ -352,9 +349,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GenerateFilteredCandidatesOnGpu( int count, @@ -390,29 +384,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv return filteredCount; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int FindFirstHitOnGpu( - int count, - ArrayView1D hitsView, - ArrayView1D hitIndexView, - Action, ArrayView> hitKernel) - { - // EvenPerfectBitScanner only calls this helper with positive counts, - // so the guard stays commented out to keep the hot path branch-free. - // if (count <= 0) - // { - // return -1; - // } - - int sentinel = int.MaxValue; - hitIndexView.SubView(0, 1).CopyFromCPU(ref sentinel, 1); - - hitKernel(count, hitsView, hitIndexView); - - int firstHit = sentinel; - hitIndexView.SubView(0, 1).CopyToCPU(ref firstHit, 1); - return firstHit >= count ? -1 : firstHit; - } @@ -660,7 +631,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv DivisorData = ThreadStaticPools.MontgomeryDivisorDataPool.Rent(actualCapacity); CycleLengths = ulongPool.Rent(cycleCapacity); FilteredCandidateKernel = owner.GetFilteredCandidateKernel(accelerator); - HitDetectionKernel = owner.GetHitDetectionKernel(accelerator); Capacity = actualCapacity; CycleCapacity = cycleCapacity; } @@ -689,8 +659,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv internal readonly Action, ArrayView, ArrayView, GpuUInt128, GpuUInt128, byte> FilteredCandidateKernel; - internal readonly Action, ArrayView> HitDetectionKernel; - internal readonly int Capacity; internal readonly int CycleCapacity;