Implement multi-tier divisor cycle caching and per-thread reuse
This commit is contained in:
1 parent
e92bca197d
commit
d4dcd0a534
8 files changed
+585
-145
No files matched your search
@@ -447,16 +447,18 @@ internal static class Program
|
||||
}
|
||||
|
||||
Console.WriteLine($"Loading divisor cycles into memory...");
|
||||
if (string.Equals(Path.GetExtension(cyclesPath), ".csv", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
MersenneDivisorCycles.Shared.LoadFrom(cyclesPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
MersenneDivisorCycles.Shared.LoadFrom(cyclesPath);
|
||||
}
|
||||
if (string.Equals(Path.GetExtension(cyclesPath), ".csv", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
MersenneDivisorCycles.Shared.LoadFrom(cyclesPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
MersenneDivisorCycles.Shared.LoadFrom(cyclesPath);
|
||||
}
|
||||
|
||||
Console.WriteLine("Divisor cycles are ready");
|
||||
DivisorCycleCache.Shared.ReloadFromCurrentSnapshot();
|
||||
|
||||
Console.WriteLine("Divisor cycles are ready");
|
||||
|
||||
if (useByDivisor)
|
||||
{
|
||||
@@ -882,12 +884,18 @@ internal static class Program
|
||||
bool exhausted = false;
|
||||
ulong divisor = 0UL;
|
||||
int activeCount = 0;
|
||||
Span<byte> hitsSpan = default;
|
||||
int hitIndex = 0;
|
||||
int index = 0;
|
||||
Span<byte> hitsSpan = default;
|
||||
int hitIndex = 0;
|
||||
int index = 0;
|
||||
bool useCycleFilter = _byDivisorTester!.UseDivisorCycles;
|
||||
DivisorCycleCache.Lease cycleLease = default;
|
||||
ulong cycleLeaseStart = 0UL;
|
||||
ulong cycleLeaseEnd = 0UL;
|
||||
ulong[]? cycleValues = null;
|
||||
ulong divisorCycle = 0UL;
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (Volatile.Read(ref remainingStates) == 0)
|
||||
@@ -900,11 +908,11 @@ internal static class Program
|
||||
|
||||
divisor = AcquireNextDivisor(ref nextDivisor, divisorLimit, ref divisorsExhaustedFlag, ref finalDivisorBits, out exhausted, ref localDivisorCursor, ref localDivisorsRemaining);
|
||||
|
||||
if (divisor == 0UL)
|
||||
{
|
||||
if (!exhausted)
|
||||
{
|
||||
if (Volatile.Read(ref remainingStates) == 0)
|
||||
if (divisor == 0UL)
|
||||
{
|
||||
if (!exhausted)
|
||||
{
|
||||
if (Volatile.Read(ref remainingStates) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -937,11 +945,48 @@ internal static class Program
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Can we prepare as many buffers as many threads were requested upfront and have them prepared in separate task, so that they're always ready when needed?
|
||||
activeCount = BuildPrimeBuffer(divisor, primeValues, allowedMaxValues, stateFlags, primeBuffer, indexBuffer, completionsBuffer, ref completionsCount, ref remainingStates, activeStateMask, ref activeStartIndex);
|
||||
if (useCycleFilter)
|
||||
{
|
||||
if (!cycleLease.IsValid || divisor < cycleLeaseStart || divisor > cycleLeaseEnd)
|
||||
{
|
||||
cycleLease.Dispose();
|
||||
cycleLease = DivisorCycleCache.Shared.Acquire(divisor);
|
||||
cycleLeaseStart = cycleLease.Start;
|
||||
cycleLeaseEnd = cycleLease.End;
|
||||
cycleValues = cycleLease.Values;
|
||||
}
|
||||
else if (cycleValues is null)
|
||||
{
|
||||
cycleValues = cycleLease.Values;
|
||||
}
|
||||
|
||||
if (cycleValues is not null && divisor >= cycleLeaseStart)
|
||||
{
|
||||
ulong offset = divisor - cycleLeaseStart;
|
||||
if (offset < (ulong)cycleValues.Length)
|
||||
{
|
||||
divisorCycle = cycleValues[(int)offset];
|
||||
}
|
||||
else
|
||||
{
|
||||
divisorCycle = cycleLease.GetCycle(divisor);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
divisorCycle = cycleLease.GetCycle(divisor);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
divisorCycle = 0UL;
|
||||
}
|
||||
|
||||
// Can we prepare as many buffers as many threads were requested upfront and have them prepared in separate task, so that they're always ready when needed?
|
||||
activeCount = BuildPrimeBuffer(divisor, primeValues, allowedMaxValues, stateFlags, primeBuffer, indexBuffer, completionsBuffer, ref completionsCount, ref remainingStates, activeStateMask, ref activeStartIndex);
|
||||
|
||||
if (completionsCount > 0)
|
||||
{
|
||||
@@ -953,9 +998,9 @@ internal static class Program
|
||||
continue;
|
||||
}
|
||||
|
||||
hitsSpan = hitsBuffer.AsSpan(0, activeCount);
|
||||
hitsSpan.Clear();
|
||||
session.CheckDivisor(divisor, primeBuffer.AsSpan(0, activeCount), hitsSpan);
|
||||
hitsSpan = hitsBuffer.AsSpan(0, activeCount);
|
||||
hitsSpan.Clear();
|
||||
session.CheckDivisor(divisor, divisorCycle, primeBuffer.AsSpan(0, activeCount), hitsSpan);
|
||||
|
||||
for (hitIndex = 0; hitIndex < activeCount; hitIndex++)
|
||||
{
|
||||
@@ -993,14 +1038,15 @@ internal static class Program
|
||||
{
|
||||
FlushPendingResults(compositesBuffer, ref compositesCount);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<PendingResult>.Shared.Return(compositesBuffer, clearArray: true);
|
||||
ArrayPool<PendingResult>.Shared.Return(completionsBuffer, clearArray: true);
|
||||
ArrayPool<int>.Shared.Return(indexBuffer, clearArray: true);
|
||||
ArrayPool<ulong>.Shared.Return(primeBuffer, clearArray: true);
|
||||
ArrayPool<byte>.Shared.Return(hitsBuffer, clearArray: true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
cycleLease.Dispose();
|
||||
ArrayPool<PendingResult>.Shared.Return(compositesBuffer, clearArray: true);
|
||||
ArrayPool<PendingResult>.Shared.Return(completionsBuffer, clearArray: true);
|
||||
ArrayPool<int>.Shared.Return(indexBuffer, clearArray: true);
|
||||
ArrayPool<ulong>.Shared.Return(primeBuffer, clearArray: true);
|
||||
ArrayPool<byte>.Shared.Return(hitsBuffer, clearArray: true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using System.Reflection;
|
||||
using Xunit;
|
||||
@@ -36,7 +37,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
typeof(MersenneNumberDivisorGpuTester)
|
||||
.GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!
|
||||
.SetValue(null, Array.Empty<(ulong, uint)>());
|
||||
.SetValue(null, Array.Empty<(ulong, ulong)>());
|
||||
|
||||
tester.IsPrime(11UL, UInt128.Zero, 0UL, out bool divisorsExhausted).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeFalse();
|
||||
@@ -112,11 +113,13 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
ulong[] primes = { 5UL, 7UL, 11UL, 13UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
|
||||
session.CheckDivisor(23UL, primes, hits);
|
||||
ulong cycle23 = MersenneDivisorCycles.CalculateCycleLength(23UL);
|
||||
session.CheckDivisor(23UL, cycle23, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
|
||||
|
||||
Array.Fill(hits, (byte)0);
|
||||
session.CheckDivisor(31UL, primes, hits);
|
||||
ulong cycle31 = MersenneDivisorCycles.CalculateCycleLength(31UL);
|
||||
session.CheckDivisor(31UL, cycle31, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ public class MersenneNumberOrderGpuTesterTests
|
||||
var smallCyclesField = typeof(MersenneDivisorCycles).GetField("_smallCycles", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||
|
||||
var originalTable = ((List<(ulong Divisor, ulong Cycle)>)tableField.GetValue(cycles)!).Select(x => x).ToList();
|
||||
var originalSmall = (uint[]?)smallCyclesField.GetValue(cycles);
|
||||
uint[]? backupSmall = originalSmall is null ? null : (uint[])originalSmall.Clone();
|
||||
var originalSmall = (ulong[]?)smallCyclesField.GetValue(cycles);
|
||||
ulong[]? backupSmall = originalSmall is null ? null : (ulong[])originalSmall.Clone();
|
||||
|
||||
cycles.LoadFrom(tempPath);
|
||||
GpuContextPool.DisposeAll();
|
||||
|
||||
@@ -0,0 +1,414 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
public sealed class DivisorCycleCache
|
||||
{
|
||||
internal sealed class CycleBlock
|
||||
{
|
||||
private int _referenceCount;
|
||||
|
||||
internal CycleBlock(int index, ulong start, ulong[] cycles)
|
||||
{
|
||||
Index = index;
|
||||
Start = start;
|
||||
Cycles = cycles;
|
||||
End = start + (ulong)cycles.Length - 1UL;
|
||||
}
|
||||
|
||||
internal int Index { get; }
|
||||
|
||||
internal ulong Start { get; }
|
||||
|
||||
internal ulong End { get; }
|
||||
|
||||
internal ulong[] Cycles { get; }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void AddRef()
|
||||
{
|
||||
Interlocked.Increment(ref _referenceCount);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void Release()
|
||||
{
|
||||
Interlocked.Decrement(ref _referenceCount);
|
||||
}
|
||||
|
||||
internal bool IsInUse => Volatile.Read(ref _referenceCount) > 0;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal ulong GetCycle(ulong divisor)
|
||||
{
|
||||
if (divisor < Start || divisor > End)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
return Cycles[(int)(divisor - Start)];
|
||||
}
|
||||
}
|
||||
|
||||
public struct Lease : IDisposable
|
||||
{
|
||||
private readonly DivisorCycleCache? _owner;
|
||||
private CycleBlock? _block;
|
||||
|
||||
internal Lease(DivisorCycleCache owner, CycleBlock block)
|
||||
{
|
||||
_owner = owner;
|
||||
_block = block;
|
||||
_block.AddRef();
|
||||
}
|
||||
|
||||
public bool IsValid => _block is not null;
|
||||
|
||||
public ulong Start => _block?.Start ?? 0UL;
|
||||
|
||||
public ulong End => _block?.End ?? 0UL;
|
||||
|
||||
public ulong[]? Values => _block?.Cycles;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ulong GetCycle(ulong divisor)
|
||||
{
|
||||
return _block?.GetCycle(divisor) ?? 0UL;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CycleBlock? block = _block;
|
||||
if (block is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
block.Release();
|
||||
_owner?.Release(block);
|
||||
_block = null;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly object _sync = new();
|
||||
private readonly ConcurrentDictionary<int, Task<CycleBlock>> _pending = new();
|
||||
private CycleBlock _baseBlock = null!;
|
||||
private CycleBlock? _previous;
|
||||
private CycleBlock? _current;
|
||||
private CycleBlock? _next;
|
||||
private bool _initialized;
|
||||
|
||||
private static readonly Lazy<DivisorCycleCache> _lazy = new(() => new DivisorCycleCache());
|
||||
|
||||
public static DivisorCycleCache Shared => _lazy.Value;
|
||||
|
||||
private DivisorCycleCache()
|
||||
{
|
||||
ulong[] snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
||||
Initialize(snapshot);
|
||||
}
|
||||
|
||||
public void ReloadFromCurrentSnapshot()
|
||||
{
|
||||
ulong[] snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
||||
Initialize(snapshot);
|
||||
}
|
||||
|
||||
private void Initialize(ulong[] snapshot)
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
_pending.Clear();
|
||||
_baseBlock = new CycleBlock(index: 0, start: 0UL, snapshot);
|
||||
_previous = _baseBlock;
|
||||
_current = null;
|
||||
_next = null;
|
||||
_initialized = true;
|
||||
StartPrefetchLocked(1);
|
||||
}
|
||||
}
|
||||
|
||||
public Lease Acquire(ulong divisor)
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
throw new InvalidOperationException("DivisorCycleCache must be initialized before use.");
|
||||
}
|
||||
|
||||
if (divisor <= _baseBlock.End)
|
||||
{
|
||||
return new Lease(this, _baseBlock);
|
||||
}
|
||||
|
||||
CycleBlock block = AcquireDynamicBlock(divisor);
|
||||
return new Lease(this, block);
|
||||
}
|
||||
|
||||
private CycleBlock AcquireDynamicBlock(ulong divisor)
|
||||
{
|
||||
int blockIndex = GetBlockIndex(divisor);
|
||||
while (true)
|
||||
{
|
||||
Task<CycleBlock>? taskToAwait = null;
|
||||
lock (_sync)
|
||||
{
|
||||
if (TryGetCachedBlockLocked(blockIndex, out CycleBlock? cached))
|
||||
{
|
||||
return cached!;
|
||||
}
|
||||
|
||||
if (!_pending.TryGetValue(blockIndex, out taskToAwait))
|
||||
{
|
||||
taskToAwait = Task.Run(() => GenerateBlock(blockIndex));
|
||||
_pending[blockIndex] = taskToAwait;
|
||||
}
|
||||
}
|
||||
|
||||
CycleBlock block = taskToAwait.GetAwaiter().GetResult();
|
||||
lock (_sync)
|
||||
{
|
||||
_pending.TryRemove(blockIndex, out _);
|
||||
|
||||
if (TryGetCachedBlockLocked(blockIndex, out CycleBlock? cachedAfterAwait))
|
||||
{
|
||||
return cachedAfterAwait!;
|
||||
}
|
||||
|
||||
PromoteBlockLocked(block);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetCachedBlockLocked(int blockIndex, out CycleBlock? block)
|
||||
{
|
||||
if (_current is { Index: var currentIndex } current && currentIndex == blockIndex)
|
||||
{
|
||||
block = current;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_previous is { Index: var previousIndex } previous && previousIndex == blockIndex)
|
||||
{
|
||||
block = previous;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_next is { Index: var nextIndex } next && nextIndex == blockIndex)
|
||||
{
|
||||
PromoteNextLocked();
|
||||
block = _current!;
|
||||
return true;
|
||||
}
|
||||
|
||||
block = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void PromoteNextLocked()
|
||||
{
|
||||
if (_next is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CycleBlock? oldPrevious = _previous;
|
||||
_previous = _current ?? _baseBlock;
|
||||
_current = _next;
|
||||
_next = null;
|
||||
StartPrefetchLocked(_current!.Index + 1);
|
||||
TryReleaseBlockLocked(oldPrevious);
|
||||
}
|
||||
|
||||
private void PromoteBlockLocked(CycleBlock block)
|
||||
{
|
||||
if (_current is null)
|
||||
{
|
||||
_previous = _baseBlock;
|
||||
_current = block;
|
||||
StartPrefetchLocked(block.Index + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (block.Index > _current.Index)
|
||||
{
|
||||
CycleBlock? oldPrevious = _previous;
|
||||
_previous = _current;
|
||||
_current = block;
|
||||
StartPrefetchLocked(block.Index + 1);
|
||||
TryReleaseBlockLocked(oldPrevious);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_previous is null || block.Index >= _previous.Index)
|
||||
{
|
||||
CycleBlock? oldPrevious = _previous;
|
||||
_previous = block;
|
||||
TryReleaseBlockLocked(oldPrevious);
|
||||
}
|
||||
}
|
||||
|
||||
private void StartPrefetchLocked(int blockIndex)
|
||||
{
|
||||
if (blockIndex <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_next is { Index: var nextIndex } next && nextIndex == blockIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pending.TryGetValue(blockIndex, out Task<CycleBlock>? existing))
|
||||
{
|
||||
if (existing.IsCompletedSuccessfully)
|
||||
{
|
||||
if (_next is null || _next.Index < blockIndex)
|
||||
{
|
||||
_next = existing.Result;
|
||||
}
|
||||
|
||||
_pending.TryRemove(blockIndex, out _);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Task<CycleBlock> task = Task.Run(() => GenerateBlock(blockIndex));
|
||||
_pending[blockIndex] = task;
|
||||
task.ContinueWith(t => OnPrefetchCompleted(blockIndex, t), TaskScheduler.Default);
|
||||
}
|
||||
|
||||
private void OnPrefetchCompleted(int blockIndex, Task<CycleBlock> task)
|
||||
{
|
||||
if (!task.IsCompletedSuccessfully)
|
||||
{
|
||||
_pending.TryRemove(blockIndex, out _);
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
{
|
||||
if (_next is null || _next.Index < blockIndex)
|
||||
{
|
||||
_next = task.Result;
|
||||
}
|
||||
|
||||
_pending.TryRemove(blockIndex, out _);
|
||||
TrimCacheLocked();
|
||||
}
|
||||
}
|
||||
|
||||
private void TrimCacheLocked()
|
||||
{
|
||||
if (_previous is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(_previous, _baseBlock))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_previous.IsInUse)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_next is not null && _current is not null)
|
||||
{
|
||||
_previous = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void TryReleaseBlockLocked(CycleBlock? block)
|
||||
{
|
||||
if (block is null || ReferenceEquals(block, _baseBlock))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(block, _current) || ReferenceEquals(block, _next))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (block.IsInUse)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(block, _previous))
|
||||
{
|
||||
_previous = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void Release(CycleBlock block)
|
||||
{
|
||||
if (!block.IsInUse)
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
TryReleaseBlockLocked(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CycleBlock GenerateBlock(int blockIndex)
|
||||
{
|
||||
if (blockIndex == 0)
|
||||
{
|
||||
return _baseBlock;
|
||||
}
|
||||
|
||||
ulong start = GetBlockStart(blockIndex);
|
||||
int length = GetBlockLength(blockIndex);
|
||||
ulong[] cycles = new ulong[length];
|
||||
ulong divisor = start;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
cycles[i] = MersenneDivisorCycles.CalculateCycleLength(divisor);
|
||||
divisor++;
|
||||
}
|
||||
|
||||
return new CycleBlock(blockIndex, start, cycles);
|
||||
}
|
||||
|
||||
private int GetBlockLength(int blockIndex)
|
||||
{
|
||||
if (blockIndex == 0)
|
||||
{
|
||||
return _baseBlock.Cycles.Length;
|
||||
}
|
||||
|
||||
return PerfectNumberConstants.MaxQForDivisorCycles;
|
||||
}
|
||||
|
||||
private ulong GetBlockStart(int blockIndex)
|
||||
{
|
||||
if (blockIndex == 0)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
ulong baseEnd = _baseBlock.End;
|
||||
return baseEnd + 1UL + (ulong)(blockIndex - 1) * (ulong)PerfectNumberConstants.MaxQForDivisorCycles;
|
||||
}
|
||||
|
||||
private int GetBlockIndex(ulong divisor)
|
||||
{
|
||||
if (divisor <= _baseBlock.End)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulong offset = divisor - (_baseBlock.End + 1UL);
|
||||
return (int)(offset / (ulong)PerfectNumberConstants.MaxQForDivisorCycles) + 1;
|
||||
}
|
||||
}
|
||||
@@ -27,19 +27,19 @@ public sealed class KernelContainer
|
||||
{
|
||||
// Serializes first-time initialization of kernels/buffers per accelerator.
|
||||
public Action<AcceleratorStream, Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>? Order;
|
||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
||||
ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>? Incremental;
|
||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
||||
ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>>? Incremental;
|
||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
||||
ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>,
|
||||
ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>,
|
||||
ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>,
|
||||
ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2Mod;
|
||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
||||
ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<uint, Stride1D.Dense>>? IncrementalOrder;
|
||||
ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>? IncrementalOrder;
|
||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
||||
ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<uint, Stride1D.Dense>>? Pow2ModOrder;
|
||||
ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2ModOrder;
|
||||
|
||||
// Optional device buffer with small divisor cycles (<= 4M). Index = divisor, value = cycle length.
|
||||
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallCycles;
|
||||
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallCycles;
|
||||
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastOne;
|
||||
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2LastOne;
|
||||
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastSeven;
|
||||
@@ -123,7 +123,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
||||
}
|
||||
}
|
||||
|
||||
public readonly Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> Pow2ModKernel
|
||||
public readonly Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> Pow2ModKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -132,15 +132,15 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
||||
{
|
||||
return KernelContainer.InitOnce(ref _kernels.Pow2Mod, () =>
|
||||
{
|
||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernelScan);
|
||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernelScan);
|
||||
var kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public readonly Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>> IncrementalKernel
|
||||
public readonly Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>> IncrementalKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -149,15 +149,15 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
||||
{
|
||||
return KernelContainer.InitOnce(ref _kernels.Incremental, () =>
|
||||
{
|
||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>>(IncrementalKernelScan);
|
||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernelScan);
|
||||
var kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>>>();
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public readonly Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<uint, Stride1D.Dense>> IncrementalOrderKernel
|
||||
public readonly Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>> IncrementalOrderKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -166,15 +166,15 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
||||
{
|
||||
return KernelContainer.InitOnce(ref _kernels.IncrementalOrder, () =>
|
||||
{
|
||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<uint, Stride1D.Dense>>(IncrementalOrderKernelScan);
|
||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalOrderKernelScan);
|
||||
var kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<uint, Stride1D.Dense>>>();
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public readonly Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<uint, Stride1D.Dense>> Pow2ModOrderKernel
|
||||
public readonly Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>> Pow2ModOrderKernel
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -183,9 +183,9 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
||||
{
|
||||
return KernelContainer.InitOnce(ref _kernels.Pow2ModOrder, () =>
|
||||
{
|
||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<uint, Stride1D.Dense>>(Pow2ModOrderKernelScan);
|
||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModOrderKernelScan);
|
||||
var kernel = KernelUtil.GetKernel(loaded);
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<uint, Stride1D.Dense>>>();
|
||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -196,7 +196,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
||||
|
||||
private static void IncrementalKernelScan(Index1D index, ulong exponent, GpuUInt128 twoP, GpuUInt128 kStart, byte lastIsSeven, ulong divMul,
|
||||
ulong q0m10, ulong q0m8, ulong q0m3, ulong q0m5, ArrayView<ulong> orders,
|
||||
ArrayView1D<uint, Stride1D.Dense> smallCycles)
|
||||
ArrayView1D<ulong, Stride1D.Dense> smallCycles)
|
||||
{
|
||||
ulong idx = (ulong)index.X;
|
||||
ulong idxMod3 = idx % 3UL;
|
||||
@@ -246,15 +246,11 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
||||
// Small-cycles in-kernel early rejection from device table
|
||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||
{
|
||||
uint cyc = smallCycles[(int)q.Low];
|
||||
if (cyc != 0U)
|
||||
ulong cycle = smallCycles[(int)q.Low];
|
||||
if (cycle != 0UL && cycle <= exponent && (exponent % cycle) != 0UL)
|
||||
{
|
||||
ulong cycle = cyc;
|
||||
if (cycle <= exponent && (exponent % cycle) != 0UL)
|
||||
{
|
||||
orders[index] = 0UL;
|
||||
return;
|
||||
}
|
||||
orders[index] = 0UL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,7 +348,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
||||
|
||||
private static void Pow2ModKernelScan(Index1D index, ulong exponent, GpuUInt128 twoP, GpuUInt128 kStart, byte lastIsSeven, ulong _,
|
||||
ResidueAutomatonArgs ra, ArrayView<ulong> orders,
|
||||
ArrayView1D<uint, Stride1D.Dense> smallCycles,
|
||||
ArrayView1D<ulong, Stride1D.Dense> smallCycles,
|
||||
ArrayView1D<uint, Stride1D.Dense> smallPrimesLastOne,
|
||||
ArrayView1D<uint, Stride1D.Dense> smallPrimesLastSeven,
|
||||
ArrayView1D<ulong, Stride1D.Dense> smallPrimesPow2LastOne,
|
||||
@@ -402,15 +398,11 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
||||
GpuUInt128 q = twoP.Mul64(k) + GpuUInt128.One;
|
||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||
{
|
||||
uint cyc = smallCycles[(int)q.Low];
|
||||
if (cyc != 0U)
|
||||
ulong cycle = smallCycles[(int)q.Low];
|
||||
if (cycle != 0UL && cycle <= exponent && (exponent % cycle) != 0UL)
|
||||
{
|
||||
ulong cycle = cyc;
|
||||
if (cycle <= exponent && (exponent % cycle) != 0UL)
|
||||
{
|
||||
orders[index] = 0UL;
|
||||
return;
|
||||
}
|
||||
orders[index] = 0UL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (GpuUInt128.Pow2Minus1Mod(exponent, q) != GpuUInt128.Zero)
|
||||
@@ -489,7 +481,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
||||
}
|
||||
|
||||
private static void IncrementalOrderKernelScan(Index1D index, ulong exponent, GpuUInt128 twoP, GpuUInt128 kStart, byte lastIsSeven, ulong divMul,
|
||||
ResidueAutomatonArgs ra, ArrayView<int> found, ArrayView1D<uint, Stride1D.Dense> smallCycles)
|
||||
ResidueAutomatonArgs ra, ArrayView<int> found, ArrayView1D<ulong, Stride1D.Dense> smallCycles)
|
||||
{
|
||||
ulong idx = (ulong)index.X;
|
||||
ulong idxMod3 = idx % 3UL;
|
||||
@@ -536,14 +528,10 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
||||
// Small-cycles in-kernel early rejection from device table
|
||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||
{
|
||||
uint cyc = smallCycles[(int)q.Low];
|
||||
if (cyc != 0U)
|
||||
ulong cycle = smallCycles[(int)q.Low];
|
||||
if (cycle != 0UL && cycle <= exponent && (exponent % cycle) != 0UL)
|
||||
{
|
||||
ulong cycle = cyc;
|
||||
if (cycle <= exponent && (exponent % cycle) != 0UL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
GpuUInt128 phi = q - GpuUInt128.One;
|
||||
@@ -575,7 +563,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
||||
}
|
||||
|
||||
private static void Pow2ModOrderKernelScan(Index1D index, ulong exponent, GpuUInt128 twoP, GpuUInt128 kStart, byte lastIsSeven, ulong _,
|
||||
ResidueAutomatonArgs ra, ArrayView<int> found, ArrayView1D<uint, Stride1D.Dense> smallCycles)
|
||||
ResidueAutomatonArgs ra, ArrayView<int> found, ArrayView1D<ulong, Stride1D.Dense> smallCycles)
|
||||
{
|
||||
ulong idx = (ulong)index.X;
|
||||
ulong idxMod3 = idx % 3UL;
|
||||
@@ -621,14 +609,10 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
||||
// Small-cycles in-kernel early rejection from device table
|
||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||
{
|
||||
uint cyc = smallCycles[(int)q.Low];
|
||||
if (cyc != 0U)
|
||||
ulong cycle = smallCycles[(int)q.Low];
|
||||
if (cycle != 0UL && cycle <= exponent && (exponent % cycle) != 0UL)
|
||||
{
|
||||
ulong cycle = cyc;
|
||||
if (cycle <= exponent && (exponent % cycle) != 0UL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (GpuUInt128.Pow2Mod(exponent, q) != GpuUInt128.One)
|
||||
@@ -681,7 +665,7 @@ public class GpuKernelPool
|
||||
|
||||
// Ensures the small cycles table is uploaded to the device for the given accelerator.
|
||||
// Returns the ArrayView to pass into kernels (when kernels are extended to accept it).
|
||||
public static ArrayView1D<uint, Stride1D.Dense> EnsureSmallCyclesOnDevice(Accelerator accelerator)
|
||||
public static ArrayView1D<ulong, Stride1D.Dense> EnsureSmallCyclesOnDevice(Accelerator accelerator)
|
||||
{
|
||||
var kernels = GetKernels(accelerator);
|
||||
if (kernels.SmallCycles is { } buffer)
|
||||
@@ -698,7 +682,7 @@ public class GpuKernelPool
|
||||
}
|
||||
|
||||
var host = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
||||
var device = accelerator.Allocate1D<uint>(host.Length);
|
||||
var device = accelerator.Allocate1D<ulong>(host.Length);
|
||||
device.View.CopyFromCPU(host);
|
||||
kernels.SmallCycles = device;
|
||||
return device.View;
|
||||
|
||||
@@ -389,12 +389,12 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
|
||||
_positionBuffer = ArrayPool<int>.Shared.Rent(_capacity);
|
||||
}
|
||||
|
||||
public void CheckDivisor(ulong divisor, ReadOnlySpan<ulong> primes, Span<byte> hits)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(DivisorScanSession));
|
||||
}
|
||||
public void CheckDivisor(ulong divisor, ulong divisorCycle, ReadOnlySpan<ulong> primes, Span<byte> hits)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(DivisorScanSession));
|
||||
}
|
||||
|
||||
int primesLength = primes.Length;
|
||||
if (primesLength == 0)
|
||||
@@ -413,14 +413,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
|
||||
return;
|
||||
}
|
||||
|
||||
bool useCycles = owner._useDivisorCycles;
|
||||
ulong cycle = 0UL;
|
||||
bool cycleEnabled = false;
|
||||
if (useCycles)
|
||||
{
|
||||
cycle = MersenneDivisorCycles.CalculateCycleLengthGpu(modulus);
|
||||
cycleEnabled = cycle != 0UL;
|
||||
}
|
||||
bool cycleEnabled = owner._useDivisorCycles && divisorCycle != 0UL;
|
||||
|
||||
Accelerator accelerator = _accelerator;
|
||||
Action<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<ulong>> kernel = _kernel;
|
||||
@@ -448,11 +441,11 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
|
||||
for (int i = 0; i < batchSize; i++)
|
||||
{
|
||||
ulong prime = primesSlice[i];
|
||||
if (cycleEnabled && prime % cycle != 0UL)
|
||||
{
|
||||
hitsSlice[i] = 0;
|
||||
continue;
|
||||
}
|
||||
if (cycleEnabled && prime % divisorCycle != 0UL)
|
||||
{
|
||||
hitsSlice[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
primeSpan[computeCount] = prime;
|
||||
positionSpan[computeCount] = i;
|
||||
|
||||
@@ -15,9 +15,9 @@ public sealed class MersenneNumberDivisorGpuTester
|
||||
|
||||
public static void BuildDivisorCandidates()
|
||||
{
|
||||
uint[] snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
||||
(ulong divisor, uint cycle)[] list = new (ulong divisor, uint cycle)[snapshot.Length / 2];
|
||||
uint cycle;
|
||||
ulong[] snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
||||
(ulong divisor, ulong cycle)[] list = new (ulong divisor, ulong cycle)[snapshot.Length / 2];
|
||||
ulong cycle;
|
||||
int count = 0, i, snapshotLength = snapshot.Length;
|
||||
for (i = 3; i < snapshotLength; i += 2)
|
||||
{
|
||||
@@ -30,7 +30,7 @@ public sealed class MersenneNumberDivisorGpuTester
|
||||
list[count++] = ((ulong)i, cycle);
|
||||
}
|
||||
|
||||
_divisorCandidates = count == 0 ? [] : list[..count];
|
||||
_divisorCandidates = count == 0 ? [] : list[..count];
|
||||
}
|
||||
|
||||
public bool IsDivisible(ulong exponent, UInt128 divisor)
|
||||
@@ -52,7 +52,7 @@ public sealed class MersenneNumberDivisorGpuTester
|
||||
return divisible;
|
||||
}
|
||||
|
||||
private static (ulong divisor, uint cycle)[]? _divisorCandidates = Array.Empty<(ulong divisor, uint cycle)>();
|
||||
private static (ulong divisor, ulong cycle)[]? _divisorCandidates = Array.Empty<(ulong divisor, ulong cycle)>();
|
||||
|
||||
public bool IsPrime(ulong p, UInt128 d, ulong divisorCyclesSearchLimit, out bool divisorsExhausted)
|
||||
{
|
||||
@@ -71,7 +71,7 @@ public sealed class MersenneNumberDivisorGpuTester
|
||||
if (_divisorCandidates is { Length: > 0 } candidates)
|
||||
{
|
||||
int len = candidates.Length;
|
||||
uint cycle;
|
||||
ulong cycle;
|
||||
for (int k = 0; k < len; k++)
|
||||
{
|
||||
(ulong dSmall, cycle) = candidates[k];
|
||||
|
||||
@@ -9,9 +9,9 @@ namespace PerfectNumbers.Core;
|
||||
|
||||
public class MersenneDivisorCycles
|
||||
{
|
||||
private List<(ulong divisor, ulong cycleLength)> _table = [];
|
||||
// Lightweight read-mostly cache for small divisors (<= 4,000,000). 0 => unknown
|
||||
private uint[]? _smallCycles;
|
||||
private List<(ulong divisor, ulong cycleLength)> _table = [];
|
||||
// Lightweight read-mostly cache for small divisors (<= 4,000,000). 0 => unknown
|
||||
private ulong[]? _smallCycles;
|
||||
|
||||
public static MersenneDivisorCycles Shared { get; } = new MersenneDivisorCycles();
|
||||
|
||||
@@ -22,15 +22,15 @@ public class MersenneDivisorCycles
|
||||
public void LoadFrom(string path)
|
||||
{
|
||||
EnsureSmallBuffer();
|
||||
List<(ulong divisor, ulong cycle)> cycles = [];
|
||||
List<(ulong divisor, ulong cycle)> cycles = [];
|
||||
using Stream outputStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize10M, useAsync: false);
|
||||
foreach (var (d, c) in EnumerateStream(outputStream))
|
||||
{
|
||||
cycles.Add((d, c));
|
||||
if (d <= PerfectNumberConstants.MaxQForDivisorCycles)
|
||||
{
|
||||
_smallCycles![(int)d] = (uint)Math.Min(uint.MaxValue, c == 0UL ? 1UL : c);
|
||||
}
|
||||
if (d <= PerfectNumberConstants.MaxQForDivisorCycles)
|
||||
{
|
||||
_smallCycles![(int)d] = c == 0UL ? 1UL : c;
|
||||
}
|
||||
}
|
||||
|
||||
cycles.Sort((a, b) => a.divisor < b.divisor ? -1 : (a.divisor > b.divisor ? 1 : 0));
|
||||
@@ -41,13 +41,13 @@ public class MersenneDivisorCycles
|
||||
|
||||
// Provides a snapshot array of small cycles [0..PerfectNumberConstants.MaxQForDivisorCycles]. Index is the divisor.
|
||||
// The returned array is a copy safe for use across threads and device uploads.
|
||||
public uint[] ExportSmallCyclesSnapshot()
|
||||
{
|
||||
EnsureSmallBuffer();
|
||||
uint[] snapshot = new uint[PerfectNumberConstants.MaxQForDivisorCycles + 1];
|
||||
Array.Copy(_smallCycles!, snapshot, snapshot.Length);
|
||||
return snapshot;
|
||||
}
|
||||
public ulong[] ExportSmallCyclesSnapshot()
|
||||
{
|
||||
EnsureSmallBuffer();
|
||||
ulong[] snapshot = new ulong[PerfectNumberConstants.MaxQForDivisorCycles + 1];
|
||||
Array.Copy(_smallCycles!, snapshot, snapshot.Length);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
public static IEnumerable<(ulong divisor, ulong cycleLength)> EnumerateStream(Stream compressor)
|
||||
{
|
||||
@@ -75,14 +75,14 @@ public class MersenneDivisorCycles
|
||||
// Fast-path: in-memory array for small divisors
|
||||
if (divisor <= PerfectNumberConstants.MaxQForDivisorCycles)
|
||||
{
|
||||
var arr = _smallCycles;
|
||||
var arr = _smallCycles;
|
||||
if (arr is not null)
|
||||
{
|
||||
uint cached = arr[(int)divisor];
|
||||
if (cached != 0U)
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
ulong cached = arr[(int)divisor];
|
||||
if (cached != 0UL)
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ public class MersenneDivisorCycles
|
||||
}
|
||||
}
|
||||
|
||||
return CalculateCycleLength(divisor);
|
||||
return CalculateCycleLength(divisor);
|
||||
}
|
||||
|
||||
public static UInt128 GetCycle(UInt128 divisor)
|
||||
@@ -396,9 +396,9 @@ public class MersenneDivisorCycles
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void EnsureSmallBuffer()
|
||||
{
|
||||
if (_smallCycles is null)
|
||||
{
|
||||
_smallCycles = new uint[PerfectNumberConstants.MaxQForDivisorCycles + 1];
|
||||
}
|
||||
}
|
||||
if (_smallCycles is null)
|
||||
{
|
||||
_smallCycles = new ulong[PerfectNumberConstants.MaxQForDivisorCycles + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user