Serialize GPU prime sieve per accelerator
This commit is contained in:
1 parent
ab133d23c1
commit
47e694cdc1
3 files changed
+111
-36
No files matched your search
@@ -360,6 +360,48 @@ public class ProgramTests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Residue_mode_accepts_known_primes_in_parallel()
|
||||
{
|
||||
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var residueField = typeof(Program).GetField("PResidue", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var gcdFilterField = typeof(Program).GetField("_useGcdFilter", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||
|
||||
useDivisorField.SetValue(null, false);
|
||||
gcdFilterField.SetValue(null, false);
|
||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
||||
useIncremental: true,
|
||||
useResidue: true,
|
||||
maxK: 5_000_000UL,
|
||||
residueDivisorSets: PerfectNumberConstants.ExtraDivisorCycleSearchLimit), trackAllValues: true));
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
residueField.SetValue(null, new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
|
||||
try
|
||||
{
|
||||
ulong[] primes = [107UL, 127UL, 521UL, 607UL];
|
||||
Parallel.ForEach(primes, prime =>
|
||||
{
|
||||
Program.IsEvenPerfectCandidate(prime, 0UL, out bool searched, out bool detailed).Should().BeTrue();
|
||||
searched.Should().BeTrue();
|
||||
detailed.Should().BeTrue();
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
useDivisorField.SetValue(null, false);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
residueField.SetValue(null, null);
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Residue_mode_with_default_limits_accepts_large_known_primes()
|
||||
{
|
||||
|
||||
@@ -36,5 +36,33 @@ public class PrimeTesterIsPrimeTests
|
||||
|
||||
GpuContextPool.ForceCpu = false;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrimeGpu_handles_parallel_calls_for_known_primes()
|
||||
{
|
||||
GpuContextPool.ForceCpu = false;
|
||||
GpuPrimeWorkLimiter.SetLimit(64);
|
||||
PrimeTester.GpuBatchSize = 256_000;
|
||||
|
||||
ulong[] primes = [107UL, 127UL, 521UL, 607UL];
|
||||
var results = new bool[primes.Length];
|
||||
|
||||
var testers = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
||||
Parallel.For(0, primes.Length, i =>
|
||||
{
|
||||
results[i] = testers.Value!.IsPrimeGpu(primes[i], CancellationToken.None);
|
||||
});
|
||||
|
||||
results.Should().AllBeEquivalentTo(true);
|
||||
|
||||
Parallel.For(0, Environment.ProcessorCount * 16, _ =>
|
||||
{
|
||||
foreach (ulong prime in primes)
|
||||
{
|
||||
testers.Value!.IsPrimeGpu(prime, CancellationToken.None).Should().BeTrue();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,48 +83,53 @@ public sealed class PrimeTester(bool useInternal = false)
|
||||
// Limit concurrency and declare variables outside loops for performance and reuse
|
||||
var limiter = GpuPrimeWorkLimiter.Acquire();
|
||||
var gpu = GpuContextPool.Rent();
|
||||
var accelerator = gpu.Accelerator;
|
||||
|
||||
// Get per-accelerator cached kernel and device primes buffer.
|
||||
var state = GpuKernelState.GetOrCreate(accelerator);
|
||||
|
||||
MemoryBuffer1D<ulong, Stride1D.Dense> input;
|
||||
MemoryBuffer1D<byte, Stride1D.Dense> output;
|
||||
int totalLength, batchSize, pos, remaining, count;
|
||||
ulong[] temp;
|
||||
|
||||
totalLength = values.Length;
|
||||
batchSize = Math.Max(1, GpuBatchSize);
|
||||
|
||||
// Rent per-call scratch buffers sized to current batch (thread-safe)
|
||||
var scratch = state.RentScratch(batchSize, accelerator);
|
||||
input = scratch.Input;
|
||||
output = scratch.Output;
|
||||
|
||||
// Reusable host buffer per call
|
||||
temp = ArrayPool<ulong>.Shared.Rent(batchSize);
|
||||
|
||||
pos = 0;
|
||||
while (pos < totalLength)
|
||||
try
|
||||
{
|
||||
remaining = totalLength - pos;
|
||||
count = remaining > batchSize ? batchSize : remaining;
|
||||
var accelerator = gpu.Accelerator;
|
||||
|
||||
// Copy chunk to host temp then to device
|
||||
values.Slice(pos, count).CopyTo(temp);
|
||||
input.View.CopyFromCPU(ref temp[0], count);
|
||||
// Get per-accelerator cached kernel and device primes buffer.
|
||||
var state = GpuKernelState.GetOrCreate(accelerator);
|
||||
int totalLength = values.Length;
|
||||
int batchSize = Math.Max(1, GpuBatchSize);
|
||||
|
||||
state.Kernel(count, input.View, state.DevicePrimes.View, output.View);
|
||||
accelerator.Synchronize();
|
||||
output.View.CopyToCPU(ref results[pos], count);
|
||||
lock (gpu.ExecutionLock)
|
||||
{
|
||||
var scratch = state.RentScratch(batchSize, accelerator);
|
||||
var input = scratch.Input;
|
||||
var output = scratch.Output;
|
||||
ulong[] temp = ArrayPool<ulong>.Shared.Rent(batchSize);
|
||||
|
||||
pos += count;
|
||||
try
|
||||
{
|
||||
int pos = 0;
|
||||
while (pos < totalLength)
|
||||
{
|
||||
int remaining = totalLength - pos;
|
||||
int count = remaining > batchSize ? batchSize : remaining;
|
||||
|
||||
values.Slice(pos, count).CopyTo(temp);
|
||||
input.View.CopyFromCPU(ref temp[0], count);
|
||||
|
||||
state.Kernel(count, input.View, state.DevicePrimes.View, output.View);
|
||||
accelerator.Synchronize();
|
||||
output.View.CopyToCPU(ref results[pos], count);
|
||||
|
||||
pos += count;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<ulong>.Shared.Return(temp);
|
||||
state.ReturnScratch(scratch);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
gpu.Dispose();
|
||||
limiter.Dispose();
|
||||
}
|
||||
|
||||
ArrayPool<ulong>.Shared.Return(temp);
|
||||
state.ReturnScratch(scratch);
|
||||
gpu.Dispose();
|
||||
limiter.Dispose();
|
||||
}
|
||||
|
||||
// Per-accelerator GPU state for prime sieve (kernel + uploaded primes).
|
||||
|
||||
Reference in new issue
Block a user