Implement multi-tier divisor cycle caching and per-thread reuse
This commit is contained in:
1 parent
e92bca197d
commit
d4dcd0a534
8 files changed
+515
-75
No files matched your search
@@ -456,6 +456,8 @@ internal static class Program
|
|||||||
MersenneDivisorCycles.Shared.LoadFrom(cyclesPath);
|
MersenneDivisorCycles.Shared.LoadFrom(cyclesPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DivisorCycleCache.Shared.ReloadFromCurrentSnapshot();
|
||||||
|
|
||||||
Console.WriteLine("Divisor cycles are ready");
|
Console.WriteLine("Divisor cycles are ready");
|
||||||
|
|
||||||
if (useByDivisor)
|
if (useByDivisor)
|
||||||
@@ -885,6 +887,12 @@ internal static class Program
|
|||||||
Span<byte> hitsSpan = default;
|
Span<byte> hitsSpan = default;
|
||||||
int hitIndex = 0;
|
int hitIndex = 0;
|
||||||
int index = 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
|
||||||
{
|
{
|
||||||
@@ -940,6 +948,43 @@ internal static class Program
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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?
|
// 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);
|
activeCount = BuildPrimeBuffer(divisor, primeValues, allowedMaxValues, stateFlags, primeBuffer, indexBuffer, completionsBuffer, ref completionsCount, ref remainingStates, activeStateMask, ref activeStartIndex);
|
||||||
|
|
||||||
@@ -955,7 +1000,7 @@ internal static class Program
|
|||||||
|
|
||||||
hitsSpan = hitsBuffer.AsSpan(0, activeCount);
|
hitsSpan = hitsBuffer.AsSpan(0, activeCount);
|
||||||
hitsSpan.Clear();
|
hitsSpan.Clear();
|
||||||
session.CheckDivisor(divisor, primeBuffer.AsSpan(0, activeCount), hitsSpan);
|
session.CheckDivisor(divisor, divisorCycle, primeBuffer.AsSpan(0, activeCount), hitsSpan);
|
||||||
|
|
||||||
for (hitIndex = 0; hitIndex < activeCount; hitIndex++)
|
for (hitIndex = 0; hitIndex < activeCount; hitIndex++)
|
||||||
{
|
{
|
||||||
@@ -996,6 +1041,7 @@ internal static class Program
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
cycleLease.Dispose();
|
||||||
ArrayPool<PendingResult>.Shared.Return(compositesBuffer, clearArray: true);
|
ArrayPool<PendingResult>.Shared.Return(compositesBuffer, clearArray: true);
|
||||||
ArrayPool<PendingResult>.Shared.Return(completionsBuffer, clearArray: true);
|
ArrayPool<PendingResult>.Shared.Return(completionsBuffer, clearArray: true);
|
||||||
ArrayPool<int>.Shared.Return(indexBuffer, clearArray: true);
|
ArrayPool<int>.Shared.Return(indexBuffer, clearArray: true);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using PerfectNumbers.Core;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@@ -36,7 +37,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
var tester = new MersenneNumberDivisorGpuTester();
|
var tester = new MersenneNumberDivisorGpuTester();
|
||||||
typeof(MersenneNumberDivisorGpuTester)
|
typeof(MersenneNumberDivisorGpuTester)
|
||||||
.GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!
|
.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();
|
tester.IsPrime(11UL, UInt128.Zero, 0UL, out bool divisorsExhausted).Should().BeTrue();
|
||||||
divisorsExhausted.Should().BeFalse();
|
divisorsExhausted.Should().BeFalse();
|
||||||
@@ -112,11 +113,13 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
ulong[] primes = { 5UL, 7UL, 11UL, 13UL };
|
ulong[] primes = { 5UL, 7UL, 11UL, 13UL };
|
||||||
byte[] hits = new byte[primes.Length];
|
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 });
|
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
|
||||||
|
|
||||||
Array.Fill(hits, (byte)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 });
|
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 smallCyclesField = typeof(MersenneDivisorCycles).GetField("_smallCycles", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||||
|
|
||||||
var originalTable = ((List<(ulong Divisor, ulong Cycle)>)tableField.GetValue(cycles)!).Select(x => x).ToList();
|
var originalTable = ((List<(ulong Divisor, ulong Cycle)>)tableField.GetValue(cycles)!).Select(x => x).ToList();
|
||||||
var originalSmall = (uint[]?)smallCyclesField.GetValue(cycles);
|
var originalSmall = (ulong[]?)smallCyclesField.GetValue(cycles);
|
||||||
uint[]? backupSmall = originalSmall is null ? null : (uint[])originalSmall.Clone();
|
ulong[]? backupSmall = originalSmall is null ? null : (ulong[])originalSmall.Clone();
|
||||||
|
|
||||||
cycles.LoadFrom(tempPath);
|
cycles.LoadFrom(tempPath);
|
||||||
GpuContextPool.DisposeAll();
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,18 +28,18 @@ public sealed class KernelContainer
|
|||||||
// Serializes first-time initialization of kernels/buffers per accelerator.
|
// 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, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>? Order;
|
||||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
||||||
ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<uint, Stride1D.Dense>>? Incremental;
|
ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>? Incremental;
|
||||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
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<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>,
|
||||||
ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2Mod;
|
ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2Mod;
|
||||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
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,
|
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.
|
// 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<uint, Stride1D.Dense>? SmallPrimesLastOne;
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2LastOne;
|
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2LastOne;
|
||||||
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastSeven;
|
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
|
get
|
||||||
{
|
{
|
||||||
@@ -132,15 +132,15 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
|||||||
{
|
{
|
||||||
return KernelContainer.InitOnce(ref _kernels.Pow2Mod, () =>
|
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);
|
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
|
get
|
||||||
{
|
{
|
||||||
@@ -149,15 +149,15 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
|||||||
{
|
{
|
||||||
return KernelContainer.InitOnce(ref _kernels.Incremental, () =>
|
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);
|
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
|
get
|
||||||
{
|
{
|
||||||
@@ -166,15 +166,15 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
|||||||
{
|
{
|
||||||
return KernelContainer.InitOnce(ref _kernels.IncrementalOrder, () =>
|
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);
|
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
|
get
|
||||||
{
|
{
|
||||||
@@ -183,9 +183,9 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
|||||||
{
|
{
|
||||||
return KernelContainer.InitOnce(ref _kernels.Pow2ModOrder, () =>
|
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);
|
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,
|
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,
|
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 idx = (ulong)index.X;
|
||||||
ulong idxMod3 = idx % 3UL;
|
ulong idxMod3 = idx % 3UL;
|
||||||
@@ -246,17 +246,13 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
|||||||
// Small-cycles in-kernel early rejection from device table
|
// Small-cycles in-kernel early rejection from device table
|
||||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||||
{
|
{
|
||||||
uint cyc = smallCycles[(int)q.Low];
|
ulong cycle = smallCycles[(int)q.Low];
|
||||||
if (cyc != 0U)
|
if (cycle != 0UL && cycle <= exponent && (exponent % cycle) != 0UL)
|
||||||
{
|
|
||||||
ulong cycle = cyc;
|
|
||||||
if (cycle <= exponent && (exponent % cycle) != 0UL)
|
|
||||||
{
|
{
|
||||||
orders[index] = 0UL;
|
orders[index] = 0UL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
GpuUInt128 phi = q - GpuUInt128.One;
|
GpuUInt128 phi = q - GpuUInt128.One;
|
||||||
if (phi.High != 0UL)
|
if (phi.High != 0UL)
|
||||||
@@ -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 _,
|
private static void Pow2ModKernelScan(Index1D index, ulong exponent, GpuUInt128 twoP, GpuUInt128 kStart, byte lastIsSeven, ulong _,
|
||||||
ResidueAutomatonArgs ra, ArrayView<ulong> orders,
|
ResidueAutomatonArgs ra, ArrayView<ulong> orders,
|
||||||
ArrayView1D<uint, Stride1D.Dense> smallCycles,
|
ArrayView1D<ulong, Stride1D.Dense> smallCycles,
|
||||||
ArrayView1D<uint, Stride1D.Dense> smallPrimesLastOne,
|
ArrayView1D<uint, Stride1D.Dense> smallPrimesLastOne,
|
||||||
ArrayView1D<uint, Stride1D.Dense> smallPrimesLastSeven,
|
ArrayView1D<uint, Stride1D.Dense> smallPrimesLastSeven,
|
||||||
ArrayView1D<ulong, Stride1D.Dense> smallPrimesPow2LastOne,
|
ArrayView1D<ulong, Stride1D.Dense> smallPrimesPow2LastOne,
|
||||||
@@ -402,17 +398,13 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
|||||||
GpuUInt128 q = twoP.Mul64(k) + GpuUInt128.One;
|
GpuUInt128 q = twoP.Mul64(k) + GpuUInt128.One;
|
||||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||||
{
|
{
|
||||||
uint cyc = smallCycles[(int)q.Low];
|
ulong cycle = smallCycles[(int)q.Low];
|
||||||
if (cyc != 0U)
|
if (cycle != 0UL && cycle <= exponent && (exponent % cycle) != 0UL)
|
||||||
{
|
|
||||||
ulong cycle = cyc;
|
|
||||||
if (cycle <= exponent && (exponent % cycle) != 0UL)
|
|
||||||
{
|
{
|
||||||
orders[index] = 0UL;
|
orders[index] = 0UL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (GpuUInt128.Pow2Minus1Mod(exponent, q) != GpuUInt128.Zero)
|
if (GpuUInt128.Pow2Minus1Mod(exponent, q) != GpuUInt128.Zero)
|
||||||
{
|
{
|
||||||
orders[index] = 0UL;
|
orders[index] = 0UL;
|
||||||
@@ -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,
|
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 idx = (ulong)index.X;
|
||||||
ulong idxMod3 = idx % 3UL;
|
ulong idxMod3 = idx % 3UL;
|
||||||
@@ -536,16 +528,12 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
|||||||
// Small-cycles in-kernel early rejection from device table
|
// Small-cycles in-kernel early rejection from device table
|
||||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||||
{
|
{
|
||||||
uint cyc = smallCycles[(int)q.Low];
|
ulong cycle = smallCycles[(int)q.Low];
|
||||||
if (cyc != 0U)
|
if (cycle != 0UL && cycle <= exponent && (exponent % cycle) != 0UL)
|
||||||
{
|
|
||||||
ulong cycle = cyc;
|
|
||||||
if (cycle <= exponent && (exponent % cycle) != 0UL)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
GpuUInt128 phi = q - GpuUInt128.One;
|
GpuUInt128 phi = q - GpuUInt128.One;
|
||||||
if (phi.High != 0UL)
|
if (phi.High != 0UL)
|
||||||
{
|
{
|
||||||
@@ -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 _,
|
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 idx = (ulong)index.X;
|
||||||
ulong idxMod3 = idx % 3UL;
|
ulong idxMod3 = idx % 3UL;
|
||||||
@@ -621,16 +609,12 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
|
|||||||
// Small-cycles in-kernel early rejection from device table
|
// Small-cycles in-kernel early rejection from device table
|
||||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||||
{
|
{
|
||||||
uint cyc = smallCycles[(int)q.Low];
|
ulong cycle = smallCycles[(int)q.Low];
|
||||||
if (cyc != 0U)
|
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)
|
if (GpuUInt128.Pow2Mod(exponent, q) != GpuUInt128.One)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -681,7 +665,7 @@ public class GpuKernelPool
|
|||||||
|
|
||||||
// Ensures the small cycles table is uploaded to the device for the given accelerator.
|
// 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).
|
// 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);
|
var kernels = GetKernels(accelerator);
|
||||||
if (kernels.SmallCycles is { } buffer)
|
if (kernels.SmallCycles is { } buffer)
|
||||||
@@ -698,7 +682,7 @@ public class GpuKernelPool
|
|||||||
}
|
}
|
||||||
|
|
||||||
var host = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
var host = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
||||||
var device = accelerator.Allocate1D<uint>(host.Length);
|
var device = accelerator.Allocate1D<ulong>(host.Length);
|
||||||
device.View.CopyFromCPU(host);
|
device.View.CopyFromCPU(host);
|
||||||
kernels.SmallCycles = device;
|
kernels.SmallCycles = device;
|
||||||
return device.View;
|
return device.View;
|
||||||
|
|||||||
@@ -389,7 +389,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
|
|||||||
_positionBuffer = ArrayPool<int>.Shared.Rent(_capacity);
|
_positionBuffer = ArrayPool<int>.Shared.Rent(_capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CheckDivisor(ulong divisor, ReadOnlySpan<ulong> primes, Span<byte> hits)
|
public void CheckDivisor(ulong divisor, ulong divisorCycle, ReadOnlySpan<ulong> primes, Span<byte> hits)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
{
|
{
|
||||||
@@ -413,14 +413,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool useCycles = owner._useDivisorCycles;
|
bool cycleEnabled = owner._useDivisorCycles && divisorCycle != 0UL;
|
||||||
ulong cycle = 0UL;
|
|
||||||
bool cycleEnabled = false;
|
|
||||||
if (useCycles)
|
|
||||||
{
|
|
||||||
cycle = MersenneDivisorCycles.CalculateCycleLengthGpu(modulus);
|
|
||||||
cycleEnabled = cycle != 0UL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Accelerator accelerator = _accelerator;
|
Accelerator accelerator = _accelerator;
|
||||||
Action<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<ulong>> kernel = _kernel;
|
Action<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<ulong>> kernel = _kernel;
|
||||||
@@ -448,7 +441,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
|
|||||||
for (int i = 0; i < batchSize; i++)
|
for (int i = 0; i < batchSize; i++)
|
||||||
{
|
{
|
||||||
ulong prime = primesSlice[i];
|
ulong prime = primesSlice[i];
|
||||||
if (cycleEnabled && prime % cycle != 0UL)
|
if (cycleEnabled && prime % divisorCycle != 0UL)
|
||||||
{
|
{
|
||||||
hitsSlice[i] = 0;
|
hitsSlice[i] = 0;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ public sealed class MersenneNumberDivisorGpuTester
|
|||||||
|
|
||||||
public static void BuildDivisorCandidates()
|
public static void BuildDivisorCandidates()
|
||||||
{
|
{
|
||||||
uint[] snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
ulong[] snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
||||||
(ulong divisor, uint cycle)[] list = new (ulong divisor, uint cycle)[snapshot.Length / 2];
|
(ulong divisor, ulong cycle)[] list = new (ulong divisor, ulong cycle)[snapshot.Length / 2];
|
||||||
uint cycle;
|
ulong cycle;
|
||||||
int count = 0, i, snapshotLength = snapshot.Length;
|
int count = 0, i, snapshotLength = snapshot.Length;
|
||||||
for (i = 3; i < snapshotLength; i += 2)
|
for (i = 3; i < snapshotLength; i += 2)
|
||||||
{
|
{
|
||||||
@@ -52,7 +52,7 @@ public sealed class MersenneNumberDivisorGpuTester
|
|||||||
return divisible;
|
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)
|
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)
|
if (_divisorCandidates is { Length: > 0 } candidates)
|
||||||
{
|
{
|
||||||
int len = candidates.Length;
|
int len = candidates.Length;
|
||||||
uint cycle;
|
ulong cycle;
|
||||||
for (int k = 0; k < len; k++)
|
for (int k = 0; k < len; k++)
|
||||||
{
|
{
|
||||||
(ulong dSmall, cycle) = candidates[k];
|
(ulong dSmall, cycle) = candidates[k];
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public class MersenneDivisorCycles
|
|||||||
{
|
{
|
||||||
private List<(ulong divisor, ulong cycleLength)> _table = [];
|
private List<(ulong divisor, ulong cycleLength)> _table = [];
|
||||||
// Lightweight read-mostly cache for small divisors (<= 4,000,000). 0 => unknown
|
// Lightweight read-mostly cache for small divisors (<= 4,000,000). 0 => unknown
|
||||||
private uint[]? _smallCycles;
|
private ulong[]? _smallCycles;
|
||||||
|
|
||||||
public static MersenneDivisorCycles Shared { get; } = new MersenneDivisorCycles();
|
public static MersenneDivisorCycles Shared { get; } = new MersenneDivisorCycles();
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ public class MersenneDivisorCycles
|
|||||||
cycles.Add((d, c));
|
cycles.Add((d, c));
|
||||||
if (d <= PerfectNumberConstants.MaxQForDivisorCycles)
|
if (d <= PerfectNumberConstants.MaxQForDivisorCycles)
|
||||||
{
|
{
|
||||||
_smallCycles![(int)d] = (uint)Math.Min(uint.MaxValue, c == 0UL ? 1UL : c);
|
_smallCycles![(int)d] = c == 0UL ? 1UL : c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,10 +41,10 @@ public class MersenneDivisorCycles
|
|||||||
|
|
||||||
// Provides a snapshot array of small cycles [0..PerfectNumberConstants.MaxQForDivisorCycles]. Index is the divisor.
|
// 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.
|
// The returned array is a copy safe for use across threads and device uploads.
|
||||||
public uint[] ExportSmallCyclesSnapshot()
|
public ulong[] ExportSmallCyclesSnapshot()
|
||||||
{
|
{
|
||||||
EnsureSmallBuffer();
|
EnsureSmallBuffer();
|
||||||
uint[] snapshot = new uint[PerfectNumberConstants.MaxQForDivisorCycles + 1];
|
ulong[] snapshot = new ulong[PerfectNumberConstants.MaxQForDivisorCycles + 1];
|
||||||
Array.Copy(_smallCycles!, snapshot, snapshot.Length);
|
Array.Copy(_smallCycles!, snapshot, snapshot.Length);
|
||||||
return snapshot;
|
return snapshot;
|
||||||
}
|
}
|
||||||
@@ -78,8 +78,8 @@ public class MersenneDivisorCycles
|
|||||||
var arr = _smallCycles;
|
var arr = _smallCycles;
|
||||||
if (arr is not null)
|
if (arr is not null)
|
||||||
{
|
{
|
||||||
uint cached = arr[(int)divisor];
|
ulong cached = arr[(int)divisor];
|
||||||
if (cached != 0U)
|
if (cached != 0UL)
|
||||||
{
|
{
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
@@ -398,7 +398,7 @@ public class MersenneDivisorCycles
|
|||||||
{
|
{
|
||||||
if (_smallCycles is null)
|
if (_smallCycles is null)
|
||||||
{
|
{
|
||||||
_smallCycles = new uint[PerfectNumberConstants.MaxQForDivisorCycles + 1];
|
_smallCycles = new ulong[PerfectNumberConstants.MaxQForDivisorCycles + 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user