diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs index f6047c0..f5f238a 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs @@ -12,28 +12,12 @@ namespace PerfectNumbers.Core; internal static partial class PrimeOrderCalculator { - internal struct PendingEntry + internal struct PendingEntry(ulong value, bool knownComposite) { - public ulong Value; - public bool KnownComposite; - public bool HasKnownPrimality; - public bool IsPrime; - - public PendingEntry(ulong value, bool knownComposite) - { - Value = value; - KnownComposite = knownComposite; - HasKnownPrimality = knownComposite; - IsPrime = false; - } - - private PendingEntry(ulong value, bool knownComposite, bool hasKnownPrimality, bool isPrime) - { - Value = value; - KnownComposite = knownComposite; - HasKnownPrimality = hasKnownPrimality; - IsPrime = isPrime; - } + public readonly ulong Value = value; + public bool KnownComposite = knownComposite; + public bool HasKnownPrimality = knownComposite; + public bool IsPrime = false; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WithPrimality(bool isPrime) @@ -248,18 +232,24 @@ internal static partial class PrimeOrderCalculator private static UInt128 FinishStrictlyWideCpu(in UInt128 prime, in MontgomeryDivisorData? divisorData) { UInt128 phi = prime - UInt128.One; - Dictionary counts = new(capacity: 8); + Queue> uInt128IntDictionaryPool = ThreadStaticPools.UInt128IntDictionaryPool; + Dictionary counts = uInt128IntDictionaryPool.Rent(capacity: 8); FactorCompletelyWide(phi, counts); - if (counts.Count == 0) + int entryCount = counts.Count; + if (entryCount == 0) { + uInt128IntDictionaryPool.Return(counts); return phi; } - List> entries = new(counts); - entries.Sort(static (a, b) => a.Key.CompareTo(b.Key)); + Queue>> keyValuePairUInt128IntegerPool = ThreadStaticPools.KeyValuePairUInt128IntegerPool; + var entries = keyValuePairUInt128IntegerPool.Rent(entryCount); + entries.AddRange(counts); + uInt128IntDictionaryPool.Return(counts); + + entries.Sort(static (a, b) => a.Key.CompareTo(b.Key)); UInt128 order = phi; - int entryCount = entries.Count; for (int i = 0; i < entryCount; i++) { UInt128 primeFactor = entries[i].Key; @@ -281,6 +271,7 @@ internal static partial class PrimeOrderCalculator } } + keyValuePairUInt128IntegerPool.Return(entries); return order; } @@ -456,19 +447,23 @@ internal static partial class PrimeOrderCalculator } int capacity = config.MaxPowChecks <= 0 ? 64 : config.MaxPowChecks * 4; - List candidates = new(capacity); + Queue> uInt128ListPool = ThreadStaticPools.UInt128ListPool; + List candidates = uInt128ListPool.Rent(capacity); FactorEntry128[] factorArray = orderFactors.Factors!; + BuildCandidatesWide(order, factorArray, orderFactors.Count, candidates, capacity); - if (candidates.Count == 0) + + int candidateCount = candidates.Count; + if (candidateCount == 0) { + uInt128ListPool.Return(candidates); return false; } SortCandidatesWide(prime, previousOrder, candidates); - int powBudget = config.MaxPowChecks <= 0 ? candidates.Count : config.MaxPowChecks; + int powBudget = config.MaxPowChecks <= 0 ? candidateCount : config.MaxPowChecks; int powUsed = 0; - int candidateCount = candidates.Count; for (int i = 0; i < candidateCount; i++) { @@ -491,9 +486,11 @@ internal static partial class PrimeOrderCalculator } result = candidate; + uInt128ListPool.Return(candidates); return true; } + uInt128ListPool.Return(candidates); return false; } @@ -990,7 +987,8 @@ internal static partial class PrimeOrderCalculator int[] exponentsArray = orderFactors.Exponents!; BuildCandidates(order, factorArray, exponentsArray, orderFactors.Count, candidates, capacity); - if (candidates.Count == 0) + int candidateCount = candidates.Count; + if (candidateCount == 0) { ThreadStaticPools.ReturnUlongList(candidates); return false; @@ -998,9 +996,8 @@ internal static partial class PrimeOrderCalculator SortCandidates(prime, previousOrder, candidates); - int powBudget = config.MaxPowChecks <= 0 ? candidates.Count : config.MaxPowChecks; + int powBudget = config.MaxPowChecks <= 0 ? candidateCount : config.MaxPowChecks; int powUsed = 0; - int candidateCount = candidates.Count; Span candidateSpan = CollectionsMarshal.AsSpan(candidates); ExponentRemainderStepperCpu powStepper = ThreadStaticPools.RentExponentStepperCpu(divisorData); bool powStepperInitialized = false; @@ -1120,7 +1117,8 @@ internal static partial class PrimeOrderCalculator int[] exponentsArray = orderFactors.Exponents!; BuildCandidates(order, factorArray, exponentsArray, orderFactors.Count, candidates, capacity); - if (candidates.Count == 0) + int candidateCount = candidates.Count; + if (candidateCount == 0) { ThreadStaticPools.ReturnUlongList(candidates); return false; @@ -1128,9 +1126,8 @@ internal static partial class PrimeOrderCalculator SortCandidates(prime, previousOrder, candidates); - int powBudget = config.MaxPowChecks <= 0 ? candidates.Count : config.MaxPowChecks; + int powBudget = config.MaxPowChecks <= 0 ? candidateCount : config.MaxPowChecks; int powUsed = 0; - int candidateCount = candidates.Count; Span candidateSpan = CollectionsMarshal.AsSpan(candidates); ExponentRemainderStepperCpu powStepper = ThreadStaticPools.RentExponentStepperCpu(divisorData); bool powStepperInitialized = false; @@ -1252,7 +1249,8 @@ internal static partial class PrimeOrderCalculator int[] exponentsArray = orderFactors.Exponents!; BuildCandidates(order, factorArray, exponentsArray, orderFactors.Count, candidates, capacity); - if (candidates.Count == 0) + int candidateCount = candidates.Count; + if (candidateCount == 0) { ThreadStaticPools.ReturnUlongList(candidates); return false; @@ -1260,9 +1258,8 @@ internal static partial class PrimeOrderCalculator SortCandidates(prime, previousOrder, candidates); - int powBudget = config.MaxPowChecks <= 0 ? candidates.Count : config.MaxPowChecks; + int powBudget = config.MaxPowChecks <= 0 ? candidateCount : config.MaxPowChecks; int powUsed = 0; - int candidateCount = candidates.Count; bool allowGpuBatch = true; Span candidateSpan = CollectionsMarshal.AsSpan(candidates); ExponentRemainderStepperCpu powStepper = ThreadStaticPools.RentExponentStepperCpu(divisorData); @@ -2877,10 +2874,11 @@ internal static partial class PrimeOrderCalculator private static ulong CalculateByFactorizationCpu(ulong prime, in MontgomeryDivisorData divisorData) { ulong phi = prime - 1UL; - Dictionary counts = new(capacity: 8); + Dictionary counts = ThreadStaticPools.RentFactorCountDictionary(); FactorCompletelyCpu(phi, counts, false); if (counts.Count == 0) { + ThreadStaticPools.ReturnFactorCountDictionary(counts); return phi; } @@ -2916,6 +2914,7 @@ internal static partial class PrimeOrderCalculator } } + ThreadStaticPools.ReturnFactorCountDictionary(counts); return order; } diff --git a/PerfectNumbers.Core/ThreadStaticPools.cs b/PerfectNumbers.Core/ThreadStaticPools.cs index 8e27bef..64ed8f1 100644 --- a/PerfectNumbers.Core/ThreadStaticPools.cs +++ b/PerfectNumbers.Core/ThreadStaticPools.cs @@ -6,7 +6,7 @@ using PeterO.Numbers; namespace PerfectNumbers.Core { - public readonly struct ThreadStaticPools + public static class ThreadStaticPools { [ThreadStatic] private static ArrayPool? _eintegerPool; @@ -21,6 +21,98 @@ namespace PerfectNumbers.Core } [ThreadStatic] + private static Queue>>? _keyValuePairUInt128IntegerPool; + + public static Queue>> KeyValuePairUInt128IntegerPool + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return _keyValuePairUInt128IntegerPool ??= []; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static List> Rent(this Queue>> pool, int capacity) + { + if (pool.TryDequeue(out var pooled)) + { + if (capacity > pooled.Capacity) + { + pooled.Capacity = capacity; + } + + pooled.Clear(); + return pooled; + } + + return new(capacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Return(this Queue>> pool, List> list) => pool.Enqueue(list); + + [ThreadStatic] + private static Queue>? _uint128ListPool; + + public static Queue> UInt128ListPool + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return _uint128ListPool ??= []; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static List Rent(this Queue> pool, int capacity) + { + if (pool.TryDequeue(out var pooled)) + { + if (capacity > pooled.Capacity) + { + pooled.Capacity = capacity; + } + + pooled.Clear(); + return pooled; + } + + return new(capacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Return(this Queue> pool, List list) => pool.Enqueue(list); + + [ThreadStatic] + private static Queue>? _uint128IntDictionaryPool; + + public static Queue> UInt128IntDictionaryPool + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return _uint128IntDictionaryPool ??= []; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Dictionary Rent(this Queue> pool, int capacity) + { + if (pool.TryDequeue(out var pooled)) + { + pooled.EnsureCapacity(capacity); + pooled.Clear(); + return pooled; + } + + return new(capacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Return(this Queue> pool, Dictionary dictionary) => pool.Enqueue(dictionary); + + [ThreadStatic] private static ArrayPool? _factorEntryPool; public static ArrayPool FactorEntryPool diff --git a/test-_even_perfect_bit_scan_inc-add_thr-1_blk-1_mers-bydivisor_mersdev-gpu_ntt-reference_red-barrett128_primesdev-gpu_order-off_orderdev-gpu_gcd-off_gputh-64_llslice-32_scanb-256000_warm-5000000.csv b/test-_even_perfect_bit_scan_inc-add_thr-1_blk-1_mers-bydivisor_mersdev-gpu_ntt-reference_red-barrett128_primesdev-gpu_order-off_orderdev-gpu_gcd-off_gputh-64_llslice-32_scanb-256000_warm-5000000.csv new file mode 100644 index 0000000..9687283 --- /dev/null +++ b/test-_even_perfect_bit_scan_inc-add_thr-1_blk-1_mers-bydivisor_mersdev-gpu_ntt-reference_red-barrett128_primesdev-gpu_order-off_orderdev-gpu_gcd-off_gputh-64_llslice-32_scanb-256000_warm-5000000.csv @@ -0,0 +1 @@ +p, searchedMersenne, detailedCheck, passedAllTests