Pools made thread static. Various small optimizations. "in" parameter added to read-only method parameters

This commit is contained in:
mlemanczyk committed 2025-10-15 02:17:13 +02:00
1 parent 7c71d5165e
commit d3695a3dc3
10 files changed
+163 -225

No files matched your search

@@ -70,7 +70,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
return new DummySession(); return new DummySession();
} }
public void PrepareCandidates(ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues) public void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues)
{ {
for (int i = 0; i < primes.Length; i++) for (int i = 0; i < primes.Length; i++)
{ {
@@ -80,7 +80,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
private sealed class DummySession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession private sealed class DummySession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
{ {
public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, ReadOnlySpan<ulong> primes, Span<byte> hits) public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan<ulong> primes, Span<byte> hits)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
@@ -1,18 +1,14 @@
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Runtime.CompilerServices;
using System.Numerics;
using PerfectNumbers.Core;
namespace PerfectNumbers.Core.Cpu; namespace PerfectNumbers.Core.Cpu;
public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDivisorByDivisorTester public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDivisorByDivisorTester
{ {
private readonly object _sync = new(); private readonly object _sync = new();
private readonly ConcurrentBag<DivisorScanSession> _sessionPool = new(); private readonly ConcurrentBag<DivisorScanSession> _sessionPool = [];
private ulong _divisorLimit; private ulong _divisorLimit;
private ulong _lastStatusDivisor; private ulong _lastStatusDivisor;
private bool _isConfigured;
private int _batchSize = 1_024; private int _batchSize = 1_024;
@@ -23,56 +19,26 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
} }
public void ConfigureFromMaxPrime(ulong maxPrime) public void ConfigureFromMaxPrime(ulong maxPrime)
{
lock (_sync)
{ {
_divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime); _divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime);
_lastStatusDivisor = 0UL; _lastStatusDivisor = 0UL;
_isConfigured = true;
}
} }
public ulong DivisorLimit public ulong DivisorLimit
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get get
{ {
lock (_sync)
{
if (!_isConfigured)
{
throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
}
return _divisorLimit; return _divisorLimit;
} }
} }
}
public ulong GetAllowedMaxDivisor(ulong prime) [MethodImpl(MethodImplOptions.AggressiveInlining)]
{ public ulong GetAllowedMaxDivisor(ulong prime) => ComputeAllowedMaxDivisor(prime, _divisorLimit);
lock (_sync)
{
if (!_isConfigured)
{
throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
}
return ComputeAllowedMaxDivisor(prime, _divisorLimit);
}
}
public bool IsPrime(ulong prime, out bool divisorsExhausted) public bool IsPrime(ulong prime, out bool divisorsExhausted)
{ {
ulong allowedMax; ulong allowedMax = ComputeAllowedMaxDivisor(prime, _divisorLimit);
lock (_sync)
{
if (!_isConfigured)
{
throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
}
allowedMax = ComputeAllowedMaxDivisor(prime, _divisorLimit);
}
if (allowedMax < 3UL) if (allowedMax < 3UL)
{ {
@@ -81,13 +47,11 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
} }
ulong processedCount; ulong processedCount;
ulong lastProcessed;
bool processedAll; bool processedAll;
bool composite = CheckDivisors( bool composite = CheckDivisors(
prime, prime,
allowedMax, allowedMax,
out lastProcessed,
out processedAll, out processedAll,
out processedCount); out processedCount);
@@ -109,26 +73,11 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
return true; return true;
} }
public void PrepareCandidates(ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues) public void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues)
{ {
if (allowedMaxValues.Length < primes.Length) ulong divisorLimit = _divisorLimit;
{ int length = primes.Length;
throw new ArgumentException("allowedMaxValues span must be at least as long as primes span.", nameof(allowedMaxValues)); for (int index = 0; index < length; index++)
}
ulong divisorLimit;
lock (_sync)
{
if (!_isConfigured)
{
throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
}
divisorLimit = _divisorLimit;
}
for (int index = 0; index < primes.Length; index++)
{ {
allowedMaxValues[index] = ComputeAllowedMaxDivisor(primes[index], divisorLimit); allowedMaxValues[index] = ComputeAllowedMaxDivisor(primes[index], divisorLimit);
} }
@@ -136,13 +85,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession() public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession()
{ {
lock (_sync)
{
if (!_isConfigured)
{
throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
}
if (_sessionPool.TryTake(out DivisorScanSession? session)) if (_sessionPool.TryTake(out DivisorScanSession? session))
{ {
session.Reset(); session.Reset();
@@ -151,7 +93,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
return new DivisorScanSession(this); return new DivisorScanSession(this);
} }
}
private void ReturnSession(DivisorScanSession session) private void ReturnSession(DivisorScanSession session)
{ {
@@ -161,11 +102,9 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
private bool CheckDivisors( private bool CheckDivisors(
ulong prime, ulong prime,
ulong allowedMax, ulong allowedMax,
out ulong lastProcessed,
out bool processedAll, out bool processedAll,
out ulong processedCount) out ulong processedCount)
{ {
lastProcessed = 0UL;
processedCount = 0UL; processedCount = 0UL;
processedAll = false; processedAll = false;
@@ -214,7 +153,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
{ {
ulong candidate = (ulong)divisor; ulong candidate = (ulong)divisor;
processedCount++; processedCount++;
lastProcessed = candidate;
bool admissible = lastIsSeven bool admissible = lastIsSeven
? (remainder10 == 3 || remainder10 == 7 || remainder10 == 9) ? (remainder10 == 3 || remainder10 == 7 || remainder10 == 9)
@@ -348,13 +286,8 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
_disposed = false; _disposed = false;
} }
public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, ReadOnlySpan<ulong> primes, Span<byte> hits) public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan<ulong> primes, Span<byte> hits)
{ {
if (_disposed)
{
throw new ObjectDisposedException(nameof(DivisorScanSession));
}
int length = primes.Length; int length = primes.Length;
if (length == 0) if (length == 0)
{ {
+8
View File
@@ -0,0 +1,8 @@
namespace PerfectNumbers.Core;
public readonly struct FactorEntry128(in UInt128 value, int exponent)
{
public readonly UInt128 Value = value;
public readonly int Exponent = exponent;
}
@@ -148,7 +148,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
return true; return true;
} }
public void PrepareCandidates(ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues) public void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues)
{ {
if (allowedMaxValues.Length < primes.Length) if (allowedMaxValues.Length < primes.Length)
{ {
@@ -583,7 +583,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
_disposed = false; _disposed = false;
} }
public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, ReadOnlySpan<ulong> primes, Span<byte> hits) public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan<ulong> primes, Span<byte> hits)
{ {
if (_disposed) if (_disposed)
{ {
@@ -16,10 +16,10 @@ public interface IMersenneNumberDivisorByDivisorTester
IDivisorScanSession CreateDivisorSession(); IDivisorScanSession CreateDivisorSession();
void PrepareCandidates(ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues); void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues);
public interface IDivisorScanSession : IDisposable public interface IDivisorScanSession : IDisposable
{ {
void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, ReadOnlySpan<ulong> primes, Span<byte> hits); void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan<ulong> primes, Span<byte> hits);
} }
} }
@@ -35,12 +35,12 @@ public static class MersenneNumberDivisorByDivisorTester
bool applyStartPrime = startPrime > 0UL; bool applyStartPrime = startPrime > 0UL;
int skippedByPreviousResults = 0; int skippedByPreviousResults = 0;
ArrayPool<ulong> pool = ThreadStaticPools.UlongPool;
if (previousResults is not null && previousResults.Count > 0) if (previousResults is not null && previousResults.Count > 0)
{ {
int recordedCount = previousResults.Count; int recordedCount = previousResults.Count;
ulong[] recordedCandidates = ArrayPool<ulong>.Shared.Rent(recordedCount); ulong[] recordedCandidates = pool.Rent(recordedCount);
try
{
Span<ulong> recordedSpan = recordedCandidates.AsSpan(0, recordedCount); Span<ulong> recordedSpan = recordedCandidates.AsSpan(0, recordedCount);
int recordedIndex = 0; int recordedIndex = 0;
foreach (ulong value in previousResults.Keys) foreach (ulong value in previousResults.Keys)
@@ -78,11 +78,8 @@ public static class MersenneNumberDivisorByDivisorTester
candidateSpan = CollectionsMarshal.AsSpan(candidates); candidateSpan = CollectionsMarshal.AsSpan(candidates);
candidateCount = candidateSpan.Length; candidateCount = candidateSpan.Length;
} }
}
finally pool.Return(recordedCandidates, clearArray: true);
{
ArrayPool<ulong>.Shared.Return(recordedCandidates, clearArray: true);
}
} }
if (skippedByPreviousResults > 0) if (skippedByPreviousResults > 0)
@@ -188,12 +185,10 @@ public static class MersenneNumberDivisorByDivisorTester
tester.ConfigureFromMaxPrime(maxPrime); tester.ConfigureFromMaxPrime(maxPrime);
int primeCount = primesToTest.Count; int primeCount = primesToTest.Count;
ulong[] primeBatch = ArrayPool<ulong>.Shared.Rent(primeCount); ulong[] primeBatch = pool.Rent(primeCount);
ulong[] allowedMaxBatch = ArrayPool<ulong>.Shared.Rent(primeCount); ulong[] allowedMaxBatch = pool.Rent(primeCount);
List<ulong> filteredPrimes = new(primeCount); List<ulong> filteredPrimes = new(primeCount);
try
{
Span<ulong> primeSpan = primeBatch.AsSpan(0, primeCount); Span<ulong> primeSpan = primeBatch.AsSpan(0, primeCount);
Span<ulong> allowedMaxSpan = allowedMaxBatch.AsSpan(0, primeCount); Span<ulong> allowedMaxSpan = allowedMaxBatch.AsSpan(0, primeCount);
@@ -218,12 +213,9 @@ public static class MersenneNumberDivisorByDivisorTester
filteredPrimes.Add(prime); filteredPrimes.Add(prime);
} }
}
finally pool.Return(allowedMaxBatch, clearArray: true);
{ pool.Return(primeBatch, clearArray: true);
ArrayPool<ulong>.Shared.Return(allowedMaxBatch, clearArray: true);
ArrayPool<ulong>.Shared.Return(primeBatch, clearArray: true);
}
if (filteredPrimes.Count == 0) if (filteredPrimes.Count == 0)
{ {
+2 -2
View File
@@ -38,7 +38,7 @@ namespace PerfectNumbers.Core
return instance; return instance;
} }
public static readonly PartialFactorResult Empty = new PartialFactorResult public static readonly PartialFactorResult Empty = new()
{ {
Cofactor = 1UL, Cofactor = 1UL,
FullyFactored = true, FullyFactored = true,
@@ -70,7 +70,7 @@ namespace PerfectNumbers.Core
if (factors is not null) if (factors is not null)
{ {
Factors = null; Factors = null;
ArrayPool<FactorEntry>.Shared.Return(factors, clearArray: false); ThreadStaticPools.FactorEntryPool.Return(factors, clearArray: false);
} }
_next = s_poolHead; _next = s_poolHead;
@@ -10,10 +10,10 @@ namespace PerfectNumbers.Core;
internal static partial class PrimeOrderCalculator internal static partial class PrimeOrderCalculator
{ {
public static UInt128 Calculate( public static UInt128 Calculate(
UInt128 prime, in UInt128 prime,
UInt128? previousOrder, in UInt128? previousOrder,
PrimeOrderSearchConfig config, in PrimeOrderSearchConfig config,
PrimeOrderHeuristicDevice device) in PrimeOrderHeuristicDevice device)
{ {
var scope = UsePow2Mode(device); var scope = UsePow2Mode(device);
MontgomeryDivisorData divisorData; MontgomeryDivisorData divisorData;
@@ -49,7 +49,7 @@ internal static partial class PrimeOrderCalculator
return result; return result;
} }
private static UInt128 CalculateWideInternal(UInt128 prime, UInt128? previousOrder, in MontgomeryDivisorData divisorData, PrimeOrderSearchConfig config) private static UInt128 CalculateWideInternal(in UInt128 prime, in UInt128? previousOrder, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config)
{ {
if (prime <= UInt128.One) if (prime <= UInt128.One)
{ {
@@ -79,12 +79,12 @@ internal static partial class PrimeOrderCalculator
} }
private static UInt128 RunHeuristicPipelineWide( private static UInt128 RunHeuristicPipelineWide(
UInt128 prime, in UInt128 prime,
UInt128? previousOrder, in UInt128? previousOrder,
in MontgomeryDivisorData divisorData, in MontgomeryDivisorData divisorData,
PrimeOrderSearchConfig config, in PrimeOrderSearchConfig config,
UInt128 phi, in UInt128 phi,
PartialFactorResult128 phiFactors) in PartialFactorResult128 phiFactors)
{ {
if (phiFactors.FullyFactored && TrySpecialMaxWide(phi, prime, divisorData, phiFactors)) if (phiFactors.FullyFactored && TrySpecialMaxWide(phi, prime, divisorData, phiFactors))
{ {
@@ -112,12 +112,12 @@ internal static partial class PrimeOrderCalculator
return candidateOrder; return candidateOrder;
} }
private static UInt128 FinishStrictlyWide(UInt128 prime, in MontgomeryDivisorData divisorData) private static UInt128 FinishStrictlyWide(in UInt128 prime, in MontgomeryDivisorData divisorData)
{ {
return CalculateByFactorizationWide(prime, divisorData); return CalculateByFactorizationWide(prime, divisorData);
} }
private static bool TrySpecialMaxWide(UInt128 phi, UInt128 prime, in MontgomeryDivisorData divisorData, PartialFactorResult128 factors) private static bool TrySpecialMaxWide(in UInt128 phi, in UInt128 prime, in MontgomeryDivisorData divisorData, in PartialFactorResult128 factors)
{ {
ReadOnlySpan<FactorEntry128> factorSpan = factors.Factors; ReadOnlySpan<FactorEntry128> factorSpan = factors.Factors;
int length = factors.Count; int length = factors.Count;
@@ -134,7 +134,7 @@ internal static partial class PrimeOrderCalculator
return true; return true;
} }
private static UInt128 InitializeStartingOrderWide(UInt128 prime, UInt128 phi, in MontgomeryDivisorData divisorData) private static UInt128 InitializeStartingOrderWide(in UInt128 prime, in UInt128 phi, in MontgomeryDivisorData divisorData)
{ {
UInt128 order = phi; UInt128 order = phi;
UInt128 mod8 = prime & (UInt128)7UL; UInt128 mod8 = prime & (UInt128)7UL;
@@ -150,19 +150,20 @@ internal static partial class PrimeOrderCalculator
return order; return order;
} }
private static UInt128 ExponentLoweringWide(UInt128 order, UInt128 prime, in MontgomeryDivisorData divisorData, PartialFactorResult128 factors) private static UInt128 ExponentLoweringWide(UInt128 order, in UInt128 prime, in MontgomeryDivisorData divisorData, in PartialFactorResult128 factors)
{
FactorEntry128[]? tempArray = null;
try
{ {
ArrayPool<FactorEntry128> pool = ThreadStaticPools.FactorEntry128Pool;
ReadOnlySpan<FactorEntry128> factorSpan = factors.Factors; ReadOnlySpan<FactorEntry128> factorSpan = factors.Factors;
int length = factors.Count; int length = factors.Count;
if (length == 0) // TODO: This should never trigger from production code - check
// if (length == 0)
// {
// return order;
// }
FactorEntry128[]? tempArray = pool.Rent(length + 1);
try
{ {
return order;
}
tempArray = ArrayPool<FactorEntry128>.Shared.Rent(length + 1);
Span<FactorEntry128> buffer = tempArray.AsSpan(0, length); Span<FactorEntry128> buffer = tempArray.AsSpan(0, length);
factorSpan.CopyTo(buffer); factorSpan.CopyTo(buffer);
@@ -198,14 +199,11 @@ internal static partial class PrimeOrderCalculator
} }
finally finally
{ {
if (tempArray is not null) pool.Return(tempArray, clearArray: false);
{
ArrayPool<FactorEntry128>.Shared.Return(tempArray, clearArray: false);
}
} }
} }
private static bool TryConfirmOrderWide(UInt128 prime, UInt128 order, in MontgomeryDivisorData divisorData, PrimeOrderSearchConfig config) private static bool TryConfirmOrderWide(in UInt128 prime, in UInt128 order, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config)
{ {
if (order == UInt128.Zero) if (order == UInt128.Zero)
{ {
@@ -263,11 +261,11 @@ internal static partial class PrimeOrderCalculator
} }
private static bool TryHeuristicFinishWide( private static bool TryHeuristicFinishWide(
UInt128 prime, in UInt128 prime,
UInt128 order, in UInt128 order,
UInt128? previousOrder, in UInt128? previousOrder,
in MontgomeryDivisorData divisorData, in MontgomeryDivisorData divisorData,
PrimeOrderSearchConfig config, in PrimeOrderSearchConfig config,
out UInt128 result) out UInt128 result)
{ {
result = UInt128.Zero; result = UInt128.Zero;
@@ -339,7 +337,7 @@ internal static partial class PrimeOrderCalculator
return false; return false;
} }
private static void SortCandidatesWide(UInt128 prime, UInt128? previousOrder, List<UInt128> candidates) private static void SortCandidatesWide(UInt128 prime, in UInt128? previousOrder, List<UInt128> candidates)
{ {
UInt128 previous = previousOrder ?? UInt128.Zero; UInt128 previous = previousOrder ?? UInt128.Zero;
int previousGroup = previousOrder.HasValue ? GetGroupWide(previousOrder.Value, prime) : 1; int previousGroup = previousOrder.HasValue ? GetGroupWide(previousOrder.Value, prime) : 1;
@@ -367,9 +365,9 @@ internal static partial class PrimeOrderCalculator
} }
private static CandidateKey128 BuildKeyWide( private static CandidateKey128 BuildKeyWide(
UInt128 value, in UInt128 value,
UInt128 prime, in UInt128 prime,
UInt128 previous, in UInt128 previous,
int previousGroup, int previousGroup,
bool hasPrevious) bool hasPrevious)
{ {
@@ -391,7 +389,7 @@ internal static partial class PrimeOrderCalculator
return new CandidateKey128(primary, false, distance, false, value); return new CandidateKey128(primary, false, distance, false, value);
} }
private static int CompareComponents(bool descendingX, UInt128 valueX, bool descendingY, UInt128 valueY) private static int CompareComponents(bool descendingX, in UInt128 valueX, bool descendingY, in UInt128 valueY)
{ {
if (descendingX == descendingY) if (descendingX == descendingY)
{ {
@@ -416,7 +414,7 @@ internal static partial class PrimeOrderCalculator
return valueX > valueY ? 1 : -1; return valueX > valueY ? 1 : -1;
} }
private static int GetGroupWide(UInt128 value, UInt128 prime) private static int GetGroupWide(in UInt128 value, in UInt128 prime)
{ {
UInt128 threshold1 = prime >> 3; UInt128 threshold1 = prime >> 3;
if (value <= threshold1) if (value <= threshold1)
@@ -439,7 +437,7 @@ internal static partial class PrimeOrderCalculator
return 0; return 0;
} }
private static void BuildCandidatesWide(UInt128 order, FactorEntry128[] factors, int count, List<UInt128> candidates, int limit) private static void BuildCandidatesWide(in UInt128 order, FactorEntry128[] factors, int count, List<UInt128> candidates, int limit)
{ {
if (count == 0) if (count == 0)
{ {
@@ -452,10 +450,10 @@ internal static partial class PrimeOrderCalculator
} }
private static void BuildCandidatesRecursiveWide( private static void BuildCandidatesRecursiveWide(
UInt128 order, in UInt128 order,
ReadOnlySpan<FactorEntry128> factors, in ReadOnlySpan<FactorEntry128> factors,
int index, int index,
UInt128 divisorProduct, in UInt128 divisorProduct,
List<UInt128> candidates, List<UInt128> candidates,
int limit) int limit)
{ {
@@ -511,7 +509,7 @@ internal static partial class PrimeOrderCalculator
} }
} }
private static bool TryConfirmCandidateWide(UInt128 prime, UInt128 candidate, PrimeOrderSearchConfig config, ref int powUsed, int powBudget, in MontgomeryDivisorData divisorData) private static bool TryConfirmCandidateWide(in UInt128 prime, in UInt128 candidate, in PrimeOrderSearchConfig config, ref int powUsed, int powBudget, in MontgomeryDivisorData divisorData)
{ {
PartialFactorResult128 factorization = PartialFactorWide(candidate, config); PartialFactorResult128 factorization = PartialFactorWide(candidate, config);
if (factorization.Factors is null) if (factorization.Factors is null)
@@ -564,7 +562,7 @@ internal static partial class PrimeOrderCalculator
return true; return true;
} }
private static PartialFactorResult128 PartialFactorWide(UInt128 value, PrimeOrderSearchConfig config) private static PartialFactorResult128 PartialFactorWide(in UInt128 value, in PrimeOrderSearchConfig config)
{ {
if (value <= UInt128.One) if (value <= UInt128.One)
{ {
@@ -664,7 +662,8 @@ internal static partial class PrimeOrderCalculator
return new PartialFactorResult128(null, value, false, 0); return new PartialFactorResult128(null, value, false, 0);
} }
FactorEntry128[]? rented = counts.Count > 0 ? ArrayPool<FactorEntry128>.Shared.Rent(counts.Count) : null; ArrayPool<FactorEntry128>? pool = null;
FactorEntry128[]? rented = counts.Count > 0 ? (pool = ThreadStaticPools.FactorEntry128Pool).Rent(counts.Count) : null;
int index = 0; int index = 0;
if (rented is not null) if (rented is not null)
{ {
@@ -676,20 +675,20 @@ internal static partial class PrimeOrderCalculator
} }
FactorEntry128[]? resultArray = null; FactorEntry128[]? resultArray = null;
if (rented is not null) if (pool is not null)
{ {
Span<FactorEntry128> span = rented.AsSpan(0, index); Span<FactorEntry128> span = rented.AsSpan(0, index);
span.Sort(static (a, b) => a.Value.CompareTo(b.Value)); span.Sort(static (a, b) => a.Value.CompareTo(b.Value));
resultArray = new FactorEntry128[index]; resultArray = new FactorEntry128[index];
span.CopyTo(resultArray); span.CopyTo(resultArray);
ArrayPool<FactorEntry128>.Shared.Return(rented, clearArray: false); pool.Return(rented!, clearArray: false);
} }
bool fullyFactored = cofactor == UInt128.One; bool fullyFactored = cofactor == UInt128.One;
return new PartialFactorResult128(resultArray, cofactor, fullyFactored, index); return new PartialFactorResult128(resultArray, cofactor, fullyFactored, index);
} }
private static UInt128 PopulateSmallPrimeFactorsCpuWide(UInt128 value, uint limit, Dictionary<UInt128, int> counts) private static UInt128 PopulateSmallPrimeFactorsCpuWide(in UInt128 value, uint limit, Dictionary<UInt128, int> counts)
{ {
UInt128 remaining = value; UInt128 remaining = value;
uint[] primes = PrimesGenerator.SmallPrimes; uint[] primes = PrimesGenerator.SmallPrimes;
@@ -729,7 +728,7 @@ internal static partial class PrimeOrderCalculator
return remaining; return remaining;
} }
private static bool TryPollardRhoWide(UInt128 n, Stopwatch stopwatch, long budgetTicks, out UInt128 factor) private static bool TryPollardRhoWide(in UInt128 n, Stopwatch stopwatch, long budgetTicks, out UInt128 factor)
{ {
factor = UInt128.Zero; factor = UInt128.Zero;
if ((n & UInt128.One) == UInt128.Zero) if ((n & UInt128.One) == UInt128.Zero)
@@ -771,7 +770,7 @@ internal static partial class PrimeOrderCalculator
} }
} }
private static UInt128 AdvancePolynomialWide(UInt128 x, UInt128 c, UInt128 modulus) private static UInt128 AdvancePolynomialWide(in UInt128 x, in UInt128 c, in UInt128 modulus)
{ {
BigInteger value = (BigInteger)x; BigInteger value = (BigInteger)x;
value = (value * value + (BigInteger)c) % (BigInteger)modulus; value = (value * value + (BigInteger)c) % (BigInteger)modulus;
@@ -809,7 +808,7 @@ internal static partial class PrimeOrderCalculator
} }
} }
private static int TrailingZeroCountWide(UInt128 value) private static int TrailingZeroCountWide(in UInt128 value)
{ {
if (value == UInt128.Zero) if (value == UInt128.Zero)
{ {
@@ -865,7 +864,7 @@ internal static partial class PrimeOrderCalculator
return order; return order;
} }
private static void FactorCompletelyWide(UInt128 value, Dictionary<UInt128, int> counts) private static void FactorCompletelyWide(in UInt128 value, Dictionary<UInt128, int> counts)
{ {
if (value <= UInt128.One) if (value <= UInt128.One)
{ {
@@ -883,7 +882,7 @@ internal static partial class PrimeOrderCalculator
FactorCompletelyWide(value / factor, counts); FactorCompletelyWide(value / factor, counts);
} }
private static UInt128 PollardRhoWide(UInt128 n) private static UInt128 PollardRhoWide(in UInt128 n)
{ {
if ((n & UInt128.One) == UInt128.Zero) if ((n & UInt128.One) == UInt128.Zero)
{ {
@@ -916,7 +915,7 @@ internal static partial class PrimeOrderCalculator
} }
} }
private static bool IsPrimeWide(UInt128 value) private static bool IsPrimeWide(in UInt128 value)
{ {
if (value <= ulong.MaxValue) if (value <= ulong.MaxValue)
{ {
@@ -996,7 +995,7 @@ internal static partial class PrimeOrderCalculator
return true; return true;
} }
private static UInt128 Pow2ModWide(UInt128 exponent, in UInt128 modulus, in MontgomeryDivisorData divisorData) private static UInt128 Pow2ModWide(in UInt128 exponent, in UInt128 modulus, in MontgomeryDivisorData divisorData)
{ {
if (modulus == UInt128.One) if (modulus == UInt128.One)
{ {
@@ -1026,7 +1025,7 @@ internal static partial class PrimeOrderCalculator
return exponent.Pow2MontgomeryModWindowed(modulus); return exponent.Pow2MontgomeryModWindowed(modulus);
} }
private static void AddFactor(Dictionary<UInt128, int> counts, UInt128 prime, int exponent) private static void AddFactor(Dictionary<UInt128, int> counts, in UInt128 prime, int exponent)
{ {
if (counts.TryGetValue(prime, out int existing)) if (counts.TryGetValue(prime, out int existing))
{ {
@@ -1038,58 +1037,28 @@ internal static partial class PrimeOrderCalculator
} }
} }
private readonly struct CandidateKey128 private readonly struct CandidateKey128(int primary, bool secondaryDescending, in UInt128 secondary, bool tertiaryDescending, in UInt128 tertiary)
{ {
public CandidateKey128(int primary, bool secondaryDescending, UInt128 secondary, bool tertiaryDescending, UInt128 tertiary) public int Primary { get; } = primary;
{
Primary = primary; public bool SecondaryDescending { get; } = secondaryDescending;
SecondaryDescending = secondaryDescending;
Secondary = secondary; public UInt128 Secondary { get; } = secondary;
TertiaryDescending = tertiaryDescending;
Tertiary = tertiary; public bool TertiaryDescending { get; } = tertiaryDescending;
public UInt128 Tertiary { get; } = tertiary;
} }
public int Primary { get; } private readonly struct PartialFactorResult128(FactorEntry128[]? factors, UInt128 cofactor, bool fullyFactored, int count)
public bool SecondaryDescending { get; }
public UInt128 Secondary { get; }
public bool TertiaryDescending { get; }
public UInt128 Tertiary { get; }
}
private readonly struct FactorEntry128
{ {
public FactorEntry128(UInt128 value, int exponent) public FactorEntry128[]? Factors { get; } = factors;
{
Value = value;
Exponent = exponent;
}
public UInt128 Value { get; } public UInt128 Cofactor { get; } = cofactor;
public int Exponent { get; } public bool FullyFactored { get; } = fullyFactored;
}
private readonly struct PartialFactorResult128 public int Count { get; } = count;
{
public PartialFactorResult128(FactorEntry128[]? factors, UInt128 cofactor, bool fullyFactored, int count)
{
Factors = factors;
Cofactor = cofactor;
FullyFactored = fullyFactored;
Count = count;
}
public FactorEntry128[]? Factors { get; }
public UInt128 Cofactor { get; }
public bool FullyFactored { get; }
public int Count { get; }
public static PartialFactorResult128 Empty => new(null, UInt128.One, true, 0); public static PartialFactorResult128 Empty => new(null, UInt128.One, true, 0);
+17 -22
View File
@@ -236,12 +236,12 @@ internal static partial class PrimeOrderCalculator
private static ulong ExponentLoweringCpu(ulong order, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData) private static ulong ExponentLoweringCpu(ulong order, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData)
{ {
FactorEntry[]? tempArray = null; ArrayPool<FactorEntry> pool = ThreadStaticPools.FactorEntryPool;
try
{
ReadOnlySpan<FactorEntry> factorSpan = factors.Factors; ReadOnlySpan<FactorEntry> factorSpan = factors.Factors;
int length = factors.Count; int length = factors.Count;
tempArray = ArrayPool<FactorEntry>.Shared.Rent(length + 1); FactorEntry[] tempArray = pool.Rent(length + 1);
Span<FactorEntry> buffer = tempArray; Span<FactorEntry> buffer = tempArray;
factorSpan.CopyTo(buffer); factorSpan.CopyTo(buffer);
@@ -277,16 +277,9 @@ internal static partial class PrimeOrderCalculator
} }
} }
pool.Return(tempArray, clearArray: false);
return order; return order;
} }
finally
{
if (tempArray is not null)
{
ArrayPool<FactorEntry>.Shared.Return(tempArray, clearArray: false);
}
}
}
private static bool TryConfirmOrderCpu(ulong prime, ulong order, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config) private static bool TryConfirmOrderCpu(ulong prime, ulong order, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config)
{ {
@@ -432,6 +425,7 @@ internal static partial class PrimeOrderCalculator
const int MaxGpuBatchSize = 256; const int MaxGpuBatchSize = 256;
const int StackGpuBatchSize = 64; const int StackGpuBatchSize = 64;
Span<ulong> stackGpuRemainders = stackalloc ulong[StackGpuBatchSize]; Span<ulong> stackGpuRemainders = stackalloc ulong[StackGpuBatchSize];
ArrayPool<ulong> pool = ThreadStaticPools.UlongPool;
while (index < candidateCount && powUsed < powBudget) while (index < candidateCount && powUsed < powBudget)
{ {
int remaining = candidateCount - index; int remaining = candidateCount - index;
@@ -463,7 +457,7 @@ internal static partial class PrimeOrderCalculator
} }
else else
{ {
gpuPool = ArrayPool<ulong>.Shared.Rent(batchSize); gpuPool = pool.Rent(batchSize);
Span<ulong> pooledRemainders = gpuPool.AsSpan(0, batchSize); Span<ulong> pooledRemainders = gpuPool.AsSpan(0, batchSize);
status = PrimeOrderGpuHeuristics.TryPow2ModBatch(batch, prime, pooledRemainders, divisorData); status = PrimeOrderGpuHeuristics.TryPow2ModBatch(batch, prime, pooledRemainders, divisorData);
if (status == GpuPow2ModStatus.Success) if (status == GpuPow2ModStatus.Success)
@@ -473,7 +467,7 @@ internal static partial class PrimeOrderCalculator
} }
else else
{ {
ArrayPool<ulong>.Shared.Return(gpuPool, clearArray: false); pool.Return(gpuPool, clearArray: false);
gpuPool = null; gpuPool = null;
} }
} }
@@ -511,7 +505,7 @@ internal static partial class PrimeOrderCalculator
if (gpuPool is not null) if (gpuPool is not null)
{ {
ArrayPool<ulong>.Shared.Return(gpuPool, clearArray: false); pool.Return(gpuPool, clearArray: false);
} }
result = candidate; result = candidate;
@@ -520,7 +514,7 @@ internal static partial class PrimeOrderCalculator
if (gpuPool is not null) if (gpuPool is not null)
{ {
ArrayPool<ulong>.Shared.Return(gpuPool, clearArray: false); pool.Return(gpuPool, clearArray: false);
} }
index += batchSize; index += batchSize;
@@ -648,7 +642,7 @@ internal static partial class PrimeOrderCalculator
BuildCandidatesRecursive(order, buffer, 0, 1UL, candidates, limit); BuildCandidatesRecursive(order, buffer, 0, 1UL, candidates, limit);
} }
private static void BuildCandidatesRecursive(ulong order, ReadOnlySpan<FactorEntry> factors, int index, ulong divisorProduct, List<ulong> candidates, int limit) private static void BuildCandidatesRecursive(ulong order, in ReadOnlySpan<FactorEntry> factors, int index, ulong divisorProduct, List<ulong> candidates, int limit)
{ {
if (candidates.Count >= limit) if (candidates.Count >= limit)
{ {
@@ -934,6 +928,7 @@ internal static partial class PrimeOrderCalculator
} }
} }
ArrayPool<FactorEntry> pool = ThreadStaticPools.FactorEntryPool;
if (useDictionary) if (useDictionary)
{ {
if ((counts is null || counts.Count == 0) && cofactor == value) if ((counts is null || counts.Count == 0) && cofactor == value)
@@ -946,7 +941,7 @@ internal static partial class PrimeOrderCalculator
return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0); return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0);
} }
FactorEntry[] factors = ArrayPool<FactorEntry>.Shared.Rent(counts.Count); FactorEntry[] factors = pool.Rent(counts.Count);
int index = 0; int index = 0;
foreach (KeyValuePair<ulong, int> entry in counts) foreach (KeyValuePair<ulong, int> entry in counts)
{ {
@@ -980,7 +975,7 @@ internal static partial class PrimeOrderCalculator
return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0); return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0);
} }
FactorEntry[] array = ArrayPool<FactorEntry>.Shared.Rent(actualCount); FactorEntry[] array = pool.Rent(actualCount);
int arrayIndex = 0; int arrayIndex = 0;
for (int i = 0; i < factorCount; i++) for (int i = 0; i < factorCount; i++)
{ {
@@ -1301,8 +1296,8 @@ internal static partial class PrimeOrderCalculator
} }
private static void CopyFactorsToDictionary( private static void CopyFactorsToDictionary(
ReadOnlySpan<ulong> primes, in ReadOnlySpan<ulong> primes,
ReadOnlySpan<int> exponents, in ReadOnlySpan<int> exponents,
int count, int count,
Dictionary<ulong, int> target) Dictionary<ulong, int> target)
{ {
@@ -1329,7 +1324,7 @@ internal static partial class PrimeOrderCalculator
return phi; return phi;
} }
List<KeyValuePair<ulong, int>> entries = new(counts); List<KeyValuePair<ulong, int>> entries = [.. counts];
entries.Sort(static (a, b) => a.Key.CompareTo(b.Key)); entries.Sort(static (a, b) => a.Key.CompareTo(b.Key));
ulong order = phi; ulong order = phi;
+41
View File
@@ -0,0 +1,41 @@
using System.Buffers;
using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core
{
public static class ThreadStaticPools
{
[ThreadStatic]
private static ArrayPool<FactorEntry>? _factorEntryPool;
public static ArrayPool<FactorEntry> FactorEntryPool
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return _factorEntryPool ??= ArrayPool<FactorEntry>.Create();
}
}
[ThreadStatic]
private static ArrayPool<FactorEntry128>? _factorEntry128Pool;
public static ArrayPool<FactorEntry128> FactorEntry128Pool
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return _factorEntry128Pool ??= ArrayPool<FactorEntry128>.Create();
}
}
[ThreadStatic]
private static ArrayPool<ulong>? _ulongPool;
public static ArrayPool<ulong> UlongPool
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return _ulongPool ??= ArrayPool<ulong>.Create();
}
}
}
}