diff --git a/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs b/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs index 1e9e937..7e9d05d 100644 --- a/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs +++ b/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs @@ -3,6 +3,7 @@ using System.Buffers; using System.Collections.Concurrent; using System.Collections.Generic; using System.Numerics; +using System.Threading; using PerfectNumbers.Core; using PerfectNumbers.Core.Gpu; @@ -10,14 +11,11 @@ namespace PerfectNumbers.Core.Cpu; public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDivisorByDivisorTester { - private readonly object _sync = new(); private readonly ConcurrentBag _sessionPool = new(); private ulong _divisorLimit; - private ulong _lastStatusDivisor; + private int _lastStatusDivisor; private bool _isConfigured; private int _batchSize = 1_024; - - private ulong _configuredMaxPrime; private ByDivisorDeltasDevice _deltasDevice = ByDivisorDeltasDevice.Cpu; private ByDivisorMontgomeryDevice _montgomeryDevice = ByDivisorMontgomeryDevice.Cpu; @@ -34,80 +32,55 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv public ByDivisorDeltasDevice DeltasDevice { get => _deltasDevice; - set - { - lock (_sync) - { - _deltasDevice = value; - } - } + set => _deltasDevice = value; } public ByDivisorMontgomeryDevice MontgomeryDevice { get => _montgomeryDevice; - set - { - lock (_sync) - { - _montgomeryDevice = value; - } - } + set => _montgomeryDevice = value; } public void ConfigureFromMaxPrime(ulong maxPrime) { - lock (_sync) - { - _configuredMaxPrime = maxPrime; - _divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime); - _lastStatusDivisor = 0UL; - _isConfigured = true; - } + _divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime); + _lastStatusDivisor = 0; + _isConfigured = true; } public ulong DivisorLimit { get { - lock (_sync) + if (!_isConfigured) { - if (!_isConfigured) - { - throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester."); - } - - return _divisorLimit; + throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester."); } + + return _divisorLimit; } } public ulong GetAllowedMaxDivisor(ulong prime) { - lock (_sync) + if (!_isConfigured) { - if (!_isConfigured) - { - throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester."); - } - - return ComputeAllowedMaxDivisor(prime, _divisorLimit); + throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester."); } + + return ComputeAllowedMaxDivisor(prime, _divisorLimit); } public bool IsPrime(ulong prime, out bool divisorsExhausted, TimeSpan? timeLimit = null) { - ulong allowedMax; - lock (_sync) + if (!_isConfigured) { - if (!_isConfigured) - { - throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester."); - } - - allowedMax = ComputeAllowedMaxDivisor(prime, _divisorLimit); + throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester."); } + ulong divisorLimit = _divisorLimit; + ulong allowedMax = ComputeAllowedMaxDivisor(prime, divisorLimit); + if (allowedMax < 3UL) { divisorsExhausted = true; @@ -128,10 +101,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv if (processedCount > 0UL) { - lock (_sync) - { - UpdateStatusUnsafe(processedCount); - } + UpdateStatusUnsafe(processedCount); } if (composite) @@ -603,18 +573,34 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv return; } - ulong interval = PerfectNumberConstants.ConsoleInterval; + int interval = PerfectNumberConstants.ConsoleInterval; // PerfectNumberConstants.ConsoleInterval is a positive constant, so this guard never triggers on EvenPerfectBitScanner. - // if (interval == 0UL) + // if (interval <= 0) // { - // _lastStatusDivisor = 0UL; + // Volatile.Write(ref _lastStatusDivisor, 0); // return; // } - ulong total = _lastStatusDivisor + processedCount; - // TODO: Replace this modulo with the ring-buffer style counter (subtract loop) used in the fast CLI - // status benchmarks so we avoid `%` in this hot loop while still wrapping progress correctly. - _lastStatusDivisor = total % interval; + int processedDelta = (int)(processedCount % (uint)interval); + if (processedDelta == 0) + { + return; + } + + while (true) + { + int previous = Volatile.Read(ref _lastStatusDivisor); + int total = previous + processedDelta; + if (total >= interval) + { + total -= interval; + } + + if (previous == Interlocked.CompareExchange(ref _lastStatusDivisor, total, previous)) + { + return; + } + } } private static ulong ComputeDivisorLimitFromMaxPrime(ulong maxPrime) diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs index 5fc08b4..b3aa033 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs @@ -19,7 +19,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv private const int RemainderSlotCount = 4; private int _gpuBatchSize = GpuConstants.ScanBatchSize; - private readonly object _sync = new(); private ulong _divisorLimit; private bool _isConfigured; @@ -151,18 +150,12 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv throw new ArgumentException("allowedMaxValues span must be at least as long as primes span.", nameof(allowedMaxValues)); } - ulong divisorLimit; - - lock (_sync) + if (!_isConfigured) { - if (!_isConfigured) - { - throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester."); - } - - divisorLimit = _divisorLimit; + throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester."); } + ulong divisorLimit = _divisorLimit; for (int index = 0; index < primes.Length; index++) { allowedMaxValues[index] = ComputeAllowedMaxDivisor(primes[index], divisorLimit); @@ -943,34 +936,28 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv public ulong GetAllowedMaxDivisor(ulong prime) { - lock (_sync) + if (!_isConfigured) { - if (!_isConfigured) - { - throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester."); - } - - return ComputeAllowedMaxDivisor(prime, _divisorLimit); + throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester."); } + + return ComputeAllowedMaxDivisor(prime, _divisorLimit); } public DivisorScanSession CreateDivisorSession() { - lock (_sync) + if (!_isConfigured) { - if (!_isConfigured) - { - throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester."); - } - - if (_sessionPool.TryTake(out DivisorScanSession? session)) - { - session.Reset(); - return session; - } - - return new DivisorScanSession(this); + throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester."); } + + if (_sessionPool.TryTake(out DivisorScanSession? session)) + { + session.Reset(); + return session; + } + + return new DivisorScanSession(this); } IMersenneNumberDivisorByDivisorTester.IDivisorScanSession IMersenneNumberDivisorByDivisorTester.CreateDivisorSession() => CreateDivisorSession();