Fix divisor cycle generation range partitioning
This commit is contained in:
1 parent
283913375b
commit
b69a1101c4
2 files changed
+123
-50
No files matched your search
@@ -49,4 +49,57 @@ public class MersenneDivisorCyclesGenerateGpuTests
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Generate_produces_unique_cycles_for_expected_divisors()
|
||||
{
|
||||
string path = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
MersenneDivisorCycles.Generate(path, maxDivisor: 50UL, threads: 4);
|
||||
|
||||
var pairs = new List<(ulong divisor, ulong cycle)>();
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
using var reader = new BinaryReader(stream);
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
ulong divisor = reader.ReadUInt64();
|
||||
ulong cycle = reader.ReadUInt64();
|
||||
pairs.Add((divisor, cycle));
|
||||
}
|
||||
|
||||
var expected = new List<ulong>();
|
||||
for (ulong divisor = 3; divisor <= 50; divisor++)
|
||||
{
|
||||
if ((divisor & 1UL) == 0UL || divisor % 3UL == 0UL || divisor % 5UL == 0UL || divisor % 7UL == 0UL || divisor % 11UL == 0UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
expected.Add(divisor);
|
||||
}
|
||||
|
||||
pairs.Should().HaveCount(expected.Count);
|
||||
|
||||
HashSet<ulong> uniqueDivisors = new();
|
||||
foreach (var pair in pairs)
|
||||
{
|
||||
uniqueDivisors.Add(pair.divisor).Should().BeTrue();
|
||||
pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLength(pair.divisor));
|
||||
}
|
||||
|
||||
uniqueDivisors.Should().BeEquivalentTo(expected);
|
||||
|
||||
var (_, completeCount) = MersenneDivisorCycles.FindLast(path);
|
||||
completeCount.Should().Be(expected.Count);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,65 +150,85 @@ public class MersenneDivisorCycles
|
||||
|
||||
private const int BufferSize10M = 10 * 1024 * 1024;
|
||||
|
||||
public static void Generate(string path, ulong maxDivisor, int threads = 16)
|
||||
{
|
||||
// Binary-only generation of (divisor, cycle) pairs. CSV scaffolding removed.
|
||||
ulong start, d = start = 2;
|
||||
ulong blockSize = maxDivisor / (ulong)threads + 1;
|
||||
Task[] tasks = new Task[threads];
|
||||
using Stream outputStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, BufferSize10M, useAsync: false);
|
||||
// Start fresh to avoid mixing formats
|
||||
outputStream.SetLength(0L);
|
||||
outputStream.Position = 0L;
|
||||
using var writer = new BinaryWriter(outputStream, Encoding.UTF8, leaveOpen: false);
|
||||
for (var taskIndex = 0; taskIndex < threads; taskIndex++)
|
||||
{
|
||||
Task t = Task.Factory.StartNew((taskIndex) =>
|
||||
{
|
||||
ulong cycle, d = start + (ulong)(int)taskIndex! * blockSize;
|
||||
(ulong d, ulong cycle)[] localCycles = ArrayPool<(ulong, ulong)>.Shared.Rent((int)(maxDivisor - d + 1UL));
|
||||
int localCycleIndex = 0;
|
||||
for (; d <= maxDivisor; d++)
|
||||
{
|
||||
if ((d & 1UL) == 0UL)
|
||||
{
|
||||
// skip even divisors
|
||||
continue;
|
||||
}
|
||||
public static void Generate(string path, ulong maxDivisor, int threads = 16)
|
||||
{
|
||||
// Binary-only generation of (divisor, cycle) pairs. CSV scaffolding removed.
|
||||
ulong start = 2;
|
||||
ulong blockSize = maxDivisor / (ulong)threads + 1UL;
|
||||
Task[] tasks = new Task[threads];
|
||||
using Stream outputStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, BufferSize10M, useAsync: false);
|
||||
// Start fresh to avoid mixing formats
|
||||
outputStream.SetLength(0L);
|
||||
outputStream.Position = 0L;
|
||||
using var writer = new BinaryWriter(outputStream, Encoding.UTF8, leaveOpen: false);
|
||||
for (var taskIndex = 0; taskIndex < threads; taskIndex++)
|
||||
{
|
||||
ulong threadStart = start + (ulong)taskIndex * blockSize;
|
||||
if (threadStart > maxDivisor)
|
||||
{
|
||||
tasks[taskIndex] = Task.CompletedTask;
|
||||
continue;
|
||||
}
|
||||
|
||||
cycle = CalculateCycleLength(d);
|
||||
ulong threadEnd = threadStart + blockSize - 1UL;
|
||||
if (threadEnd > maxDivisor)
|
||||
{
|
||||
threadEnd = maxDivisor;
|
||||
}
|
||||
|
||||
localCycles[localCycleIndex++] = (d, cycle);
|
||||
}
|
||||
ulong localStart = threadStart;
|
||||
ulong localEnd = threadEnd;
|
||||
tasks[taskIndex] = Task.Run(() =>
|
||||
{
|
||||
ulong rangeLength = localEnd - localStart + 1UL;
|
||||
(ulong divisor, ulong cycle)[] localCycles = ArrayPool<(ulong, ulong)>.Shared.Rent(checked((int)rangeLength));
|
||||
int localCycleIndex = 0;
|
||||
|
||||
if (localCycleIndex > 0)
|
||||
{
|
||||
lock (writer)
|
||||
{
|
||||
writer.BaseStream.Position = writer.BaseStream.Length;
|
||||
for (int i = 0; i < localCycleIndex; i++)
|
||||
{
|
||||
(d, cycle) = localCycles[i];
|
||||
if ((d % 3UL) == 0UL || (d % 5UL) == 0UL || (d % 7UL) == 0UL || (d % 11UL) == 0UL)
|
||||
continue;
|
||||
try
|
||||
{
|
||||
for (ulong divisor = localStart; divisor <= localEnd; divisor++)
|
||||
{
|
||||
if ((divisor & 1UL) == 0UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
writer.Write(d);
|
||||
writer.Write(cycle);
|
||||
}
|
||||
if ((divisor % 3UL) == 0UL || (divisor % 5UL) == 0UL || (divisor % 7UL) == 0UL || (divisor % 11UL) == 0UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
writer.Flush();
|
||||
}
|
||||
}
|
||||
ulong cycle = CalculateCycleLength(divisor);
|
||||
localCycles[localCycleIndex++] = (divisor, cycle);
|
||||
}
|
||||
|
||||
ArrayPool<(ulong, ulong)>.Shared.Return(localCycles, clearArray: false);
|
||||
if (localCycleIndex == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
}, taskIndex);
|
||||
lock (writer)
|
||||
{
|
||||
writer.BaseStream.Position = writer.BaseStream.Length;
|
||||
for (int i = 0; i < localCycleIndex; i++)
|
||||
{
|
||||
(ulong divisor, ulong cycle) = localCycles[i];
|
||||
writer.Write(divisor);
|
||||
writer.Write(cycle);
|
||||
}
|
||||
|
||||
tasks[taskIndex] = t;
|
||||
}
|
||||
writer.Flush();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<(ulong, ulong)>.Shared.Return(localCycles, clearArray: false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Task.WaitAll(tasks);
|
||||
}
|
||||
Task.WaitAll(tasks);
|
||||
}
|
||||
|
||||
public static void GenerateGpu(string path, ulong maxDivisor, int batchSize = 1_000_000, long skipCount = 0L, long nextPosition = 0L)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user