diff --git a/EvenPerfectBitScanner/Program.cs b/EvenPerfectBitScanner/Program.cs index c5866e8..77eb824 100644 --- a/EvenPerfectBitScanner/Program.cs +++ b/EvenPerfectBitScanner/Program.cs @@ -55,6 +55,7 @@ internal static class Program ulong remainder = currentP % 6UL; bool startPrimeProvided = _cliArguments.StartPrimeProvided; int threadCount = Math.Max(1, _cliArguments.ThreadCount); + UnboundedTaskScheduler.ConfigureThreadCount(threadCount); int blockSize = Math.Max(1, _cliArguments.BlockSize); int gpuPrimeThreads = Math.Max(1, _cliArguments.GpuPrimeThreads); int gpuPrimeBatch = Math.Max(1, _cliArguments.GpuPrimeBatch); diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.cs b/PerfectNumbers.Core/PrimeOrderCalculator.cs index 34b4c1b..fd2da4b 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.cs @@ -10,9 +10,21 @@ internal static partial class PrimeOrderCalculator internal readonly struct PrimeOrderSearchConfig(uint smallFactorLimit, int pollardRhoMilliseconds, int maxPowChecks, PrimeOrderMode mode) { - public static readonly PrimeOrderSearchConfig HeuristicDefault = new(smallFactorLimit: 100_000, pollardRhoMilliseconds: 24, maxPowChecks: 24, PrimeOrderMode.Heuristic); + public static PrimeOrderSearchConfig HeuristicDefault + { + get + { + return CreateHeuristicDefault(UnboundedTaskScheduler.ConfiguredThreadCount); + } + } public static readonly PrimeOrderSearchConfig StrictDefault = new(smallFactorLimit: 1_000_000, pollardRhoMilliseconds: 0, maxPowChecks: 0, PrimeOrderMode.Strict); + public static PrimeOrderSearchConfig CreateHeuristicDefault(int threadCount) + { + int normalizedThreadCount = threadCount < 1 ? 1 : threadCount; + return new(smallFactorLimit: 100_000, pollardRhoMilliseconds: normalizedThreadCount * 24, maxPowChecks: 24, PrimeOrderMode.Heuristic); + } + public readonly uint SmallFactorLimit = smallFactorLimit; public readonly int PollardRhoMilliseconds = pollardRhoMilliseconds; public readonly int MaxPowChecks = maxPowChecks; diff --git a/PerfectNumbers.Core/UnboundedTaskScheduler.cs b/PerfectNumbers.Core/UnboundedTaskScheduler.cs index ec9209f..66912e9 100644 --- a/PerfectNumbers.Core/UnboundedTaskScheduler.cs +++ b/PerfectNumbers.Core/UnboundedTaskScheduler.cs @@ -1,18 +1,53 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace PerfectNumbers.Core; public sealed class UnboundedTaskScheduler : TaskScheduler { - public static UnboundedTaskScheduler Instance { get; } = new(); + private static readonly object InstanceLock = new(); + private static UnboundedTaskScheduler? _instance; + private static int _configuredThreadCount = Environment.ProcessorCount; - private readonly TaskThreadPool _threadPool; - - private UnboundedTaskScheduler() + public static UnboundedTaskScheduler Instance { - _threadPool = new TaskThreadPool(10_340, ExecuteTask); + get + { + lock (InstanceLock) + { + _instance ??= new UnboundedTaskScheduler(GetNormalizedThreadCount(_configuredThreadCount)); + return _instance; + } + } + } + + public static int ConfiguredThreadCount => GetNormalizedThreadCount(Volatile.Read(ref _configuredThreadCount)); + + private readonly object _poolLock = new(); + private TaskThreadPool _threadPool; + + private UnboundedTaskScheduler(int threadCount) + { + _threadPool = new TaskThreadPool(threadCount, ExecuteTask); + } + + public static void ConfigureThreadCount(int threadCount) + { + int normalizedThreadCount = GetNormalizedThreadCount(threadCount); + + lock (InstanceLock) + { + Volatile.Write(ref _configuredThreadCount, normalizedThreadCount); + + if (_instance is null) + { + return; + } + + _instance.UpdateThreadPool(normalizedThreadCount); + } } public override int MaximumConcurrencyLevel => int.MaxValue; @@ -22,6 +57,22 @@ public sealed class UnboundedTaskScheduler : TaskScheduler _threadPool.Queue(task); } + private void UpdateThreadPool(int threadCount) + { + lock (_poolLock) + { + if (_threadPool.ThreadCount == threadCount) + { + return; + } + + TaskThreadPool newPool = new(threadCount, ExecuteTask); + TaskThreadPool oldPool = _threadPool; + _threadPool = newPool; + oldPool.Dispose(); + } + } + private void ExecuteTask(Task task) { TryExecuteTask(task); @@ -36,4 +87,9 @@ public sealed class UnboundedTaskScheduler : TaskScheduler { return _threadPool.GetScheduledTasks(); } + + private static int GetNormalizedThreadCount(int threadCount) + { + return threadCount < 1 ? 1 : threadCount; + } }