Use span growth instead of dictionaries in partial factor
This commit is contained in:
1 parent
6864b316d5
commit
567b92792c
2 files changed
+101
-234
No files matched your search
@@ -1057,13 +1057,13 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// Span<int> exponentSlots = exponentSlotsArray.AsSpan(0, FactorSlotCount);
|
// Span<int> exponentSlots = exponentSlotsArray.AsSpan(0, FactorSlotCount);
|
||||||
Span<ulong> primeSlots = stackalloc ulong[FactorSlotCount];
|
Span<ulong> primeSlots = stackalloc ulong[FactorSlotCount];
|
||||||
Span<int> exponentSlots = stackalloc int[FactorSlotCount];
|
Span<int> exponentSlots = stackalloc int[FactorSlotCount];
|
||||||
|
ulong[]? primeSlotArray = null;
|
||||||
|
int[]? exponentSlotArray = null;
|
||||||
// We don't need to worry about leftovers, because we always use indexes within the calculated counts
|
// We don't need to worry about leftovers, because we always use indexes within the calculated counts
|
||||||
// primeSlots.Clear();
|
// primeSlots.Clear();
|
||||||
// exponentSlots.Clear();
|
// exponentSlots.Clear();
|
||||||
|
|
||||||
int factorCount = 0;
|
int factorCount = 0;
|
||||||
Dictionary<ulong, int>? counts = null;
|
|
||||||
bool useDictionary = false;
|
|
||||||
|
|
||||||
uint limit = config.SmallFactorLimit == 0 ? uint.MaxValue : config.SmallFactorLimit;
|
uint limit = config.SmallFactorLimit == 0 ? uint.MaxValue : config.SmallFactorLimit;
|
||||||
ulong remaining = value;
|
ulong remaining = value;
|
||||||
@@ -1083,41 +1083,15 @@ internal static partial class PrimeOrderCalculator
|
|||||||
|
|
||||||
if (!gpuFactored)
|
if (!gpuFactored)
|
||||||
{
|
{
|
||||||
// We don't need to worry about leftovers, because we always use indexes within the calculated counts
|
PopulateSmallPrimeFactorsCpu(
|
||||||
// primeSlots.Clear();
|
value,
|
||||||
// exponentSlots.Clear();
|
limit,
|
||||||
|
ref primeSlots,
|
||||||
counts = ThreadStaticPools.RentUlongIntDictionary(Math.Max(FactorSlotCount, 8));
|
ref exponentSlots,
|
||||||
counts.Clear();
|
ref primeSlotArray,
|
||||||
|
ref exponentSlotArray,
|
||||||
// bool gpuPopulated = TryPopulateSmallPrimeFactorsGpu(value, limit, primeSlots, exponentSlots, out factorCount, out remaining);
|
out factorCount,
|
||||||
// if (!gpuPopulated)
|
out remaining);
|
||||||
// {
|
|
||||||
// throw new InvalidOperationException($"GPU didn't populate factors for {value}");
|
|
||||||
// counts.Clear();
|
|
||||||
remaining = PopulateSmallPrimeFactorsCpu(value, limit, counts);
|
|
||||||
// }
|
|
||||||
|
|
||||||
int dictionaryCount = counts.Count;
|
|
||||||
if (dictionaryCount <= FactorSlotCount)
|
|
||||||
{
|
|
||||||
int copyIndex = 0;
|
|
||||||
foreach (KeyValuePair<ulong, int> entry in counts)
|
|
||||||
{
|
|
||||||
primeSlots[copyIndex] = entry.Key;
|
|
||||||
exponentSlots[copyIndex] = entry.Value;
|
|
||||||
copyIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
factorCount = copyIndex;
|
|
||||||
ThreadStaticPools.ReturnUlongIntDictionary(counts);
|
|
||||||
counts = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
useDictionary = true;
|
|
||||||
factorCount = dictionaryCount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1157,7 +1131,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
|
|
||||||
if (isPrime)
|
if (isPrime)
|
||||||
{
|
{
|
||||||
AddFactorToCollector(ref useDictionary, ref counts, primeSlots, exponentSlots, ref factorCount, composite, 1);
|
AddFactorToCollector(ref primeSlots, ref exponentSlots, ref primeSlotArray, ref exponentSlotArray, ref factorCount, composite, 1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1229,7 +1203,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
|
|
||||||
if (entry.IsPrime)
|
if (entry.IsPrime)
|
||||||
{
|
{
|
||||||
AddFactorToCollector(ref useDictionary, ref counts, primeSlots, exponentSlots, ref factorCount, composite, 1);
|
AddFactorToCollector(ref primeSlots, ref exponentSlots, ref primeSlotArray, ref exponentSlotArray, ref factorCount, composite, 1);
|
||||||
}
|
}
|
||||||
// composite is never smaller on the execution path
|
// composite is never smaller on the execution path
|
||||||
// else if (composite > 1UL)
|
// else if (composite > 1UL)
|
||||||
@@ -1264,36 +1238,18 @@ internal static partial class PrimeOrderCalculator
|
|||||||
|
|
||||||
ArrayPool<FactorEntry> pool = ThreadStaticPools.FactorEntryPool;
|
ArrayPool<FactorEntry> pool = ThreadStaticPools.FactorEntryPool;
|
||||||
bool temp;
|
bool temp;
|
||||||
if (useDictionary)
|
if (factorCount == 0)
|
||||||
{
|
{
|
||||||
temp = counts is null || counts.Count == 0;
|
if (cofactor == value)
|
||||||
if (temp && cofactor == value)
|
|
||||||
{
|
{
|
||||||
result = PartialFactorResult.Rent(null, cofactor, false, 0, cofactorIsPrime);
|
result = PartialFactorResult.Rent(null, cofactor, false, 0, cofactorIsPrime);
|
||||||
goto ReturnResult;
|
goto ReturnResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (temp)
|
|
||||||
{
|
|
||||||
result = PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0, cofactorIsPrime);
|
result = PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0, cofactorIsPrime);
|
||||||
goto ReturnResult;
|
goto ReturnResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
FactorEntry[] factors = pool.Rent(counts!.Count);
|
|
||||||
index = 0;
|
|
||||||
foreach (KeyValuePair<ulong, int> entry in counts)
|
|
||||||
{
|
|
||||||
factors[index] = new FactorEntry(entry.Key, entry.Value, true);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Array.Sort(factors, static (a, b) => a.Value.CompareTo(b.Value));
|
|
||||||
|
|
||||||
temp = cofactor == 1UL;
|
|
||||||
result = PartialFactorResult.Rent(factors, cofactor, temp, index, cofactorIsPrime);
|
|
||||||
goto ReturnResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will never happen in production code. We'll always get at least 1 factor
|
// This will never happen in production code. We'll always get at least 1 factor
|
||||||
// if (factorCount == 0)
|
// if (factorCount == 0)
|
||||||
// {
|
// {
|
||||||
@@ -1330,14 +1286,14 @@ internal static partial class PrimeOrderCalculator
|
|||||||
result = PartialFactorResult.Rent(array, cofactor, temp, arrayIndex, cofactorIsPrime);
|
result = PartialFactorResult.Rent(array, cofactor, temp, arrayIndex, cofactorIsPrime);
|
||||||
|
|
||||||
ReturnResult:
|
ReturnResult:
|
||||||
// ThreadStaticPools.UlongPool.Return(primeSlotsArray);
|
if (primeSlotArray is not null)
|
||||||
// ThreadStaticPools.IntPool.Return(exponentSlotsArray);
|
|
||||||
|
|
||||||
|
|
||||||
if (counts is not null)
|
|
||||||
{
|
{
|
||||||
counts.Clear();
|
ThreadStaticPools.UlongPool.Return(primeSlotArray, clearArray: false);
|
||||||
ThreadStaticPools.ReturnUlongIntDictionary(counts);
|
}
|
||||||
|
|
||||||
|
if (exponentSlotArray is not null)
|
||||||
|
{
|
||||||
|
ThreadStaticPools.IntPool.Return(exponentSlotArray, clearArray: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compositeStack is not null)
|
if (compositeStack is not null)
|
||||||
@@ -1351,35 +1307,28 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void PopulateSmallPrimeFactorsCpu(
|
||||||
private static bool TryPopulateSmallPrimeFactorsCpu(
|
|
||||||
ulong value,
|
ulong value,
|
||||||
uint limit,
|
uint limit,
|
||||||
Span<ulong> primeTargets,
|
ref Span<ulong> primeTargets,
|
||||||
Span<int> exponentTargets,
|
ref Span<int> exponentTargets,
|
||||||
|
ref ulong[]? primeArray,
|
||||||
|
ref int[]? exponentArray,
|
||||||
out int factorCount,
|
out int factorCount,
|
||||||
out ulong remaining)
|
out ulong remaining)
|
||||||
{
|
{
|
||||||
factorCount = 0;
|
factorCount = 0;
|
||||||
remaining = value;
|
ulong remainingLocal = value;
|
||||||
|
|
||||||
// value will never <= 1 in production code
|
if (value <= 1UL)
|
||||||
// if (value <= 1UL)
|
{
|
||||||
// {
|
remaining = remainingLocal;
|
||||||
// return true;
|
return;
|
||||||
// }
|
}
|
||||||
|
|
||||||
int capacity = Math.Min(primeTargets.Length, exponentTargets.Length);
|
|
||||||
// capacity will never equal 0 in production code
|
|
||||||
// if (capacity == 0)
|
|
||||||
// {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
uint[] primes = PrimesGenerator.SmallPrimes;
|
uint[] primes = PrimesGenerator.SmallPrimes;
|
||||||
ulong[] squares = PrimesGenerator.SmallPrimesPow2;
|
ulong[] squares = PrimesGenerator.SmallPrimesPow2;
|
||||||
int primeCount = primes.Length;
|
int primeCount = primes.Length;
|
||||||
ulong remainingLocal = value;
|
|
||||||
uint effectiveLimit = limit == 0 ? uint.MaxValue : limit;
|
uint effectiveLimit = limit == 0 ? uint.MaxValue : limit;
|
||||||
|
|
||||||
for (int i = 0; i < primeCount && remainingLocal > 1UL; i++)
|
for (int i = 0; i < primeCount && remainingLocal > 1UL; i++)
|
||||||
@@ -1391,19 +1340,11 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
ulong primeSquare = squares[i];
|
ulong primeSquare = squares[i];
|
||||||
// primeSquare will never == 0 in production code
|
|
||||||
// if (primeSquare != 0 && primeSquare > remainingLocal)
|
|
||||||
if (primeSquare > remainingLocal)
|
if (primeSquare > remainingLocal)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// primeCandidate will never equal 0 in production code
|
|
||||||
// if (primeCandidate == 0UL)
|
|
||||||
// {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
|
|
||||||
ulong primeValue = primeCandidate;
|
ulong primeValue = primeCandidate;
|
||||||
if ((remainingLocal % primeValue) != 0UL)
|
if ((remainingLocal % primeValue) != 0UL)
|
||||||
{
|
{
|
||||||
@@ -1416,10 +1357,13 @@ internal static partial class PrimeOrderCalculator
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (factorCount >= capacity)
|
EnsureCollectorCapacity(
|
||||||
{
|
ref primeTargets,
|
||||||
throw new InvalidOperationException($"Capacity is smaller than factor count");
|
ref exponentTargets,
|
||||||
}
|
ref primeArray,
|
||||||
|
ref exponentArray,
|
||||||
|
factorCount,
|
||||||
|
factorCount + 1);
|
||||||
|
|
||||||
primeTargets[factorCount] = primeValue;
|
primeTargets[factorCount] = primeValue;
|
||||||
exponentTargets[factorCount] = exponent;
|
exponentTargets[factorCount] = exponent;
|
||||||
@@ -1427,7 +1371,6 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
remaining = remainingLocal;
|
remaining = remainingLocal;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -1495,46 +1438,6 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return exponent;
|
return exponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ulong PopulateSmallPrimeFactorsCpu(ulong value, uint limit, Dictionary<ulong, int> counts)
|
|
||||||
{
|
|
||||||
ulong remaining = value;
|
|
||||||
uint[] primes = PrimesGenerator.SmallPrimes;
|
|
||||||
ulong[] squares = PrimesGenerator.SmallPrimesPow2;
|
|
||||||
int primeCount = primes.Length;
|
|
||||||
|
|
||||||
for (int i = 0; i < primeCount; i++)
|
|
||||||
{
|
|
||||||
uint primeCandidate = primes[i];
|
|
||||||
if (primeCandidate > limit)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (squares[i] > remaining)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((remaining % primeCandidate) != 0UL)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int exponent = 0;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
// TODO: Implement DivRem like solution or residue stepper to re-use previous iteration results in new calculation. Add / use method, likely to ULongExtensions
|
|
||||||
remaining /= primeCandidate;
|
|
||||||
exponent++;
|
|
||||||
}
|
|
||||||
while ((remaining % primeCandidate) == 0UL);
|
|
||||||
|
|
||||||
counts[primeCandidate] = exponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return remaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static long CreateDeadlineTimestamp(int milliseconds)
|
private static long CreateDeadlineTimestamp(int milliseconds)
|
||||||
{
|
{
|
||||||
if (milliseconds <= 0)
|
if (milliseconds <= 0)
|
||||||
@@ -1657,38 +1560,11 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryAppendFactor(Span<ulong> primes, Span<int> exponents, ref int count, ulong prime, int exponent)
|
|
||||||
{
|
|
||||||
if (exponent <= 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
if (primes[i] == prime)
|
|
||||||
{
|
|
||||||
exponents[i] += exponent;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count >= primes.Length)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
primes[count] = prime;
|
|
||||||
exponents[count] = exponent;
|
|
||||||
count++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AddFactorToCollector(
|
private static void AddFactorToCollector(
|
||||||
ref bool useDictionary,
|
ref Span<ulong> primes,
|
||||||
ref Dictionary<ulong, int>? counts,
|
ref Span<int> exponents,
|
||||||
Span<ulong> primes,
|
ref ulong[]? primeArray,
|
||||||
Span<int> exponents,
|
ref int[]? exponentArray,
|
||||||
ref int count,
|
ref int count,
|
||||||
ulong prime,
|
ulong prime,
|
||||||
int exponent)
|
int exponent)
|
||||||
@@ -1698,41 +1574,69 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useDictionary)
|
|
||||||
{
|
|
||||||
AddFactor(counts!, prime, exponent);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TryAppendFactor(primes, exponents, ref count, prime, exponent))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
counts ??= ThreadStaticPools.RentUlongIntDictionary(Math.Max(count, 8));
|
|
||||||
CopyFactorsToDictionary(primes, exponents, count, counts);
|
|
||||||
count = 0;
|
|
||||||
useDictionary = true;
|
|
||||||
AddFactor(counts, prime, exponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CopyFactorsToDictionary(
|
|
||||||
in ReadOnlySpan<ulong> primes,
|
|
||||||
in ReadOnlySpan<int> exponents,
|
|
||||||
int count,
|
|
||||||
Dictionary<ulong, int> target)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
ulong prime = primes[i];
|
if (primes[i] == prime)
|
||||||
int exponent = exponents[i];
|
|
||||||
if (prime == 0UL || exponent == 0)
|
|
||||||
{
|
{
|
||||||
continue;
|
exponents[i] += exponent;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
target[prime] = exponent;
|
EnsureCollectorCapacity(
|
||||||
|
ref primes,
|
||||||
|
ref exponents,
|
||||||
|
ref primeArray,
|
||||||
|
ref exponentArray,
|
||||||
|
count,
|
||||||
|
count + 1);
|
||||||
|
|
||||||
|
primes[count] = prime;
|
||||||
|
exponents[count] = exponent;
|
||||||
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EnsureCollectorCapacity(
|
||||||
|
ref Span<ulong> primes,
|
||||||
|
ref Span<int> exponents,
|
||||||
|
ref ulong[]? primeArray,
|
||||||
|
ref int[]? exponentArray,
|
||||||
|
int count,
|
||||||
|
int requiredCapacity)
|
||||||
|
{
|
||||||
|
if (requiredCapacity <= primes.Length)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int newCapacity = primes.Length << 1;
|
||||||
|
if (newCapacity < requiredCapacity)
|
||||||
|
{
|
||||||
|
newCapacity = requiredCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong[] newPrimeArray = ThreadStaticPools.UlongPool.Rent(newCapacity);
|
||||||
|
int[] newExponentArray = ThreadStaticPools.IntPool.Rent(newCapacity);
|
||||||
|
Span<ulong> newPrimeSpan = newPrimeArray.AsSpan();
|
||||||
|
Span<int> newExponentSpan = newExponentArray.AsSpan();
|
||||||
|
|
||||||
|
primes.Slice(0, count).CopyTo(newPrimeSpan);
|
||||||
|
exponents.Slice(0, count).CopyTo(newExponentSpan);
|
||||||
|
|
||||||
|
if (primeArray is not null)
|
||||||
|
{
|
||||||
|
ThreadStaticPools.UlongPool.Return(primeArray, clearArray: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exponentArray is not null)
|
||||||
|
{
|
||||||
|
ThreadStaticPools.IntPool.Return(exponentArray, clearArray: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
primeArray = newPrimeArray;
|
||||||
|
exponentArray = newExponentArray;
|
||||||
|
primes = newPrimeSpan;
|
||||||
|
exponents = newExponentSpan;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ulong CalculateByFactorizationCpu(ulong prime, in MontgomeryDivisorData divisorData)
|
private static ulong CalculateByFactorizationCpu(ulong prime, in MontgomeryDivisorData divisorData)
|
||||||
|
|||||||
@@ -260,43 +260,6 @@ namespace PerfectNumbers.Core
|
|||||||
pool.Add(stack);
|
pool.Add(stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ThreadStatic]
|
|
||||||
private static List<Dictionary<ulong, int>>? _ulongIntDictionaryPool;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static Dictionary<ulong, int> RentUlongIntDictionary(int capacityHint)
|
|
||||||
{
|
|
||||||
List<Dictionary<ulong, int>>? pool = _ulongIntDictionaryPool;
|
|
||||||
if (pool is not null)
|
|
||||||
{
|
|
||||||
int poolCount = pool.Count;
|
|
||||||
if (poolCount > 0)
|
|
||||||
{
|
|
||||||
int lastIndex = poolCount - 1;
|
|
||||||
Dictionary<ulong, int> dictionary = pool[lastIndex];
|
|
||||||
pool.RemoveAt(lastIndex);
|
|
||||||
dictionary.EnsureCapacity(capacityHint);
|
|
||||||
|
|
||||||
return dictionary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Dictionary<ulong, int>(capacityHint);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void ReturnUlongIntDictionary(Dictionary<ulong, int> dictionary)
|
|
||||||
{
|
|
||||||
List<Dictionary<ulong, int>>? pool = _ulongIntDictionaryPool;
|
|
||||||
if (pool is null)
|
|
||||||
{
|
|
||||||
pool = new List<Dictionary<ulong, int>>(4);
|
|
||||||
_ulongIntDictionaryPool = pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
pool.Add(dictionary);
|
|
||||||
}
|
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static Dictionary<ulong, int>? _factorCountDictionary;
|
private static Dictionary<ulong, int>? _factorCountDictionary;
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user