Predictive scanning implemented for bydivisor method.
This commit is contained in:
1 parent
ef8034b258
commit
a6d408f2aa
10 files changed
+1562
-40
No files matched your search
@@ -0,0 +1,344 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace PerfectNumbers.Core.ByDivisor;
|
||||
|
||||
public sealed class ByDivisorClassEntry
|
||||
{
|
||||
public double K50 { get; set; }
|
||||
public double K75 { get; set; }
|
||||
public double GapProbability { get; set; }
|
||||
public int SampleCount { get; set; }
|
||||
public int GapCount { get; set; }
|
||||
public string Stability { get; set; } = "unknown";
|
||||
}
|
||||
|
||||
public sealed class ByDivisorScanPlan
|
||||
{
|
||||
public double CheapLimit { get; init; }
|
||||
public double Start { get; init; }
|
||||
public double Target { get; init; }
|
||||
public double Rho1 { get; init; }
|
||||
public double Rho2 { get; init; }
|
||||
}
|
||||
|
||||
public sealed class ByDivisorScanTuning
|
||||
{
|
||||
public double CheapLimit { get; init; } = 10_000d;
|
||||
public double Gamma50 { get; init; } = 0.8d;
|
||||
public double Gamma75 { get; init; } = 1.2d;
|
||||
public double AggressiveGamma50 { get; init; } = 1.0d;
|
||||
public double AggressiveGamma75 { get; init; } = 1.4d;
|
||||
public double Rho1 { get; init; } = 1.35d;
|
||||
public double Rho2 { get; init; } = 1.6d;
|
||||
public double AggressiveRho2 { get; init; } = 1.8d;
|
||||
public double HardGapThreshold { get; init; } = 0.75d;
|
||||
|
||||
[JsonIgnore]
|
||||
public static ByDivisorScanTuning Default => new();
|
||||
}
|
||||
|
||||
public sealed class ByDivisorClassModel
|
||||
{
|
||||
private const int ClassCount = 1024;
|
||||
public int Version { get; set; } = 1;
|
||||
public double CheapLimit { get; set; } = 10_000d;
|
||||
public ByDivisorClassEntry[] Entries { get; set; } = Enumerable.Range(0, ClassCount)
|
||||
.Select(_ => new ByDivisorClassEntry { K50 = 1d, K75 = 1d })
|
||||
.ToArray();
|
||||
|
||||
public ByDivisorScanPlan BuildPlan(ulong prime, BigInteger allowedMax, in ByDivisorScanTuning tuning)
|
||||
{
|
||||
int suffix = (int)(prime & 1023UL);
|
||||
ByDivisorClassEntry entry = Entries[suffix];
|
||||
|
||||
double k50 = entry.K50 > 1d ? entry.K50 : 1d;
|
||||
double k75 = entry.K75 > 1d ? entry.K75 : Math.Max(k50, 1d);
|
||||
bool isHard = entry.GapProbability >= tuning.HardGapThreshold || string.Equals(entry.Stability, "escaping", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
double gamma50 = isHard ? tuning.AggressiveGamma50 : tuning.Gamma50;
|
||||
double gamma75 = isHard ? tuning.AggressiveGamma75 : tuning.Gamma75;
|
||||
double start = Math.Ceiling(Math.Max(1d, gamma50 * k50));
|
||||
double target = Math.Ceiling(Math.Max(start + 1d, gamma75 * k75));
|
||||
|
||||
double cheap = tuning.CheapLimit > 1d ? tuning.CheapLimit : CheapLimit;
|
||||
double rho2 = isHard ? tuning.AggressiveRho2 : tuning.Rho2;
|
||||
|
||||
double maxKAllowed = ComputeMaxKAllowed(prime, allowedMax);
|
||||
if (maxKAllowed > 0d)
|
||||
{
|
||||
cheap = Math.Min(cheap, maxKAllowed);
|
||||
start = Math.Min(start, maxKAllowed);
|
||||
target = Math.Min(target, maxKAllowed);
|
||||
}
|
||||
|
||||
return new ByDivisorScanPlan
|
||||
{
|
||||
CheapLimit = cheap,
|
||||
Start = start,
|
||||
Target = target,
|
||||
Rho1 = tuning.Rho1,
|
||||
Rho2 = rho2,
|
||||
};
|
||||
}
|
||||
|
||||
private static double ComputeMaxKAllowed(ulong prime, in BigInteger allowedMax)
|
||||
{
|
||||
if (prime == 0UL || allowedMax.IsZero)
|
||||
{
|
||||
return 0d;
|
||||
}
|
||||
|
||||
BigInteger step = ((BigInteger)prime) << 1;
|
||||
if (step.IsZero)
|
||||
{
|
||||
return 0d;
|
||||
}
|
||||
|
||||
BigInteger maxK = (allowedMax - BigInteger.One) / step;
|
||||
if (maxK <= BigInteger.Zero)
|
||||
{
|
||||
return 0d;
|
||||
}
|
||||
|
||||
double value = (double)maxK;
|
||||
return double.IsInfinity(value) || value <= 0d ? 0d : value;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ByDivisorClassModelLoader
|
||||
{
|
||||
public static ByDivisorClassModel LoadOrBuild(string modelPath, string resultsPath, in ByDivisorScanTuning tuning, double cheapLimit, bool rebuildWhenResultsNewer = true)
|
||||
{
|
||||
DateTime modelTimestamp = File.Exists(modelPath) ? File.GetLastWriteTimeUtc(modelPath) : DateTime.MinValue;
|
||||
DateTime resultsTimestamp = File.Exists(resultsPath) ? File.GetLastWriteTimeUtc(resultsPath) : DateTime.MinValue;
|
||||
|
||||
if (File.Exists(modelPath) && (!rebuildWhenResultsNewer || resultsTimestamp <= modelTimestamp))
|
||||
{
|
||||
try
|
||||
{
|
||||
ByDivisorClassModel? loaded = JsonSerializer.Deserialize<ByDivisorClassModel>(File.ReadAllText(modelPath));
|
||||
if (loaded is not null)
|
||||
{
|
||||
loaded.CheapLimit = cheapLimit;
|
||||
return loaded;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// fall back to rebuild
|
||||
}
|
||||
}
|
||||
|
||||
ByDivisorClassModel model = BuildFromResults(resultsPath, cheapLimit);
|
||||
Save(modelPath, model);
|
||||
return model;
|
||||
}
|
||||
|
||||
public static void RebuildAndSave(string modelPath, string resultsPath, double cheapLimit)
|
||||
{
|
||||
ByDivisorClassModel model = BuildFromResults(resultsPath, cheapLimit);
|
||||
Save(modelPath, model);
|
||||
}
|
||||
|
||||
private static void Save(string modelPath, ByDivisorClassModel model)
|
||||
{
|
||||
string? directory = Path.GetDirectoryName(modelPath);
|
||||
if (!string.IsNullOrEmpty(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
};
|
||||
string json = JsonSerializer.Serialize(model, options);
|
||||
string tempPath = modelPath + ".tmp";
|
||||
File.WriteAllText(tempPath, json);
|
||||
File.Copy(tempPath, modelPath, overwrite: true);
|
||||
File.Delete(tempPath);
|
||||
}
|
||||
|
||||
private static ByDivisorClassModel BuildFromResults(string resultsPath, double cheapLimit)
|
||||
{
|
||||
var model = new ByDivisorClassModel
|
||||
{
|
||||
CheapLimit = cheapLimit,
|
||||
};
|
||||
|
||||
if (!File.Exists(resultsPath))
|
||||
{
|
||||
return model;
|
||||
}
|
||||
|
||||
var samples = new List<double>[1024];
|
||||
Span<int> sampleCounts = stackalloc int[1024];
|
||||
Span<int> gapCounts = stackalloc int[1024];
|
||||
|
||||
using FileStream stream = new(resultsPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
using StreamReader reader = new(stream);
|
||||
string? line;
|
||||
bool headerSkipped = false;
|
||||
|
||||
while ((line = reader.ReadLine()) is not null)
|
||||
{
|
||||
if (!headerSkipped)
|
||||
{
|
||||
headerSkipped = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] parts = line.Split(',');
|
||||
if (parts.Length < 5)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ulong.TryParse(parts[0], NumberStyles.None, CultureInfo.InvariantCulture, out ulong p))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!BigInteger.TryParse(parts[1], NumberStyles.None, CultureInfo.InvariantCulture, out BigInteger divisor))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!bool.TryParse(parts[3], out bool detailedCheck))
|
||||
{
|
||||
detailedCheck = false;
|
||||
}
|
||||
|
||||
if (!bool.TryParse(parts[4], out bool passedAllTests))
|
||||
{
|
||||
passedAllTests = false;
|
||||
}
|
||||
|
||||
int suffix = (int)(p & 1023UL);
|
||||
bool validDivisor = divisor > BigInteger.One && IsValidDivisor(p, divisor);
|
||||
if (validDivisor)
|
||||
{
|
||||
double kValue = ComputeK(divisor, p);
|
||||
if (!double.IsNaN(kValue) && !double.IsInfinity(kValue))
|
||||
{
|
||||
samples[suffix] ??= new List<double>();
|
||||
samples[suffix]!.Add(kValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (detailedCheck)
|
||||
{
|
||||
sampleCounts[suffix]++;
|
||||
if (!validDivisor || passedAllTests)
|
||||
{
|
||||
gapCounts[suffix]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 1024; i++)
|
||||
{
|
||||
List<double>? classSamples = samples[i];
|
||||
double k50 = 1d;
|
||||
double k75 = 1d;
|
||||
if (classSamples is not null && classSamples.Count > 0)
|
||||
{
|
||||
classSamples.Sort();
|
||||
k50 = ComputePercentile(classSamples, 0.5d);
|
||||
k75 = ComputePercentile(classSamples, 0.75d);
|
||||
}
|
||||
|
||||
int total = sampleCounts[i];
|
||||
int gaps = gapCounts[i];
|
||||
double gapProb = total > 0 ? (double)gaps / total : 0d;
|
||||
|
||||
model.Entries[i] = new ByDivisorClassEntry
|
||||
{
|
||||
K50 = k50,
|
||||
K75 = k75,
|
||||
GapProbability = gapProb,
|
||||
SampleCount = total,
|
||||
GapCount = gaps,
|
||||
Stability = model.Entries[i].Stability,
|
||||
};
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
private static bool IsValidDivisor(ulong prime, in BigInteger divisor)
|
||||
{
|
||||
if (divisor <= BigInteger.One)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BigInteger modulus = ((BigInteger)prime) << 1;
|
||||
if (modulus.IsZero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((divisor - BigInteger.One) % modulus != BigInteger.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return BigInteger.ModPow(2, prime, divisor) == BigInteger.One;
|
||||
}
|
||||
|
||||
private static double ComputeK(in BigInteger divisor, ulong prime)
|
||||
{
|
||||
BigInteger numerator = divisor - BigInteger.One;
|
||||
BigInteger denominator = ((BigInteger)prime) << 1;
|
||||
if (denominator.IsZero)
|
||||
{
|
||||
return 0d;
|
||||
}
|
||||
|
||||
BigInteger k = numerator / denominator;
|
||||
if (k <= BigInteger.Zero)
|
||||
{
|
||||
return 0d;
|
||||
}
|
||||
|
||||
double value = (double)k;
|
||||
if (double.IsInfinity(value) || double.IsNaN(value) || value <= 0d)
|
||||
{
|
||||
return double.MaxValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static double ComputePercentile(List<double> values, double percentile)
|
||||
{
|
||||
if (values.Count == 0)
|
||||
{
|
||||
return 1d;
|
||||
}
|
||||
|
||||
double index = (values.Count - 1) * percentile;
|
||||
int lower = (int)Math.Floor(index);
|
||||
int upper = (int)Math.Ceiling(index);
|
||||
if (lower == upper)
|
||||
{
|
||||
return values[lower];
|
||||
}
|
||||
|
||||
double fraction = index - lower;
|
||||
return values[lower] + ((values[upper] - values[lower]) * fraction);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using PerfectNumbers.Core.ByDivisor;
|
||||
|
||||
namespace PerfectNumbers.Core.Cpu;
|
||||
|
||||
@@ -13,6 +14,7 @@ public enum DivisorSet
|
||||
{
|
||||
OneByOne,
|
||||
Pow2Groups,
|
||||
Predictive,
|
||||
}
|
||||
|
||||
[EnumDependentTemplate(typeof(DivisorSet))]
|
||||
@@ -71,19 +73,24 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsMersenneValue(ulong prime, in BigInteger divisor)
|
||||
{
|
||||
if (divisor <= BigInteger.One || prime == 0UL || prime > int.MaxValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int divisorBitLength = GetBitLengthPortable(divisor);
|
||||
if (divisorBitLength != (int)prime)
|
||||
if (divisor <= BigInteger.One || prime == 0UL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BigInteger plusOne = divisor + BigInteger.One;
|
||||
return plusOne > BigInteger.One && IsPowerOfTwoBig(plusOne);
|
||||
if (plusOne <= BigInteger.One || !IsPowerOfTwoBig(plusOne))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int exponentBits = GetBitLengthPortable(plusOne) - 1;
|
||||
if (exponentBits <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return prime == (ulong)exponentBits;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -331,8 +338,36 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
step,
|
||||
out processedAll,
|
||||
out foundDivisor);
|
||||
// Console.WriteLine($"[by-divisor-plan] p={prime} suffix={(prime & 1023UL)} using pow2-groups plan; model skipped.");
|
||||
return compositePow2;
|
||||
#else
|
||||
#if DivisorSet_Predictive
|
||||
ByDivisorClassModel? classifier = EnvironmentConfiguration.ByDivisorClassModel;
|
||||
if (classifier is not null)
|
||||
{
|
||||
bool compositePlanned = TryExecutePlannedScan(
|
||||
classifier,
|
||||
prime,
|
||||
allowedMax,
|
||||
normalizedMinK,
|
||||
out processedAll,
|
||||
out foundDivisor);
|
||||
if (compositePlanned)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (processedAll)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"[by-divisor-plan] p={prime} suffix={(prime & 1023UL)} model unavailable, falling back to sequential scan.");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fits64 && composite64)
|
||||
{
|
||||
allowedMax64 = unlimited ? ulong.MaxValue : (ulong)allowedMax;
|
||||
@@ -556,6 +591,400 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !DivisorSet_Pow2Groups && DivisorSet_Predictive
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
private bool TryExecutePlannedScan(
|
||||
ByDivisorClassModel model,
|
||||
ulong prime,
|
||||
BigInteger allowedMax,
|
||||
BigInteger minK,
|
||||
out bool processedAll,
|
||||
out BigInteger foundDivisor)
|
||||
{
|
||||
processedAll = false;
|
||||
foundDivisor = BigInteger.Zero;
|
||||
|
||||
if (minK > ulong.MaxValue)
|
||||
{
|
||||
// Console.WriteLine($"[by-divisor-plan] p={prime} using BigInteger plan (minK={minK}).");
|
||||
return TryExecutePlannedScanBig(model, prime, allowedMax, minK, out processedAll, out foundDivisor);
|
||||
}
|
||||
|
||||
ulong allowedMax64;
|
||||
if (allowedMax.IsZero)
|
||||
{
|
||||
allowedMax64 = ulong.MaxValue;
|
||||
}
|
||||
else if (allowedMax > ulong.MaxValue)
|
||||
{
|
||||
// Console.WriteLine($"[by-divisor-plan] p={prime} using BigInteger plan (allowedMax={allowedMax}).");
|
||||
return TryExecutePlannedScanBig(model, prime, allowedMax, minK, out processedAll, out foundDivisor);
|
||||
}
|
||||
else
|
||||
{
|
||||
allowedMax64 = (ulong)allowedMax;
|
||||
}
|
||||
bool composite = TryExecutePlannedScan64(
|
||||
model,
|
||||
prime,
|
||||
allowedMax64,
|
||||
(ulong)minK,
|
||||
out processedAll,
|
||||
out ulong foundDivisor64);
|
||||
if (composite)
|
||||
{
|
||||
foundDivisor = foundDivisor64;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (processedAll)
|
||||
{
|
||||
foundDivisor = BigInteger.Zero;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
private bool TryExecutePlannedScanBig(
|
||||
ByDivisorClassModel model,
|
||||
ulong prime,
|
||||
in BigInteger allowedMax,
|
||||
in BigInteger minK,
|
||||
out bool processedAll,
|
||||
out BigInteger foundDivisor)
|
||||
{
|
||||
processedAll = true;
|
||||
foundDivisor = BigInteger.Zero;
|
||||
|
||||
if (allowedMax.IsZero)
|
||||
{
|
||||
processedAll = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
BigInteger step = ((BigInteger)prime) << 1;
|
||||
BigInteger maxKAllowed = (allowedMax - BigInteger.One) / step;
|
||||
TryClampMaxKForMersenneTail(prime, step, ref maxKAllowed);
|
||||
if (maxKAllowed <= BigInteger.Zero || minK > maxKAllowed)
|
||||
{
|
||||
processedAll = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
ByDivisorScanTuning tuning = EnvironmentConfiguration.ByDivisorScanTuning;
|
||||
ByDivisorScanPlan plan = model.BuildPlan(prime, allowedMax, tuning);
|
||||
int suffix = (int)(prime & 1023UL);
|
||||
// ByDivisorClassEntry entry = model.Entries[suffix];
|
||||
// Console.WriteLine($"[by-divisor-plan] p={prime} suffix={suffix} k50={entry.K50:F2} k75={entry.K75:F2} gap={entry.GapProbability:F3} cheap<= {plan.CheapLimit:F0} start={plan.Start:F0} target={plan.Target:F0} rho1={plan.Rho1:F2} rho2={plan.Rho2:F2} maxK={maxKAllowed}");
|
||||
|
||||
BigInteger cheapLimit = BigInteger.Min((BigInteger)plan.CheapLimit, maxKAllowed);
|
||||
if (cheapLimit >= minK)
|
||||
{
|
||||
BigInteger currentK = minK;
|
||||
bool compositeCheap = CheckDivisorsBigPlanRange(
|
||||
prime,
|
||||
step,
|
||||
allowedMax,
|
||||
minK,
|
||||
cheapLimit,
|
||||
out bool processedRange,
|
||||
out BigInteger foundRange,
|
||||
ref currentK);
|
||||
if (compositeCheap)
|
||||
{
|
||||
foundDivisor = foundRange;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!processedRange)
|
||||
{
|
||||
processedAll = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
BigInteger startPlan = plan.Start <= 0d ? minK : new BigInteger(Math.Ceiling(plan.Start));
|
||||
if (startPlan < minK)
|
||||
{
|
||||
startPlan = minK;
|
||||
}
|
||||
|
||||
BigInteger target = plan.Target <= 0d ? startPlan + BigInteger.One : new BigInteger(Math.Ceiling(plan.Target));
|
||||
if (target < startPlan)
|
||||
{
|
||||
target = startPlan + BigInteger.One;
|
||||
}
|
||||
|
||||
BigInteger current = cheapLimit + BigInteger.One;
|
||||
if (current < startPlan)
|
||||
{
|
||||
current = startPlan;
|
||||
}
|
||||
|
||||
bool loggedProgressive = false;
|
||||
while (current <= maxKAllowed)
|
||||
{
|
||||
if (!loggedProgressive)
|
||||
{
|
||||
// Console.WriteLine($"[by-divisor-progressive] p={prime} startK={current} target={target} rho={(current < target ? plan.Rho1 : plan.Rho2):F2} maxK={maxKAllowed}");
|
||||
loggedProgressive = true;
|
||||
}
|
||||
|
||||
BigInteger startRange = current;
|
||||
BigInteger endRange = current;
|
||||
bool composite = CheckDivisorsBigPlanRange(
|
||||
prime,
|
||||
step,
|
||||
allowedMax,
|
||||
startRange,
|
||||
endRange,
|
||||
out bool processedRange,
|
||||
out BigInteger foundRange,
|
||||
ref startRange);
|
||||
if (composite)
|
||||
{
|
||||
foundDivisor = foundRange;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!processedRange)
|
||||
{
|
||||
processedAll = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
double multiplier = current < target ? plan.Rho1 : plan.Rho2;
|
||||
BigInteger advanced = new BigInteger(Math.Ceiling((double)current * multiplier));
|
||||
if (advanced <= current)
|
||||
{
|
||||
advanced = current + BigInteger.One;
|
||||
}
|
||||
|
||||
if (advanced > maxKAllowed)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
current = advanced;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
private bool CheckDivisorsBigPlanRange(
|
||||
ulong prime,
|
||||
in BigInteger step,
|
||||
in BigInteger allowedMax,
|
||||
in BigInteger startK,
|
||||
in BigInteger endK,
|
||||
out bool processedAll,
|
||||
out BigInteger foundDivisor,
|
||||
ref BigInteger currentK)
|
||||
{
|
||||
processedAll = true;
|
||||
foundDivisor = BigInteger.Zero;
|
||||
|
||||
if (startK < BigInteger.One || endK < startK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BigInteger maxKAllowed = (allowedMax - BigInteger.One) / step;
|
||||
TryClampMaxKForMersenneTail(prime, step, ref maxKAllowed);
|
||||
if (startK > maxKAllowed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BigInteger effectiveEnd = endK > maxKAllowed ? maxKAllowed : endK;
|
||||
ushort primeDecimalMask = DivisorGenerator.GetDecimalMask((prime & 3UL) == 3UL);
|
||||
BigInteger divisor = (step * startK) + BigInteger.One;
|
||||
if (divisor > allowedMax)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BigInteger iterations = (effectiveEnd - startK) + BigInteger.One;
|
||||
currentK = startK;
|
||||
|
||||
var rem10 = new BigIntegerCycleRemainderStepper(10);
|
||||
var rem8 = new BigIntegerCycleRemainderStepper(8);
|
||||
var rem3 = new BigIntegerCycleRemainderStepper(3);
|
||||
var rem7 = new BigIntegerCycleRemainderStepper(7);
|
||||
var rem11 = new BigIntegerCycleRemainderStepper(11);
|
||||
var rem13 = new BigIntegerCycleRemainderStepper(13);
|
||||
var rem17 = new BigIntegerCycleRemainderStepper(17);
|
||||
var rem19 = new BigIntegerCycleRemainderStepper(19);
|
||||
|
||||
byte remainder10 = rem10.Initialize(divisor);
|
||||
BigInteger primeBig = (BigInteger)prime;
|
||||
|
||||
while (iterations.Sign > 0)
|
||||
{
|
||||
if (remainder10 == 1 || remainder10 == 7)
|
||||
{
|
||||
if (rem3.NextIsNotDivisible(divisor) &&
|
||||
rem7.NextIsNotDivisible(divisor) &&
|
||||
rem11.NextIsNotDivisible(divisor) &&
|
||||
rem13.NextIsNotDivisible(divisor) &&
|
||||
rem17.NextIsNotDivisible(divisor) &&
|
||||
rem19.NextIsNotDivisible(divisor))
|
||||
{
|
||||
byte remainder8 = rem8.ComputeNext(divisor);
|
||||
if (remainder8 == 1 || remainder8 == 7)
|
||||
{
|
||||
if (((primeDecimalMask >> remainder10) & 1) != 0)
|
||||
{
|
||||
RecordState(currentK);
|
||||
BigInteger powResult = BigInteger.ModPow(BigIntegerNumbers.Two, primeBig, divisor);
|
||||
if (powResult.IsOne && !IsMersenneValue(prime, divisor))
|
||||
{
|
||||
foundDivisor = divisor;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iterations--;
|
||||
if (iterations.IsZero)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
currentK++;
|
||||
divisor += step;
|
||||
remainder10 = rem10.ComputeNext(divisor);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
private bool TryExecutePlannedScan64(
|
||||
ByDivisorClassModel model,
|
||||
ulong prime,
|
||||
ulong allowedMax,
|
||||
ulong minK,
|
||||
out bool processedAll,
|
||||
out ulong foundDivisor)
|
||||
{
|
||||
processedAll = true;
|
||||
foundDivisor = 0UL;
|
||||
|
||||
ulong step64 = prime << 1;
|
||||
if (step64 == 0UL)
|
||||
{
|
||||
processedAll = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
ByDivisorScanTuning tuning = EnvironmentConfiguration.ByDivisorScanTuning;
|
||||
ByDivisorScanPlan plan = model.BuildPlan(prime, allowedMax == ulong.MaxValue ? BigInteger.Zero : new BigInteger(allowedMax), tuning);
|
||||
int suffix = (int)(prime & 1023UL);
|
||||
// ByDivisorClassEntry entry = model.Entries[suffix];
|
||||
// Console.WriteLine($"[by-divisor-plan] p={prime} suffix={suffix} k50={entry.K50:F2} k75={entry.K75:F2} gap={entry.GapProbability:F3} cheap<= {plan.CheapLimit:F0} start={plan.Start:F0} target={plan.Target:F0} rho1={plan.Rho1:F2} rho2={plan.Rho2:F2}");
|
||||
|
||||
ulong effectiveAllowed = allowedMax == 0UL ? ulong.MaxValue : allowedMax;
|
||||
ulong maxKAllowed = effectiveAllowed == ulong.MaxValue ? ulong.MaxValue : ((effectiveAllowed - 1UL) / step64);
|
||||
if (maxKAllowed == 0UL || maxKAllowed < minK)
|
||||
{
|
||||
processedAll = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
ulong cheapLimit = (ulong)Math.Min(plan.CheapLimit, maxKAllowed);
|
||||
if (cheapLimit >= minK)
|
||||
{
|
||||
ulong currentK = minK;
|
||||
bool composite = CheckDivisors64Range(
|
||||
prime,
|
||||
step64,
|
||||
effectiveAllowed,
|
||||
minK,
|
||||
cheapLimit,
|
||||
ref currentK,
|
||||
out bool processedRange,
|
||||
out ulong foundRange);
|
||||
if (composite)
|
||||
{
|
||||
foundDivisor = foundRange;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!processedRange)
|
||||
{
|
||||
processedAll = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
double nextK = Math.Max(plan.Start, cheapLimit + 1d);
|
||||
if (nextK < minK)
|
||||
{
|
||||
nextK = minK;
|
||||
}
|
||||
|
||||
double target = plan.Target <= 0d ? nextK : plan.Target;
|
||||
if (target < nextK)
|
||||
{
|
||||
target = nextK + 1d;
|
||||
}
|
||||
|
||||
ulong current = (ulong)Math.Ceiling(nextK);
|
||||
bool loggedProgressive = false;
|
||||
while (current <= maxKAllowed)
|
||||
{
|
||||
if (!loggedProgressive)
|
||||
{
|
||||
Console.WriteLine($"[by-divisor-progressive] p={prime} startK={current} target={target:F0} rho={(current < target ? plan.Rho1 : plan.Rho2):F2} maxK={maxKAllowed}");
|
||||
loggedProgressive = true;
|
||||
}
|
||||
|
||||
ulong currentK = current;
|
||||
bool compositeSingle = CheckDivisors64Range(
|
||||
prime,
|
||||
step64,
|
||||
effectiveAllowed,
|
||||
currentK,
|
||||
currentK,
|
||||
ref currentK,
|
||||
out bool processedSingle,
|
||||
out ulong foundSingle);
|
||||
if (compositeSingle)
|
||||
{
|
||||
foundDivisor = foundSingle;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!processedSingle)
|
||||
{
|
||||
processedAll = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
double multiplier = current < target ? plan.Rho1 : plan.Rho2;
|
||||
double advanced = Math.Ceiling(current * multiplier);
|
||||
if (advanced <= current)
|
||||
{
|
||||
advanced = current + 1d;
|
||||
}
|
||||
|
||||
if (advanced > maxKAllowed)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
current = (ulong)advanced;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
private bool CheckDivisors64Bit(
|
||||
ulong prime,
|
||||
|
||||
@@ -11,6 +11,10 @@ using System.Runtime.CompilerServices;
|
||||
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForCpuOrder;
|
||||
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForHybridOrder;
|
||||
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForGpuOrder;
|
||||
#elif DivisorSet_Predictive
|
||||
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForCpuOrder;
|
||||
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForHybridOrder;
|
||||
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForGpuOrder;
|
||||
#else
|
||||
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForCpuOrder;
|
||||
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForHybridOrder;
|
||||
|
||||
@@ -20,6 +20,9 @@ public static class EnvironmentConfiguration
|
||||
public static bool UseCpuOrder;
|
||||
public static bool UsePow2GroupDivisors;
|
||||
public static int ByDivisorSpecialRange;
|
||||
public static ByDivisor.ByDivisorClassModel? ByDivisorClassModel;
|
||||
public static ByDivisor.ByDivisorScanTuning ByDivisorScanTuning = ByDivisor.ByDivisorScanTuning.Default;
|
||||
public static double ByDivisorCheapKLimit = 100_000d;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static void Initialize()
|
||||
|
||||
@@ -9,6 +9,9 @@ using System.Runtime.CompilerServices;
|
||||
using MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForCpuOrder;
|
||||
using MersenneNumberDivisorByDivisorCpuTesterWithHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForHybridOrder;
|
||||
using MersenneNumberDivisorByDivisorCpuTesterWithGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForGpuOrder;
|
||||
using MersenneNumberDivisorByDivisorCpuTesterWithPredictiveCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForCpuOrder;
|
||||
using MersenneNumberDivisorByDivisorCpuTesterWithPredictiveHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForHybridOrder;
|
||||
using MersenneNumberDivisorByDivisorCpuTesterWithPredictiveGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForGpuOrder;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
@@ -45,6 +48,37 @@ public static class MersenneNumberDivisorByDivisorTester
|
||||
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||
RunCpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static void Run(
|
||||
List<ulong> candidates,
|
||||
ref MersenneNumberDivisorByDivisorCpuTesterWithPredictiveCpuOrder 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 MersenneNumberDivisorByDivisorCpuTesterWithPredictiveCpuOrder prototype,
|
||||
int workerCount,
|
||||
int partitionSize,
|
||||
Action mc,
|
||||
Action cc,
|
||||
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||
RunPredictiveCpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static void Run(
|
||||
List<ulong> candidates,
|
||||
@@ -76,6 +110,37 @@ public static class MersenneNumberDivisorByDivisorTester
|
||||
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||
RunHybridOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static void Run(
|
||||
List<ulong> candidates,
|
||||
ref MersenneNumberDivisorByDivisorCpuTesterWithPredictiveHybridOrder 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 MersenneNumberDivisorByDivisorCpuTesterWithPredictiveHybridOrder prototype,
|
||||
int workerCount,
|
||||
int partitionSize,
|
||||
Action mc,
|
||||
Action cc,
|
||||
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||
RunPredictiveHybridOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static void Run(
|
||||
List<ulong> candidates,
|
||||
@@ -107,6 +172,37 @@ public static class MersenneNumberDivisorByDivisorTester
|
||||
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||
RunGpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static void Run(
|
||||
List<ulong> candidates,
|
||||
ref MersenneNumberDivisorByDivisorCpuTesterWithPredictiveGpuOrder 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 MersenneNumberDivisorByDivisorCpuTesterWithPredictiveGpuOrder prototype,
|
||||
int workerCount,
|
||||
int partitionSize,
|
||||
Action mc,
|
||||
Action cc,
|
||||
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||
RunPredictiveGpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static void Run(
|
||||
List<ulong> candidates,
|
||||
@@ -1056,6 +1152,84 @@ public static class MersenneNumberDivisorByDivisorTester
|
||||
startGate.Dispose();
|
||||
}
|
||||
|
||||
private static void RunPredictiveCpuOrderFiltered(
|
||||
List<ulong> filteredPrimes,
|
||||
in MersenneNumberDivisorByDivisorCpuTesterWithPredictiveCpuOrder prototype,
|
||||
int workerCount,
|
||||
int partitionSize,
|
||||
Action markComposite,
|
||||
Action clearComposite,
|
||||
Action<ulong, bool, bool, bool, BigInteger> printResult)
|
||||
{
|
||||
MersenneNumberDivisorByDivisorCpuTesterWithPredictiveCpuOrder prototypeCopy = prototype;
|
||||
int totalCount = filteredPrimes.Count;
|
||||
if (partitionSize > totalCount)
|
||||
{
|
||||
partitionSize = totalCount;
|
||||
}
|
||||
|
||||
if (workerCount == 1)
|
||||
{
|
||||
DeterministicRandomCpu.Initialize();
|
||||
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForPredictiveDivisorSetForCpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
||||
foreach (ulong prime in filteredPrimes)
|
||||
{
|
||||
session.ProcessPrime(prime);
|
||||
}
|
||||
|
||||
session.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
TaskScheduler scheduler = UnboundedTaskScheduler.Instance;
|
||||
int nextIndex = 0;
|
||||
Task[] tasks = new Task[workerCount];
|
||||
var startGate = new ManualResetEventSlim(initialState: false);
|
||||
|
||||
for (int taskIndex = 0; taskIndex < workerCount; taskIndex++)
|
||||
{
|
||||
int workerIndex = taskIndex;
|
||||
tasks[taskIndex] = Task.Factory.StartNew(
|
||||
() =>
|
||||
{
|
||||
DeterministicRandomCpu.Initialize();
|
||||
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForPredictiveDivisorSetForCpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
||||
startGate.Wait();
|
||||
Console.WriteLine($"Task started for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
|
||||
|
||||
while (true)
|
||||
{
|
||||
int start = Interlocked.Add(ref nextIndex, partitionSize) - partitionSize;
|
||||
if (start >= totalCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int end = start + partitionSize;
|
||||
if (end > totalCount)
|
||||
{
|
||||
end = totalCount;
|
||||
}
|
||||
|
||||
for (int index = start; index < end; index++)
|
||||
{
|
||||
session.ProcessPrime(filteredPrimes[index]);
|
||||
}
|
||||
}
|
||||
|
||||
session.Dispose();
|
||||
Console.WriteLine($"Task finished for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
|
||||
},
|
||||
CancellationToken.None,
|
||||
TaskCreationOptions.DenyChildAttach,
|
||||
scheduler);
|
||||
}
|
||||
|
||||
startGate.Set();
|
||||
Task.WaitAll(tasks);
|
||||
startGate.Dispose();
|
||||
}
|
||||
|
||||
private static void RunHybridOrderFiltered(
|
||||
List<ulong> filteredPrimes,
|
||||
in MersenneNumberDivisorByDivisorCpuTesterWithHybridOrder prototype,
|
||||
@@ -1147,6 +1321,84 @@ public static class MersenneNumberDivisorByDivisorTester
|
||||
startGate.Dispose();
|
||||
}
|
||||
|
||||
private static void RunPredictiveHybridOrderFiltered(
|
||||
List<ulong> filteredPrimes,
|
||||
in MersenneNumberDivisorByDivisorCpuTesterWithPredictiveHybridOrder prototype,
|
||||
int workerCount,
|
||||
int partitionSize,
|
||||
Action markComposite,
|
||||
Action clearComposite,
|
||||
Action<ulong, bool, bool, bool, BigInteger> printResult)
|
||||
{
|
||||
MersenneNumberDivisorByDivisorCpuTesterWithPredictiveHybridOrder prototypeCopy = prototype;
|
||||
int totalCount = filteredPrimes.Count;
|
||||
if (partitionSize > totalCount)
|
||||
{
|
||||
partitionSize = totalCount;
|
||||
}
|
||||
|
||||
if (workerCount == 1)
|
||||
{
|
||||
DeterministicRandomCpu.Initialize();
|
||||
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForPredictiveDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
||||
foreach (ulong prime in filteredPrimes)
|
||||
{
|
||||
session.ProcessPrime(prime);
|
||||
}
|
||||
|
||||
session.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
TaskScheduler scheduler = UnboundedTaskScheduler.Instance;
|
||||
int nextIndex = 0;
|
||||
Task[] tasks = new Task[workerCount];
|
||||
var startGate = new ManualResetEventSlim(initialState: false);
|
||||
|
||||
for (int taskIndex = 0; taskIndex < workerCount; taskIndex++)
|
||||
{
|
||||
int workerIndex = taskIndex;
|
||||
tasks[taskIndex] = Task.Factory.StartNew(
|
||||
() =>
|
||||
{
|
||||
DeterministicRandomCpu.Initialize();
|
||||
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForPredictiveDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
||||
startGate.Wait();
|
||||
Console.WriteLine($"Task started for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
|
||||
|
||||
while (true)
|
||||
{
|
||||
int start = Interlocked.Add(ref nextIndex, partitionSize) - partitionSize;
|
||||
if (start >= totalCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int end = start + partitionSize;
|
||||
if (end > totalCount)
|
||||
{
|
||||
end = totalCount;
|
||||
}
|
||||
|
||||
for (int index = start; index < end; index++)
|
||||
{
|
||||
session.ProcessPrime(filteredPrimes[index]);
|
||||
}
|
||||
}
|
||||
|
||||
session.Dispose();
|
||||
Console.WriteLine($"Task finished for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
|
||||
},
|
||||
CancellationToken.None,
|
||||
TaskCreationOptions.DenyChildAttach,
|
||||
scheduler);
|
||||
}
|
||||
|
||||
startGate.Set();
|
||||
Task.WaitAll(tasks);
|
||||
startGate.Dispose();
|
||||
}
|
||||
|
||||
private static void RunGpuOrderFiltered(
|
||||
List<ulong> filteredPrimes,
|
||||
in MersenneNumberDivisorByDivisorCpuTesterWithGpuOrder prototype,
|
||||
@@ -1238,6 +1490,84 @@ public static class MersenneNumberDivisorByDivisorTester
|
||||
startGate.Dispose();
|
||||
}
|
||||
|
||||
private static void RunPredictiveGpuOrderFiltered(
|
||||
List<ulong> filteredPrimes,
|
||||
in MersenneNumberDivisorByDivisorCpuTesterWithPredictiveGpuOrder prototype,
|
||||
int workerCount,
|
||||
int partitionSize,
|
||||
Action markComposite,
|
||||
Action clearComposite,
|
||||
Action<ulong, bool, bool, bool, BigInteger> printResult)
|
||||
{
|
||||
MersenneNumberDivisorByDivisorCpuTesterWithPredictiveGpuOrder prototypeCopy = prototype;
|
||||
int totalCount = filteredPrimes.Count;
|
||||
if (partitionSize > totalCount)
|
||||
{
|
||||
partitionSize = totalCount;
|
||||
}
|
||||
|
||||
if (workerCount == 1)
|
||||
{
|
||||
DeterministicRandomCpu.Initialize();
|
||||
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForPredictiveDivisorSetForGpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
||||
foreach (ulong prime in filteredPrimes)
|
||||
{
|
||||
session.ProcessPrime(prime);
|
||||
}
|
||||
|
||||
session.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
TaskScheduler scheduler = UnboundedTaskScheduler.Instance;
|
||||
int nextIndex = 0;
|
||||
Task[] tasks = new Task[workerCount];
|
||||
var startGate = new ManualResetEventSlim(initialState: false);
|
||||
|
||||
for (int taskIndex = 0; taskIndex < workerCount; taskIndex++)
|
||||
{
|
||||
int workerIndex = taskIndex;
|
||||
tasks[taskIndex] = Task.Factory.StartNew(
|
||||
() =>
|
||||
{
|
||||
DeterministicRandomCpu.Initialize();
|
||||
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForPredictiveDivisorSetForGpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
||||
startGate.Wait();
|
||||
Console.WriteLine($"Task started for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
|
||||
|
||||
while (true)
|
||||
{
|
||||
int start = Interlocked.Add(ref nextIndex, partitionSize) - partitionSize;
|
||||
if (start >= totalCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int end = start + partitionSize;
|
||||
if (end > totalCount)
|
||||
{
|
||||
end = totalCount;
|
||||
}
|
||||
|
||||
for (int index = start; index < end; index++)
|
||||
{
|
||||
session.ProcessPrime(filteredPrimes[index]);
|
||||
}
|
||||
}
|
||||
|
||||
session.Dispose();
|
||||
Console.WriteLine($"Task finished for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
|
||||
},
|
||||
CancellationToken.None,
|
||||
TaskCreationOptions.DenyChildAttach,
|
||||
scheduler);
|
||||
}
|
||||
|
||||
startGate.Set();
|
||||
Task.WaitAll(tasks);
|
||||
startGate.Dispose();
|
||||
}
|
||||
|
||||
private static void RunGpuTesterFiltered(
|
||||
List<ulong> filteredPrimes,
|
||||
MersenneNumberDivisorByDivisorGpuTester prototype,
|
||||
|
||||
Reference in new issue
Block a user