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