Pools made thread static. Various small optimizations. "in" parameter added to read-only method parameters
This commit is contained in:
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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user