Remove configuration locks from by-divisor testers

This commit is contained in:
Marek Lemańczyk committed 2025-10-13 20:41:07 +02:00
1 parent 6e89700f80
commit b0ba6401ca
2 files changed
+31 -58

No files matched your search

@@ -3,6 +3,7 @@ using System.Buffers;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using System.Threading;
using PerfectNumbers.Core; using PerfectNumbers.Core;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
@@ -10,14 +11,11 @@ namespace PerfectNumbers.Core.Cpu;
public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDivisorByDivisorTester public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDivisorByDivisorTester
{ {
private readonly object _sync = new();
private readonly ConcurrentBag<DivisorScanSession> _sessionPool = new(); private readonly ConcurrentBag<DivisorScanSession> _sessionPool = new();
private ulong _divisorLimit; private ulong _divisorLimit;
private ulong _lastStatusDivisor; private int _lastStatusDivisor;
private bool _isConfigured; private bool _isConfigured;
private int _batchSize = 1_024; private int _batchSize = 1_024;
private ulong _configuredMaxPrime;
private ByDivisorDeltasDevice _deltasDevice = ByDivisorDeltasDevice.Cpu; private ByDivisorDeltasDevice _deltasDevice = ByDivisorDeltasDevice.Cpu;
private ByDivisorMontgomeryDevice _montgomeryDevice = ByDivisorMontgomeryDevice.Cpu; private ByDivisorMontgomeryDevice _montgomeryDevice = ByDivisorMontgomeryDevice.Cpu;
@@ -34,43 +32,25 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
public ByDivisorDeltasDevice DeltasDevice public ByDivisorDeltasDevice DeltasDevice
{ {
get => _deltasDevice; get => _deltasDevice;
set set => _deltasDevice = value;
{
lock (_sync)
{
_deltasDevice = value;
}
}
} }
public ByDivisorMontgomeryDevice MontgomeryDevice public ByDivisorMontgomeryDevice MontgomeryDevice
{ {
get => _montgomeryDevice; get => _montgomeryDevice;
set set => _montgomeryDevice = value;
{
lock (_sync)
{
_montgomeryDevice = value;
}
}
} }
public void ConfigureFromMaxPrime(ulong maxPrime) public void ConfigureFromMaxPrime(ulong maxPrime)
{ {
lock (_sync)
{
_configuredMaxPrime = maxPrime;
_divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime); _divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime);
_lastStatusDivisor = 0UL; _lastStatusDivisor = 0;
_isConfigured = true; _isConfigured = true;
} }
}
public ulong DivisorLimit public ulong DivisorLimit
{ {
get get
{
lock (_sync)
{ {
if (!_isConfigured) if (!_isConfigured)
{ {
@@ -80,11 +60,8 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
return _divisorLimit; return _divisorLimit;
} }
} }
}
public ulong GetAllowedMaxDivisor(ulong prime) public ulong GetAllowedMaxDivisor(ulong prime)
{
lock (_sync)
{ {
if (!_isConfigured) if (!_isConfigured)
{ {
@@ -93,20 +70,16 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
return ComputeAllowedMaxDivisor(prime, _divisorLimit); return ComputeAllowedMaxDivisor(prime, _divisorLimit);
} }
}
public bool IsPrime(ulong prime, out bool divisorsExhausted, TimeSpan? timeLimit = null) 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."); throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
} }
allowedMax = ComputeAllowedMaxDivisor(prime, _divisorLimit); ulong divisorLimit = _divisorLimit;
} ulong allowedMax = ComputeAllowedMaxDivisor(prime, divisorLimit);
if (allowedMax < 3UL) if (allowedMax < 3UL)
{ {
@@ -127,12 +100,9 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
timeLimit); timeLimit);
if (processedCount > 0UL) if (processedCount > 0UL)
{
lock (_sync)
{ {
UpdateStatusUnsafe(processedCount); UpdateStatusUnsafe(processedCount);
} }
}
if (composite) if (composite)
{ {
@@ -603,18 +573,34 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
return; return;
} }
ulong interval = PerfectNumberConstants.ConsoleInterval; int interval = PerfectNumberConstants.ConsoleInterval;
// PerfectNumberConstants.ConsoleInterval is a positive constant, so this guard never triggers on EvenPerfectBitScanner. // 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; // return;
// } // }
ulong total = _lastStatusDivisor + processedCount; int processedDelta = (int)(processedCount % (uint)interval);
// TODO: Replace this modulo with the ring-buffer style counter (subtract loop) used in the fast CLI if (processedDelta == 0)
// status benchmarks so we avoid `%` in this hot loop while still wrapping progress correctly. {
_lastStatusDivisor = total % interval; 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) private static ulong ComputeDivisorLimitFromMaxPrime(ulong maxPrime)
@@ -19,7 +19,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
private const int RemainderSlotCount = 4; private const int RemainderSlotCount = 4;
private int _gpuBatchSize = GpuConstants.ScanBatchSize; private int _gpuBatchSize = GpuConstants.ScanBatchSize;
private readonly object _sync = new();
private ulong _divisorLimit; private ulong _divisorLimit;
private bool _isConfigured; 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)); 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."); throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
} }
divisorLimit = _divisorLimit; ulong divisorLimit = _divisorLimit;
}
for (int index = 0; index < primes.Length; index++) for (int index = 0; index < primes.Length; index++)
{ {
allowedMaxValues[index] = ComputeAllowedMaxDivisor(primes[index], divisorLimit); allowedMaxValues[index] = ComputeAllowedMaxDivisor(primes[index], divisorLimit);
@@ -942,8 +935,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
} }
public ulong GetAllowedMaxDivisor(ulong prime) public ulong GetAllowedMaxDivisor(ulong prime)
{
lock (_sync)
{ {
if (!_isConfigured) if (!_isConfigured)
{ {
@@ -952,11 +943,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
return ComputeAllowedMaxDivisor(prime, _divisorLimit); return ComputeAllowedMaxDivisor(prime, _divisorLimit);
} }
}
public DivisorScanSession CreateDivisorSession() public DivisorScanSession CreateDivisorSession()
{
lock (_sync)
{ {
if (!_isConfigured) if (!_isConfigured)
{ {
@@ -971,7 +959,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
return new DivisorScanSession(this); return new DivisorScanSession(this);
} }
}
IMersenneNumberDivisorByDivisorTester.IDivisorScanSession IMersenneNumberDivisorByDivisorTester.CreateDivisorSession() => CreateDivisorSession(); IMersenneNumberDivisorByDivisorTester.IDivisorScanSession IMersenneNumberDivisorByDivisorTester.CreateDivisorSession() => CreateDivisorSession();
} }