Inline PrimeTester CPU path and reuse thread pools
This commit is contained in:
1 parent
9bc8fd5043
commit
a7fc34e37a
1 file changed
+50
-41
@@ -13,7 +13,7 @@ namespace PerfectNumbers.Core;
|
|||||||
|
|
||||||
public sealed class PrimeTester
|
public sealed class PrimeTester
|
||||||
{
|
{
|
||||||
public PrimeTester(bool useInternal = false)
|
public PrimeTester()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +24,47 @@ public sealed class PrimeTester
|
|||||||
|
|
||||||
public bool IsPrime(ulong n, CancellationToken ct)
|
public bool IsPrime(ulong n, CancellationToken ct)
|
||||||
{
|
{
|
||||||
return IsPrimeInternal(n, ct);
|
// EvenPerfectBitScanner feeds monotonically increasing odd exponents that start well above the
|
||||||
|
// small-prime cutoffs and already exclude multiples of five. These guards remain for targeted
|
||||||
|
// tests and diagnostics so ad-hoc callers can still probe small or even values when needed.
|
||||||
|
bool isTwo = n == 2UL;
|
||||||
|
bool isOdd = (n & 1UL) != 0UL;
|
||||||
|
ulong mod5 = n % 5UL;
|
||||||
|
bool divisibleByFive = n > 5UL && mod5 == 0UL;
|
||||||
|
|
||||||
|
bool result = n >= 2UL && (isTwo || isOdd) && !divisibleByFive;
|
||||||
|
|
||||||
|
// Production scans always fall through to trial division because n is already > 5 by the time this
|
||||||
|
// path executes. Keep the guard so synthetic callers that hammer tiny inputs can skip the loop.
|
||||||
|
bool requiresTrialDivision = result && n >= 7UL && !isTwo;
|
||||||
|
|
||||||
|
if (requiresTrialDivision)
|
||||||
|
{
|
||||||
|
bool sharesMaxExponentFactor = n.Mod10() == 1UL && SharesFactorWithMaxExponent(n);
|
||||||
|
result &= !sharesMaxExponentFactor;
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
var smallPrimeDivisorsLength = PrimesGenerator.SmallPrimes.Length;
|
||||||
|
uint[] smallPrimeDivisors = PrimesGenerator.SmallPrimes;
|
||||||
|
ulong[] smallPrimeDivisorsMul = PrimesGenerator.SmallPrimesPow2;
|
||||||
|
for (int i = 0; i < smallPrimeDivisorsLength; i++)
|
||||||
|
{
|
||||||
|
if (smallPrimeDivisorsMul[i] > n)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n % smallPrimeDivisors[i] == 0)
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsPrimeGpu(ulong n)
|
public static bool IsPrimeGpu(ulong n)
|
||||||
@@ -65,41 +105,8 @@ public sealed class PrimeTester
|
|||||||
|
|
||||||
public static bool IsPrimeInternal(ulong n, CancellationToken ct)
|
public static bool IsPrimeInternal(ulong n, CancellationToken ct)
|
||||||
{
|
{
|
||||||
bool isTwo = n == 2UL;
|
// Preserve the legacy entry point for callers that bypass the thread-local caches.
|
||||||
bool isOdd = (n & 1UL) != 0UL;
|
return Exclusive.IsPrime(n, ct);
|
||||||
ulong mod5 = n % 5UL;
|
|
||||||
bool divisibleByFive = n > 5UL && mod5 == 0UL;
|
|
||||||
|
|
||||||
bool result = n >= 2UL && (isTwo || isOdd) && !divisibleByFive;
|
|
||||||
bool requiresTrialDivision = result && n >= 7UL && !isTwo;
|
|
||||||
|
|
||||||
if (requiresTrialDivision)
|
|
||||||
{
|
|
||||||
bool sharesMaxExponentFactor = n.Mod10() == 1UL && SharesFactorWithMaxExponent(n);
|
|
||||||
result &= !sharesMaxExponentFactor;
|
|
||||||
|
|
||||||
if (result)
|
|
||||||
{
|
|
||||||
var smallPrimeDivisorsLength = PrimesGenerator.SmallPrimes.Length;
|
|
||||||
uint[] smallPrimeDivisors = PrimesGenerator.SmallPrimes;
|
|
||||||
ulong[] smallPrimeDivisorsMul = PrimesGenerator.SmallPrimesPow2;
|
|
||||||
for (int i = 0; i < smallPrimeDivisorsLength; i++)
|
|
||||||
{
|
|
||||||
if (smallPrimeDivisorsMul[i] > n)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n % smallPrimeDivisors[i] == 0)
|
|
||||||
{
|
|
||||||
result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -123,7 +130,8 @@ public sealed class PrimeTester
|
|||||||
var scratch = state.RentScratch(batchSize, accelerator);
|
var scratch = state.RentScratch(batchSize, accelerator);
|
||||||
var input = scratch.Input;
|
var input = scratch.Input;
|
||||||
var output = scratch.Output;
|
var output = scratch.Output;
|
||||||
ulong[] temp = ArrayPool<ulong>.Shared.Rent(batchSize);
|
ArrayPool<ulong> pool = ThreadStaticPools.UlongPool;
|
||||||
|
ulong[] temp = pool.Rent(batchSize);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -145,7 +153,7 @@ public sealed class PrimeTester
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
ArrayPool<ulong>.Shared.Return(temp);
|
pool.Return(temp, clearArray: false);
|
||||||
state.ReturnScratch(scratch);
|
state.ReturnScratch(scratch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -385,13 +393,14 @@ public sealed class PrimeTester
|
|||||||
var inputBuffer = accelerator.Allocate1D<ulong>(length);
|
var inputBuffer = accelerator.Allocate1D<ulong>(length);
|
||||||
var resultBuffer = accelerator.Allocate1D<byte>(length);
|
var resultBuffer = accelerator.Allocate1D<byte>(length);
|
||||||
|
|
||||||
ulong[] temp = ArrayPool<ulong>.Shared.Rent(length);
|
ArrayPool<ulong> pool = ThreadStaticPools.UlongPool;
|
||||||
|
ulong[] temp = pool.Rent(length);
|
||||||
values.CopyTo(temp);
|
values.CopyTo(temp);
|
||||||
inputBuffer.View.CopyFromCPU(ref temp[0], length);
|
inputBuffer.View.CopyFromCPU(ref temp[0], length);
|
||||||
kernel(length, inputBuffer.View, resultBuffer.View);
|
kernel(length, inputBuffer.View, resultBuffer.View);
|
||||||
accelerator.Synchronize();
|
accelerator.Synchronize();
|
||||||
resultBuffer.View.CopyToCPU(ref results[0], length);
|
resultBuffer.View.CopyToCPU(ref results[0], length);
|
||||||
ArrayPool<ulong>.Shared.Return(temp);
|
pool.Return(temp, clearArray: false);
|
||||||
resultBuffer.Dispose();
|
resultBuffer.Dispose();
|
||||||
inputBuffer.Dispose();
|
inputBuffer.Dispose();
|
||||||
gpu.Dispose();
|
gpu.Dispose();
|
||||||
|
|||||||
Reference in new issue
Block a user