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.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,80 +32,55 @@ 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)
|
_divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime);
|
||||||
{
|
_lastStatusDivisor = 0;
|
||||||
_configuredMaxPrime = maxPrime;
|
_isConfigured = true;
|
||||||
_divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime);
|
|
||||||
_lastStatusDivisor = 0UL;
|
|
||||||
_isConfigured = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong DivisorLimit
|
public ulong DivisorLimit
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return _divisorLimit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return _divisorLimit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong GetAllowedMaxDivisor(ulong prime)
|
public ulong GetAllowedMaxDivisor(ulong prime)
|
||||||
{
|
{
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
if (!_isConfigured)
|
||||||
lock (_sync)
|
|
||||||
{
|
{
|
||||||
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)
|
||||||
{
|
{
|
||||||
divisorsExhausted = true;
|
divisorsExhausted = true;
|
||||||
@@ -128,10 +101,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
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;
|
if (!_isConfigured)
|
||||||
|
|
||||||
lock (_sync)
|
|
||||||
{
|
{
|
||||||
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);
|
||||||
@@ -943,34 +936,28 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
public ulong GetAllowedMaxDivisor(ulong prime)
|
public ulong GetAllowedMaxDivisor(ulong prime)
|
||||||
{
|
{
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ComputeAllowedMaxDivisor(prime, _divisorLimit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ComputeAllowedMaxDivisor(prime, _divisorLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DivisorScanSession CreateDivisorSession()
|
public DivisorScanSession CreateDivisorSession()
|
||||||
{
|
{
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sessionPool.TryTake(out DivisorScanSession? session))
|
|
||||||
{
|
|
||||||
session.Reset();
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DivisorScanSession(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_sessionPool.TryTake(out DivisorScanSession? session))
|
||||||
|
{
|
||||||
|
session.Reset();
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DivisorScanSession(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
IMersenneNumberDivisorByDivisorTester.IDivisorScanSession IMersenneNumberDivisorByDivisorTester.CreateDivisorSession() => CreateDivisorSession();
|
IMersenneNumberDivisorByDivisorTester.IDivisorScanSession IMersenneNumberDivisorByDivisorTester.CreateDivisorSession() => CreateDivisorSession();
|
||||||
|
|||||||
Reference in new issue
Block a user