Initial BitTree implementation added.

This commit is contained in:
mlemanczyk committed 2026-01-09 16:13:16 +01:00
1 parent 45e3455ff3
commit d88ad25476
11 files changed
+3817 -367

No files matched your search

File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -27,6 +27,10 @@ using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTeste
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForCpuOrder;
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForHybridOrder;
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForGpuOrder;
#elif DivisorSet_BitTree
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForCpuOrder;
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForHybridOrder;
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForGpuOrder;
#else
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForCpuOrder;
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForHybridOrder;
@@ -91,6 +95,8 @@ internal struct MersenneNumberDivisorByDivisorPrimeScanSessionWithTemplate
extension = ".topdown";
#elif DivisorSet_BitContradiction
extension = ".bitcontradiction";
#elif DivisorSet_BitTree
extension = ".bittree";
#endif
string stateFile = prime.ToString(CultureInfo.InvariantCulture) + extension;
@@ -149,7 +149,7 @@ public sealed class PrimeOrderCalculatorAccelerator
private sealed class CalculatorKernels(Accelerator accelerator)
{
public readonly Action<AcceleratorStream, Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers> CalculateOrderKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers>(PrimeOrderKernels.CalculateOrderKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers>>();
// public readonly Action<AcceleratorStream, Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers> CalculateOrderKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers>(PrimeOrderKernels.CalculateOrderKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers>>();
public readonly Kernel CheckFactorsKernel = KernelUtil.GetKernel(accelerator.LoadStreamKernel<int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderKernels.CheckFactorsKernel));
@@ -177,7 +177,7 @@ public sealed class PrimeOrderCalculatorAccelerator
public readonly Action<AcceleratorStream, Index1D, ulong, ArrayView<ulong>, int, ulong, ArrayView<ulong>> SpecialMaxKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<ulong>, int, ulong, ArrayView<ulong>>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView<ulong>, int, ulong, ArrayView<ulong>>>();
}
public readonly Action<AcceleratorStream, Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers> CalculateOrderKernelLauncher;
// public readonly Action<AcceleratorStream, Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers> CalculateOrderKernelLauncher;
public readonly Kernel CheckFactorsKernel;
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>> ConvertToStandardKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ArrayView<int>, ulong, ulong, ArrayView<ulong>, ArrayView<ulong>> HeuristicCombinedTrialDivisionKernelLauncher;
@@ -421,9 +421,9 @@ public sealed class PrimeOrderCalculatorAccelerator
DevicePrimesPow2LastThree = lastDigitSharedTables.DevicePrimesPow2LastThree.View;
DevicePrimesPow2LastNine = lastDigitSharedTables.DevicePrimesPow2LastNine.View;
DivisorTables = HeuristicCombinedGpuTables
.GetStaticTables(acceleratorIndex)
.CreateViews();
// DivisorTables = HeuristicCombinedGpuTables
// .GetStaticTables(acceleratorIndex)
// .CreateViews();
var smallPrimeFactorTables = SmallPrimeFactorGpuTables.GetStaticTables(acceleratorIndex);
@@ -436,7 +436,7 @@ public sealed class PrimeOrderCalculatorAccelerator
var kernels = _kernels[acceleratorIndex];
CalculateOrderKernelLauncher = kernels.CalculateOrderKernelLauncher;
// CalculateOrderKernelLauncher = kernels.CalculateOrderKernelLauncher;
CheckFactorsKernel = kernels.CheckFactorsKernel;
ConvertToStandardKernelLauncher = kernels.ConvertToStandardKernelLauncher;
HeuristicCombinedTrialDivisionKernelLauncher = kernels.HeuristicCombinedTrialDivisionKernelLauncher;
@@ -272,27 +272,27 @@ internal static partial class PrimeOrderGpuHeuristics
var kernelConfig = new CalculateOrderKernelConfig(previousValue, hasPrevious, limit, config.MaxPowChecks, config.StrictMode);
ref var buffers = ref gpu.CalculateOrderKernelBuffers;
var kernelLauncher = gpu.CalculateOrderKernelLauncher;
// var kernelLauncher = gpu.CalculateOrderKernelLauncher;
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
// TODO: Remove the cleaning after the order kernel is modified to always set the result.
resultBufferView.MemSetToZero(stream);
statusBufferView.MemSetToZero(stream);
kernelLauncher(
stream,
1,
prime,
kernelConfig,
divisorData.Modulus,
divisorData.NPrime,
divisorData.MontgomeryOne,
divisorData.MontgomeryTwo,
divisorData.MontgomeryTwoSquared,
smallPrimesView,
smallSquaresView,
(int)smallPrimesView.Length,
buffers);
// kernelLauncher(
// stream,
// 1,
// prime,
// kernelConfig,
// divisorData.Modulus,
// divisorData.NPrime,
// divisorData.MontgomeryOne,
// divisorData.MontgomeryTwo,
// divisorData.MontgomeryTwoSquared,
// smallPrimesView,
// smallSquaresView,
// (int)smallPrimesView.Length,
// buffers);
byte status = 0;
@@ -21,6 +21,9 @@ using MersenneNumberDivisorByDivisorCpuTesterWithAdditiveGpuOrder = PerfectNumbe
using MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForCpuOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForHybridOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForGpuOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithBitTreeCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForCpuOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithBitTreeHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForHybridOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithBitTreeGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForGpuOrder;
namespace PerfectNumbers.Core;
@@ -88,6 +91,37 @@ public static class MersenneNumberDivisorByDivisorTester
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunBitContradictionCpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
ref MersenneNumberDivisorByDivisorCpuTesterWithBitTreeCpuOrder tester,
Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? previousResults,
ulong startPrime,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult,
int threadCount,
int primesPerTask)
=> RunStructTester(
candidates,
ref tester,
previousResults,
startPrime,
markComposite,
clearComposite,
printResult,
threadCount,
primesPerTask,
static (
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitTreeCpuOrder prototype,
int workerCount,
int partitionSize,
Action mc,
Action cc,
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunBitTreeCpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
@@ -243,6 +277,37 @@ public static class MersenneNumberDivisorByDivisorTester
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunBitContradictionHybridOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
ref MersenneNumberDivisorByDivisorCpuTesterWithBitTreeHybridOrder tester,
Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? previousResults,
ulong startPrime,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult,
int threadCount,
int primesPerTask)
=> RunStructTester(
candidates,
ref tester,
previousResults,
startPrime,
markComposite,
clearComposite,
printResult,
threadCount,
primesPerTask,
static (
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitTreeHybridOrder prototype,
int workerCount,
int partitionSize,
Action mc,
Action cc,
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunBitTreeHybridOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
@@ -398,6 +463,37 @@ public static class MersenneNumberDivisorByDivisorTester
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunBitContradictionGpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
ref MersenneNumberDivisorByDivisorCpuTesterWithBitTreeGpuOrder tester,
Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? previousResults,
ulong startPrime,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult,
int threadCount,
int primesPerTask)
=> RunStructTester(
candidates,
ref tester,
previousResults,
startPrime,
markComposite,
clearComposite,
printResult,
threadCount,
primesPerTask,
static (
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitTreeGpuOrder prototype,
int workerCount,
int partitionSize,
Action mc,
Action cc,
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunBitTreeGpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
@@ -2575,6 +2671,150 @@ public static class MersenneNumberDivisorByDivisorTester
}));
}
private static void RunBitTreeCpuOrderFiltered(
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitTreeCpuOrder prototype,
int workerCount,
int partitionSize,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult)
{
MersenneNumberDivisorByDivisorCpuTesterWithBitTreeCpuOrder prototypeCopy = prototype;
int totalCount = filteredPrimes.Count;
if (partitionSize > totalCount)
{
partitionSize = totalCount;
}
if (workerCount <= 1 || totalCount <= partitionSize)
{
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitTreeDivisorSetForCpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < totalCount; i++)
{
session.ProcessPrime(filteredPrimes[i]);
}
return;
}
var options = new ParallelOptions
{
MaxDegreeOfParallelism = workerCount,
};
Parallel.For(
0,
(int)Math.Ceiling((double)totalCount / partitionSize),
options,
new Action<int>(partitionIndex =>
{
int startIndex = partitionIndex * partitionSize;
int count = Math.Min(partitionSize, totalCount - startIndex);
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitTreeDivisorSetForCpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < count; i++)
{
session.ProcessPrime(filteredPrimes[startIndex + i]);
}
}));
}
private static void RunBitTreeHybridOrderFiltered(
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitTreeHybridOrder prototype,
int workerCount,
int partitionSize,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult)
{
MersenneNumberDivisorByDivisorCpuTesterWithBitTreeHybridOrder prototypeCopy = prototype;
int totalCount = filteredPrimes.Count;
if (partitionSize > totalCount)
{
partitionSize = totalCount;
}
if (workerCount <= 1 || totalCount <= partitionSize)
{
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitTreeDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < totalCount; i++)
{
session.ProcessPrime(filteredPrimes[i]);
}
return;
}
var options = new ParallelOptions
{
MaxDegreeOfParallelism = workerCount,
};
Parallel.For(
0,
(int)Math.Ceiling((double)totalCount / partitionSize),
options,
new Action<int>(partitionIndex =>
{
int startIndex = partitionIndex * partitionSize;
int count = Math.Min(partitionSize, totalCount - startIndex);
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitTreeDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < count; i++)
{
session.ProcessPrime(filteredPrimes[startIndex + i]);
}
}));
}
private static void RunBitTreeGpuOrderFiltered(
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitTreeGpuOrder prototype,
int workerCount,
int partitionSize,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult)
{
MersenneNumberDivisorByDivisorCpuTesterWithBitTreeGpuOrder prototypeCopy = prototype;
int totalCount = filteredPrimes.Count;
if (partitionSize > totalCount)
{
partitionSize = totalCount;
}
if (workerCount <= 1 || totalCount <= partitionSize)
{
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitTreeDivisorSetForGpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < totalCount; i++)
{
session.ProcessPrime(filteredPrimes[i]);
}
return;
}
var options = new ParallelOptions
{
MaxDegreeOfParallelism = workerCount,
};
Parallel.For(
0,
(int)Math.Ceiling((double)totalCount / partitionSize),
options,
new Action<int>(partitionIndex =>
{
int startIndex = partitionIndex * partitionSize;
int count = Math.Min(partitionSize, totalCount - startIndex);
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitTreeDivisorSetForGpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < count; i++)
{
session.ProcessPrime(filteredPrimes[startIndex + i]);
}
}));
}
private static void RunBitContradictionHybridOrderFiltered(
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionHybridOrder prototype,
@@ -0,0 +1,129 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Numerics;
using System.Text;
using FASTER.core;
namespace PerfectNumbers.Core.Persistence;
/// <summary>
/// Stores BitTree frontier batches using Microsoft.FASTER.Core.
/// Each batch is a byte[] payload containing a sequence of serialized states.
/// Maintains a simple head/tail cursor stored inside the KV store.
/// </summary>
public sealed class BitTreeFrontierRepository : IDisposable
{
private readonly FasterKV<string, byte[]> _store;
private readonly IDevice _logDevice;
private readonly IDevice _objectLogDevice;
private long _headIndex;
private long _tailIndex;
private const string HeadKey = "__head__";
private const string TailKey = "__tail__";
public string DatabasePath { get; }
public static BitTreeFrontierRepository Open(string directory, string repositoryFileName = "bittree.frontier.faster")
{
Directory.CreateDirectory(directory);
string dbPath = Path.Combine(directory, repositoryFileName);
return new BitTreeFrontierRepository(dbPath);
}
private BitTreeFrontierRepository(string path)
{
DatabasePath = path;
_logDevice = Devices.CreateLogDevice(path + ".log");
_objectLogDevice = Devices.CreateLogDevice(path + ".obj.log");
_store = new FasterKV<string, byte[]>(
1L << 20,
new LogSettings
{
LogDevice = _logDevice,
ObjectLogDevice = _objectLogDevice,
});
LoadCursors();
}
public void AppendBatch(ReadOnlySpan<byte> batch)
{
using var session = _store.For(new SimpleFunctions<string, byte[]>()).NewSession<SimpleFunctions<string, byte[]>>();
string key = BuildChunkKey(_tailIndex);
session.Upsert(key, batch.ToArray());
_tailIndex++;
PersistCursors(session);
session.CompletePending(true);
_store.Log.FlushAndEvict(true);
}
public bool TryDequeueBatch(int maxCount, out List<byte[]> batches)
{
batches = new List<byte[]>();
using var session = _store.For(new SimpleFunctions<string, byte[]>()).NewSession<SimpleFunctions<string, byte[]>>();
while (_headIndex < _tailIndex && batches.Count < maxCount)
{
string key = BuildChunkKey(_headIndex);
if (session.Read(key, out byte[]? stored).Found && stored is not null)
{
batches.Add(stored);
session.Delete(key);
}
_headIndex++;
}
if (batches.Count > 0)
{
PersistCursors(session);
session.CompletePending(true);
_store.Log.FlushAndEvict(true);
return true;
}
session.CompletePending(true);
_store.Log.FlushAndEvict(true);
return false;
}
public void Dispose()
{
_store.Dispose();
_logDevice.Dispose();
_objectLogDevice.Dispose();
}
private void LoadCursors()
{
using var session = _store.For(new SimpleFunctions<string, byte[]>()).NewSession<SimpleFunctions<string, byte[]>>();
_headIndex = ReadCursor(session, HeadKey);
_tailIndex = ReadCursor(session, TailKey);
session.CompletePending(true);
}
private static long ReadCursor(ClientSession<string, byte[], byte[], byte[], Empty, SimpleFunctions<string, byte[]>> session, string key)
{
if (session.Read(key, out byte[]? stored).Found && stored is not null)
{
string text = Encoding.UTF8.GetString(stored);
if (long.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out long value))
{
return value;
}
}
return 0;
}
private void PersistCursors(ClientSession<string, byte[], byte[], byte[], Empty, SimpleFunctions<string, byte[]>> session)
{
session.Upsert(HeadKey, Encoding.UTF8.GetBytes(_headIndex.ToString(CultureInfo.InvariantCulture)));
session.Upsert(TailKey, Encoding.UTF8.GetBytes(_tailIndex.ToString(CultureInfo.InvariantCulture)));
}
private static string BuildChunkKey(long index) => $"chunk-{index}";
}