Serialize GPU prime sieve per accelerator
This commit is contained in:
1 parent
ab133d23c1
commit
47e694cdc1
3 files changed
+94
-19
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]
|
[Fact]
|
||||||
public void Residue_mode_with_default_limits_accepts_large_known_primes()
|
public void Residue_mode_with_default_limits_accepts_large_known_primes()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -36,5 +36,33 @@ public class PrimeTesterIsPrimeTests
|
|||||||
|
|
||||||
GpuContextPool.ForceCpu = false;
|
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,34 +83,31 @@ public sealed class PrimeTester(bool useInternal = false)
|
|||||||
// Limit concurrency and declare variables outside loops for performance and reuse
|
// Limit concurrency and declare variables outside loops for performance and reuse
|
||||||
var limiter = GpuPrimeWorkLimiter.Acquire();
|
var limiter = GpuPrimeWorkLimiter.Acquire();
|
||||||
var gpu = GpuContextPool.Rent();
|
var gpu = GpuContextPool.Rent();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = gpu.Accelerator;
|
||||||
|
|
||||||
// Get per-accelerator cached kernel and device primes buffer.
|
// Get per-accelerator cached kernel and device primes buffer.
|
||||||
var state = GpuKernelState.GetOrCreate(accelerator);
|
var state = GpuKernelState.GetOrCreate(accelerator);
|
||||||
|
int totalLength = values.Length;
|
||||||
|
int batchSize = Math.Max(1, GpuBatchSize);
|
||||||
|
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense> input;
|
lock (gpu.ExecutionLock)
|
||||||
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);
|
var scratch = state.RentScratch(batchSize, accelerator);
|
||||||
input = scratch.Input;
|
var input = scratch.Input;
|
||||||
output = scratch.Output;
|
var output = scratch.Output;
|
||||||
|
ulong[] temp = ArrayPool<ulong>.Shared.Rent(batchSize);
|
||||||
|
|
||||||
// Reusable host buffer per call
|
try
|
||||||
temp = ArrayPool<ulong>.Shared.Rent(batchSize);
|
{
|
||||||
|
int pos = 0;
|
||||||
pos = 0;
|
|
||||||
while (pos < totalLength)
|
while (pos < totalLength)
|
||||||
{
|
{
|
||||||
remaining = totalLength - pos;
|
int remaining = totalLength - pos;
|
||||||
count = remaining > batchSize ? batchSize : remaining;
|
int count = remaining > batchSize ? batchSize : remaining;
|
||||||
|
|
||||||
// Copy chunk to host temp then to device
|
|
||||||
values.Slice(pos, count).CopyTo(temp);
|
values.Slice(pos, count).CopyTo(temp);
|
||||||
input.View.CopyFromCPU(ref temp[0], count);
|
input.View.CopyFromCPU(ref temp[0], count);
|
||||||
|
|
||||||
@@ -120,12 +117,20 @@ public sealed class PrimeTester(bool useInternal = false)
|
|||||||
|
|
||||||
pos += count;
|
pos += count;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
ArrayPool<ulong>.Shared.Return(temp);
|
ArrayPool<ulong>.Shared.Return(temp);
|
||||||
state.ReturnScratch(scratch);
|
state.ReturnScratch(scratch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
gpu.Dispose();
|
gpu.Dispose();
|
||||||
limiter.Dispose();
|
limiter.Dispose();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Per-accelerator GPU state for prime sieve (kernel + uploaded primes).
|
// Per-accelerator GPU state for prime sieve (kernel + uploaded primes).
|
||||||
private sealed class KernelState
|
private sealed class KernelState
|
||||||
|
|||||||
Reference in new issue
Block a user