Remove configuration locks from by-divisor testers
This commit is contained in:
1 parent
6e89700f80
commit
b0ba6401ca
2 files changed
+61
-88
No files matched your search
@@ -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<DivisorScanSession> _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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in new issue
Block a user