Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
497b1fcf89 | ||
|
|
df04ba601c | ||
|
|
9939c5d562 | ||
|
|
54fdff0a4e | ||
|
|
5cac51479e | ||
|
|
02d16bf05c | ||
|
|
a5696ca17f | ||
|
|
56be12ab95 | ||
|
|
bc51962f9d | ||
|
|
801b1be3e3 | ||
|
|
66ca969929 | ||
|
|
bf7c4bab39 | ||
|
|
342eff2726 | ||
|
|
2fc74cf98d | ||
|
|
f630bfb598 | ||
|
|
183b147135 | ||
|
|
c413ee1162 | ||
|
|
a84445ef2a | ||
|
|
cd7636ddbe | ||
|
|
0540411cad | ||
|
|
6cb63b6e73 | ||
|
|
42447c4e43 | ||
|
|
be435b7760 | ||
|
|
0d2a88c45f | ||
|
|
1552e10ff0 | ||
|
|
c53aa517f7 | ||
|
|
43863d03fb | ||
|
|
5bc65464d0 | ||
|
|
735bfa1557 | ||
|
|
fdc2da7dae | ||
|
|
cf5f78c2ef | ||
|
|
0de9871529 | ||
|
|
45dfa3b724 | ||
|
|
fa89958736 | ||
|
|
2726c369f6 | ||
|
|
d0b1639a5d | ||
|
|
a338bb0d0b | ||
|
|
8e779c5c9e | ||
|
|
da3093a9ba | ||
|
|
0410fd2c91 | ||
|
|
f4e365194d | ||
|
|
7c035239b4 | ||
|
|
119690086a | ||
|
|
9537addf38 | ||
|
|
c004e140db | ||
|
|
de2a78b27b | ||
|
|
64d615c402 | ||
|
|
5a17fa64d5 | ||
|
|
dbde6337ea | ||
|
|
4c4d4243fb |
No files matched your search
Vendored
+3
-1
@@ -9,5 +9,7 @@
|
|||||||
"Mersenne",
|
"Mersenne",
|
||||||
"primality",
|
"primality",
|
||||||
"suff"
|
"suff"
|
||||||
]
|
],
|
||||||
|
"geminicodeassist.project": "evenperfectbitscanner",
|
||||||
|
"todo-tree.tree.showCountsInTree": true
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using PerfectNumbers.Core;
|
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner.Benchmarks;
|
namespace EvenPerfectBitScanner.Benchmarks;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using PerfectNumbers.Core;
|
using PerfectNumbers.Core;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner.Benchmarks;
|
namespace EvenPerfectBitScanner.Benchmarks;
|
||||||
|
|
||||||
public enum ModNumericType
|
public enum ModNumericType
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
using PerfectNumbers.Core;
|
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner.Benchmarks;
|
namespace EvenPerfectBitScanner.Benchmarks;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
@@ -101,7 +100,7 @@ public class Pow2ModBenchmarks
|
|||||||
|
|
||||||
for (int i = 0; i < SampleCount; i++)
|
for (int i = 0; i < SampleCount; i++)
|
||||||
{
|
{
|
||||||
checksum ^= exponents[i].Pow2MontgomeryModWindowedGpu(divisors[i], keepMontgomery: false);
|
checksum ^= exponents[i].Pow2MontgomeryModWindowedConvertGpu(divisors[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return checksum;
|
return checksum;
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public class Pow2MontgomeryModBenchmarks
|
|||||||
|
|
||||||
for (int i = 0; i < SampleCount; i++)
|
for (int i = 0; i < SampleCount; i++)
|
||||||
{
|
{
|
||||||
checksum ^= exponents[i].Pow2MontgomeryModWindowedGpu(divisors[i], keepMontgomery: false);
|
checksum ^= exponents[i].Pow2MontgomeryModWindowedConvertGpu(divisors[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return checksum;
|
return checksum;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using PerfectNumbers.Core;
|
using PerfectNumbers.Core;
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner.Benchmarks;
|
namespace EvenPerfectBitScanner.Benchmarks;
|
||||||
|
|||||||
+2
-3
@@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
using PerfectNumbers.Core;
|
using PerfectNumbers.Core;
|
||||||
@@ -34,7 +33,7 @@ public class Pow2MontgomeryModWindowedKeepMontgomeryGpuBenchmarks
|
|||||||
for (int i = 0; i < cases.Length; i++)
|
for (int i = 0; i < cases.Length; i++)
|
||||||
{
|
{
|
||||||
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
|
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
|
||||||
checksum ^= current.Exponent.Pow2MontgomeryModWindowedGpu(current.Divisor, keepMontgomery: false);
|
checksum ^= current.Exponent.Pow2MontgomeryModWindowedConvertGpu(current.Divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
return checksum;
|
return checksum;
|
||||||
@@ -52,7 +51,7 @@ public class Pow2MontgomeryModWindowedKeepMontgomeryGpuBenchmarks
|
|||||||
for (int i = 0; i < cases.Length; i++)
|
for (int i = 0; i < cases.Length; i++)
|
||||||
{
|
{
|
||||||
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
|
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
|
||||||
checksum ^= current.Exponent.Pow2MontgomeryModWindowedGpu(current.Divisor, keepMontgomery: true);
|
checksum ^= current.Exponent.Pow2MontgomeryModWindowedKeepGpu(current.Divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
return checksum;
|
return checksum;
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
using Open.Numeric.Primes;
|
using Open.Numeric.Primes;
|
||||||
using PerfectNumbers.Core;
|
using PerfectNumbers.Core;
|
||||||
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner.Benchmarks;
|
namespace EvenPerfectBitScanner.Benchmarks;
|
||||||
|
|
||||||
@@ -12,7 +10,6 @@ namespace EvenPerfectBitScanner.Benchmarks;
|
|||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class PrimeTesterBenchmarks
|
public class PrimeTesterBenchmarks
|
||||||
{
|
{
|
||||||
private static readonly HeuristicCombinedPrimeTester _heuristicCombinedTester = new();
|
|
||||||
private static readonly HeuristicPrimeTester _heuristicTester = new();
|
private static readonly HeuristicPrimeTester _heuristicTester = new();
|
||||||
private ulong[] _candidates = [];
|
private ulong[] _candidates = [];
|
||||||
|
|
||||||
@@ -29,12 +26,14 @@ public class PrimeTesterBenchmarks
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = _heuristicTester.IsPrimeCpu(_candidates[0]);
|
PrimeOrderCalculatorAccelerator.WarmUp();
|
||||||
_ = _heuristicTester.IsPrimeGpu(_candidates[0]);
|
// HeuristicGroupABPrimeTesterAccelerator.WarmUp();
|
||||||
|
_ = HeuristicPrimeTester.IsPrimeCpu(_candidates[0]);
|
||||||
|
_ = HeuristicPrimeTester.IsPrimeGpu(_candidates[0]);
|
||||||
_ = Prime.Numbers.IsPrime(_candidates[0]);
|
_ = Prime.Numbers.IsPrime(_candidates[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark(Baseline = true)]
|
// [Benchmark]
|
||||||
public int HeuristicCpu()
|
public int HeuristicCpu()
|
||||||
{
|
{
|
||||||
HeuristicPrimeTester tester = _heuristicTester;
|
HeuristicPrimeTester tester = _heuristicTester;
|
||||||
@@ -43,7 +42,7 @@ public class PrimeTesterBenchmarks
|
|||||||
|
|
||||||
for (int i = 0; i < values.Length; i++)
|
for (int i = 0; i < values.Length; i++)
|
||||||
{
|
{
|
||||||
if (tester.IsPrimeCpu(values[i]))
|
if (HeuristicPrimeTester.IsPrimeCpu(values[i]))
|
||||||
{
|
{
|
||||||
primeCount++;
|
primeCount++;
|
||||||
}
|
}
|
||||||
@@ -61,7 +60,7 @@ public class PrimeTesterBenchmarks
|
|||||||
|
|
||||||
for (int i = 0; i < values.Length; i++)
|
for (int i = 0; i < values.Length; i++)
|
||||||
{
|
{
|
||||||
if (tester.IsPrimeGpu(values[i]))
|
if (HeuristicPrimeTester.IsPrimeGpu(values[i]))
|
||||||
{
|
{
|
||||||
primeCount++;
|
primeCount++;
|
||||||
}
|
}
|
||||||
@@ -104,18 +103,15 @@ public class PrimeTesterBenchmarks
|
|||||||
return primeCount;
|
return primeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly PrimeTester _nonHeuristicTester = new();
|
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public int NonHeuristicGpu()
|
public int NonHeuristicGpu()
|
||||||
{
|
{
|
||||||
PrimeTester tester = _nonHeuristicTester;
|
|
||||||
ulong[] values = _candidates;
|
ulong[] values = _candidates;
|
||||||
int primeCount = 0;
|
int primeCount = 0;
|
||||||
|
|
||||||
for (int i = 0; i < values.Length; i++)
|
for (int i = 0; i < values.Length; i++)
|
||||||
{
|
{
|
||||||
if (tester.IsPrimeGpu(values[i]))
|
if (PrimeTester.IsPrimeGpu(values[i]))
|
||||||
{
|
{
|
||||||
primeCount++;
|
primeCount++;
|
||||||
}
|
}
|
||||||
@@ -124,7 +120,7 @@ public class PrimeTesterBenchmarks
|
|||||||
return primeCount;
|
return primeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark(Baseline = true)]
|
||||||
public int NonHeuristicCpu()
|
public int NonHeuristicCpu()
|
||||||
{
|
{
|
||||||
ulong[] values = _candidates;
|
ulong[] values = _candidates;
|
||||||
@@ -141,7 +137,7 @@ public class PrimeTesterBenchmarks
|
|||||||
return primeCount;
|
return primeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
// [Benchmark]
|
||||||
public int OpenNumericPrimes()
|
public int OpenNumericPrimes()
|
||||||
{
|
{
|
||||||
ulong[] values = _candidates;
|
ulong[] values = _candidates;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ internal static class Program
|
|||||||
"/help"
|
"/help"
|
||||||
};
|
};
|
||||||
|
|
||||||
private const string DefaultHeader = "p,searchedMersenne,detailedCheck,passedAllTests";
|
private const string DefaultHeader = "p,divisor,searchedMersenne,detailedCheck,passedAllTests";
|
||||||
|
|
||||||
private static int Main(string[] args)
|
private static int Main(string[] args)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,13 +3,9 @@ using Xunit;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using PerfectNumbers.Core;
|
using PerfectNumbers.Core;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
using EvenPerfectBitScanner;
|
|
||||||
using EvenPerfectBitScanner.Candidates;
|
using EvenPerfectBitScanner.Candidates;
|
||||||
using EvenPerfectBitScanner.Candidates.Transforms;
|
using EvenPerfectBitScanner.Candidates.Transforms;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner.Tests;
|
namespace EvenPerfectBitScanner.Tests;
|
||||||
|
|
||||||
@@ -102,13 +98,11 @@ public class ProgramTests
|
|||||||
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var candidatesField = typeof(MersenneNumberDivisorGpuTester).GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var candidatesField = typeof(MersenneNumberDivisorGpuTester).GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
|
||||||
|
|
||||||
testerField.SetValue(null, new MersenneNumberDivisorGpuTester());
|
testerField.SetValue(null, new MersenneNumberDivisorGpuTester());
|
||||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||||
candidatesField.SetValue(null, new (ulong, uint)[] { (7UL, 3U), (23UL, 11U) });
|
candidatesField.SetValue(null, new (ulong, uint)[] { (7UL, 3U), (23UL, 11U) });
|
||||||
forceCpuProp!.SetValue(null, true);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -122,7 +116,6 @@ public class ProgramTests
|
|||||||
candidatesField.SetValue(null, null);
|
candidatesField.SetValue(null, null);
|
||||||
primeField.SetValue(null, null);
|
primeField.SetValue(null, null);
|
||||||
CandidatesCalculator.ResetResidueTrackers();
|
CandidatesCalculator.ResetResidueTrackers();
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
ResetCliArguments();
|
ResetCliArguments();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,13 +128,11 @@ public class ProgramTests
|
|||||||
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var candidatesField = typeof(MersenneNumberDivisorGpuTester).GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var candidatesField = typeof(MersenneNumberDivisorGpuTester).GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
|
||||||
|
|
||||||
testerField.SetValue(null, new MersenneNumberDivisorGpuTester());
|
testerField.SetValue(null, new MersenneNumberDivisorGpuTester());
|
||||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||||
candidatesField.SetValue(null, Array.Empty<(ulong, uint)>());
|
candidatesField.SetValue(null, Array.Empty<(ulong, uint)>());
|
||||||
forceCpuProp!.SetValue(null, true);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -155,7 +146,6 @@ public class ProgramTests
|
|||||||
candidatesField.SetValue(null, null);
|
candidatesField.SetValue(null, null);
|
||||||
primeField.SetValue(null, null);
|
primeField.SetValue(null, null);
|
||||||
CandidatesCalculator.ResetResidueTrackers();
|
CandidatesCalculator.ResetResidueTrackers();
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
ResetCliArguments();
|
ResetCliArguments();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,12 +158,10 @@ public class ProgramTests
|
|||||||
|
|
||||||
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
|
||||||
|
|
||||||
testerField.SetValue(null, new MersenneNumberDivisorGpuTester());
|
testerField.SetValue(null, new MersenneNumberDivisorGpuTester());
|
||||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||||
forceCpuProp!.SetValue(null, true);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -186,7 +174,6 @@ public class ProgramTests
|
|||||||
testerField.SetValue(null, null);
|
testerField.SetValue(null, null);
|
||||||
primeField.SetValue(null, null);
|
primeField.SetValue(null, null);
|
||||||
CandidatesCalculator.ResetResidueTrackers();
|
CandidatesCalculator.ResetResidueTrackers();
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
ResetCliArguments();
|
ResetCliArguments();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,12 +186,10 @@ public class ProgramTests
|
|||||||
|
|
||||||
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
|
||||||
|
|
||||||
testerField.SetValue(null, new MersenneNumberDivisorGpuTester());
|
testerField.SetValue(null, new MersenneNumberDivisorGpuTester());
|
||||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -221,7 +206,6 @@ public class ProgramTests
|
|||||||
testerField.SetValue(null, null);
|
testerField.SetValue(null, null);
|
||||||
primeField.SetValue(null, null);
|
primeField.SetValue(null, null);
|
||||||
CandidatesCalculator.ResetResidueTrackers();
|
CandidatesCalculator.ResetResidueTrackers();
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
ResetCliArguments();
|
ResetCliArguments();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,12 +217,10 @@ public class ProgramTests
|
|||||||
|
|
||||||
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
|
||||||
|
|
||||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(useIncremental: true, useResidue: true, maxK: 1_000UL), trackAllValues: true));
|
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(useIncremental: true, useResidue: true, maxK: 1_000UL), trackAllValues: true));
|
||||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -255,7 +237,6 @@ public class ProgramTests
|
|||||||
mersenneField.SetValue(null, null);
|
mersenneField.SetValue(null, null);
|
||||||
primeField.SetValue(null, null);
|
primeField.SetValue(null, null);
|
||||||
CandidatesCalculator.ResetResidueTrackers();
|
CandidatesCalculator.ResetResidueTrackers();
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
ResetCliArguments();
|
ResetCliArguments();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,7 +248,6 @@ public class ProgramTests
|
|||||||
|
|
||||||
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
|
||||||
|
|
||||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
||||||
useIncremental: true,
|
useIncremental: true,
|
||||||
@@ -275,7 +255,6 @@ public class ProgramTests
|
|||||||
maxK: 1_024UL), trackAllValues: true));
|
maxK: 1_024UL), trackAllValues: true));
|
||||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -292,7 +271,6 @@ public class ProgramTests
|
|||||||
mersenneField.SetValue(null, null);
|
mersenneField.SetValue(null, null);
|
||||||
primeField.SetValue(null, null);
|
primeField.SetValue(null, null);
|
||||||
CandidatesCalculator.ResetResidueTrackers();
|
CandidatesCalculator.ResetResidueTrackers();
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
ResetCliArguments();
|
ResetCliArguments();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -305,7 +283,6 @@ public class ProgramTests
|
|||||||
|
|
||||||
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
|
||||||
|
|
||||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
||||||
useIncremental: true,
|
useIncremental: true,
|
||||||
@@ -313,7 +290,6 @@ public class ProgramTests
|
|||||||
maxK: 5_000_000UL), trackAllValues: true));
|
maxK: 5_000_000UL), trackAllValues: true));
|
||||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -330,7 +306,6 @@ public class ProgramTests
|
|||||||
mersenneField.SetValue(null, null);
|
mersenneField.SetValue(null, null);
|
||||||
primeField.SetValue(null, null);
|
primeField.SetValue(null, null);
|
||||||
CandidatesCalculator.ResetResidueTrackers();
|
CandidatesCalculator.ResetResidueTrackers();
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
ResetCliArguments();
|
ResetCliArguments();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,7 +317,6 @@ public class ProgramTests
|
|||||||
|
|
||||||
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
|
||||||
|
|
||||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
||||||
useIncremental: true,
|
useIncremental: true,
|
||||||
@@ -350,7 +324,6 @@ public class ProgramTests
|
|||||||
maxK: 5_000_000UL), trackAllValues: true));
|
maxK: 5_000_000UL), trackAllValues: true));
|
||||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -367,7 +340,6 @@ public class ProgramTests
|
|||||||
mersenneField.SetValue(null, null);
|
mersenneField.SetValue(null, null);
|
||||||
primeField.SetValue(null, null);
|
primeField.SetValue(null, null);
|
||||||
CandidatesCalculator.ResetResidueTrackers();
|
CandidatesCalculator.ResetResidueTrackers();
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
ResetCliArguments();
|
ResetCliArguments();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -380,7 +352,6 @@ public class ProgramTests
|
|||||||
|
|
||||||
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
|
||||||
|
|
||||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
||||||
useIncremental: true,
|
useIncremental: true,
|
||||||
@@ -390,7 +361,6 @@ public class ProgramTests
|
|||||||
maxK: 1_024UL), trackAllValues: true));
|
maxK: 1_024UL), trackAllValues: true));
|
||||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true));
|
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true));
|
||||||
forceCpuProp!.SetValue(null, true);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -402,7 +372,6 @@ public class ProgramTests
|
|||||||
mersenneField.SetValue(null, null);
|
mersenneField.SetValue(null, null);
|
||||||
primeField.SetValue(null, null);
|
primeField.SetValue(null, null);
|
||||||
CandidatesCalculator.ResetResidueTrackers();
|
CandidatesCalculator.ResetResidueTrackers();
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
ResetCliArguments();
|
ResetCliArguments();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -417,7 +386,6 @@ public class ProgramTests
|
|||||||
|
|
||||||
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
|
||||||
|
|
||||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
||||||
useIncremental: false,
|
useIncremental: false,
|
||||||
@@ -428,7 +396,6 @@ public class ProgramTests
|
|||||||
maxK: configuration.ResidueMaxK), trackAllValues: true));
|
maxK: configuration.ResidueMaxK), trackAllValues: true));
|
||||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true));
|
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true));
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -440,9 +407,7 @@ public class ProgramTests
|
|||||||
mersenneField.SetValue(null, null);
|
mersenneField.SetValue(null, null);
|
||||||
primeField.SetValue(null, null);
|
primeField.SetValue(null, null);
|
||||||
CandidatesCalculator.ResetResidueTrackers();
|
CandidatesCalculator.ResetResidueTrackers();
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
ResetCliArguments();
|
ResetCliArguments();
|
||||||
GpuContextPool.DisposeAll();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,12 +471,10 @@ public class ProgramTests
|
|||||||
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var compositeField = typeof(Program).GetField("_lastCompositeP", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var compositeField = typeof(Program).GetField("_lastCompositeP", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
|
||||||
|
|
||||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(useIncremental: true, useResidue: false, maxK: 1_000UL), trackAllValues: true));
|
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(useIncremental: true, useResidue: false, maxK: 1_000UL), trackAllValues: true));
|
||||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||||
forceCpuProp!.SetValue(null, true);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -532,7 +495,6 @@ public class ProgramTests
|
|||||||
primeField.SetValue(null, null);
|
primeField.SetValue(null, null);
|
||||||
CandidatesCalculator.ResetResidueTrackers();
|
CandidatesCalculator.ResetResidueTrackers();
|
||||||
compositeField.SetValue(null, false);
|
compositeField.SetValue(null, false);
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
ResetCliArguments();
|
ResetCliArguments();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Numerics;
|
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PerfectNumbers.Core;
|
using PerfectNumbers.Core;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
using System.Buffers.Text;
|
using System.Buffers.Text;
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
|
||||||
using PerfectNumbers.Core;
|
using PerfectNumbers.Core;
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner;
|
namespace EvenPerfectBitScanner;
|
||||||
|
|
||||||
internal static class CalculationResultHandler
|
internal static class CalculationResultHandler
|
||||||
{
|
{
|
||||||
private const string ResultsHeader = "p,searchedMersenne,detailedCheck,passedAllTests";
|
private const string ResultsHeader = "p,divisor,searchedMersenne,detailedCheck,passedAllTests";
|
||||||
private const string PrimeFoundSuffix = " (FOUND VALID CANDIDATES)";
|
private const string PrimeFoundSuffix = " (FOUND VALID CANDIDATES)";
|
||||||
internal const int DefaultWriteBatchSize = 100;
|
internal const int DefaultWriteBatchSize = 100;
|
||||||
|
|
||||||
@@ -95,19 +93,19 @@ internal static class CalculationResultHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal static void HandleResult(ulong currentP, bool searchedMersenne, bool detailedCheck, bool passedAllTests, bool lastWasComposite)
|
internal static void HandleResult(ulong currentP, ulong divisor, bool searchedMersenne, bool detailedCheck, bool passedAllTests, bool lastWasComposite)
|
||||||
{
|
{
|
||||||
// Program flow ensures Initialize(...) and InitializeOutputBuffer() ran before this call. Do not add initialization guards here.
|
// Program flow ensures Initialize(...) and InitializeOutputBuffer() ran before this call. Do not add initialization guards here.
|
||||||
|
|
||||||
Span<byte> stackBuffer = stackalloc byte[128];
|
Span<byte> stackBuffer = stackalloc byte[128];
|
||||||
byte[]? rentedBuffer = null;
|
byte[]? rentedBuffer = null;
|
||||||
Span<byte> utf8Span = stackBuffer;
|
Span<byte> utf8Span = stackBuffer;
|
||||||
int byteCount = TryFormatResultLine(utf8Span, currentP, searchedMersenne, detailedCheck, passedAllTests);
|
int byteCount = TryFormatResultLine(utf8Span, currentP, divisor, searchedMersenne, detailedCheck, passedAllTests);
|
||||||
if (byteCount < 0)
|
if (byteCount < 0)
|
||||||
{
|
{
|
||||||
rentedBuffer = ThreadStaticPools.BytePool.Rent(256);
|
rentedBuffer = ThreadStaticPools.BytePool.Rent(256);
|
||||||
utf8Span = rentedBuffer;
|
utf8Span = rentedBuffer;
|
||||||
byteCount = TryFormatResultLine(utf8Span, currentP, searchedMersenne, detailedCheck, passedAllTests);
|
byteCount = TryFormatResultLine(utf8Span, currentP, divisor, searchedMersenne, detailedCheck, passedAllTests);
|
||||||
if (byteCount < 0)
|
if (byteCount < 0)
|
||||||
{
|
{
|
||||||
ThreadStaticPools.BytePool.Return(rentedBuffer);
|
ThreadStaticPools.BytePool.Return(rentedBuffer);
|
||||||
@@ -231,7 +229,7 @@ internal static class CalculationResultHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static int TryFormatResultLine(Span<byte> destination, ulong currentP, bool searchedMersenne, bool detailedCheck, bool passedAllTests)
|
private static int TryFormatResultLine(Span<byte> destination, ulong currentP, ulong divisor, bool searchedMersenne, bool detailedCheck, bool passedAllTests)
|
||||||
{
|
{
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
@@ -248,6 +246,19 @@ internal static class CalculationResultHandler
|
|||||||
|
|
||||||
destination[offset++] = (byte)',';
|
destination[offset++] = (byte)',';
|
||||||
|
|
||||||
|
if (!Utf8Formatter.TryFormat(divisor, destination[offset..], out written))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += written;
|
||||||
|
if (offset >= destination.Length)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
destination[offset++] = (byte)',';
|
||||||
|
|
||||||
if (!Utf8Formatter.TryFormat(searchedMersenne, destination[offset..], out written))
|
if (!Utf8Formatter.TryFormat(searchedMersenne, destination[offset..], out written))
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner.Candidates;
|
namespace EvenPerfectBitScanner.Candidates;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
|
||||||
using Open.Numeric.Primes;
|
using Open.Numeric.Primes;
|
||||||
using PerfectNumbers.Core;
|
using PerfectNumbers.Core;
|
||||||
using EvenPerfectBitScanner.Candidates.Transforms;
|
using EvenPerfectBitScanner.Candidates.Transforms;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
|
||||||
using EvenPerfectBitScanner.Candidates;
|
|
||||||
using Open.Numeric.Primes;
|
using Open.Numeric.Primes;
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner.Candidates.Transforms;
|
namespace EvenPerfectBitScanner.Candidates.Transforms;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
|
||||||
using EvenPerfectBitScanner.Candidates;
|
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner.Candidates.Transforms;
|
namespace EvenPerfectBitScanner.Candidates.Transforms;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner.Candidates.Transforms;
|
namespace EvenPerfectBitScanner.Candidates.Transforms;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Numerics;
|
|
||||||
using PerfectNumbers.Core;
|
using PerfectNumbers.Core;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Open.Collections;
|
using Open.Collections;
|
||||||
using PerfectNumbers.Core;
|
using PerfectNumbers.Core;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
using EvenPerfectBitScanner;
|
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner.IO;
|
namespace EvenPerfectBitScanner.IO;
|
||||||
|
|
||||||
@@ -101,6 +100,7 @@ internal static class CalculationResultsFile
|
|||||||
int first = 0;
|
int first = 0;
|
||||||
int second = 0;
|
int second = 0;
|
||||||
int third = 0;
|
int third = 0;
|
||||||
|
int fourth = 0;
|
||||||
ulong parsedP = 0UL;
|
ulong parsedP = 0UL;
|
||||||
ReadOnlySpan<char> detailedSpan = default;
|
ReadOnlySpan<char> detailedSpan = default;
|
||||||
ReadOnlySpan<char> passedAllTestsSpan = default;
|
ReadOnlySpan<char> passedAllTestsSpan = default;
|
||||||
@@ -140,8 +140,15 @@ internal static class CalculationResultsFile
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
detailedSpan = span[..third];
|
span = span[(third + 1)..];
|
||||||
passedAllTestsSpan = span[(third + 1)..];
|
fourth = span.IndexOf(',');
|
||||||
|
if (fourth < 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
detailedSpan = span[..fourth];
|
||||||
|
passedAllTestsSpan = span[(fourth + 1)..];
|
||||||
|
|
||||||
if (Utf8CliParser.TryParseBoolean(detailedSpan, out detailed) && Utf8CliParser.TryParseBoolean(passedAllTestsSpan, out passedAllTests))
|
if (Utf8CliParser.TryParseBoolean(detailedSpan, out detailed) && Utf8CliParser.TryParseBoolean(passedAllTestsSpan, out passedAllTests))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace EvenPerfectBitScanner;
|
|||||||
|
|
||||||
internal static class Program
|
internal static class Program
|
||||||
{
|
{
|
||||||
private static ThreadLocal<PrimeTester> PrimeTesters = null!;
|
// private static ThreadLocal<PrimeTester> PrimeTesters = null!;
|
||||||
private static ThreadLocal<MersenneNumberTester> MersenneTesters = null!;
|
private static ThreadLocal<MersenneNumberTester> MersenneTesters = null!;
|
||||||
private const ulong InitialP = PerfectNumberConstants.BiggestKnownEvenPerfectP;
|
private const ulong InitialP = PerfectNumberConstants.BiggestKnownEvenPerfectP;
|
||||||
private static MersenneNumberDivisorGpuTester? _divisorTester;
|
private static MersenneNumberDivisorGpuTester? _divisorTester;
|
||||||
@@ -58,12 +58,14 @@ internal static class Program
|
|||||||
bool startPrimeProvided = _cliArguments.StartPrimeProvided;
|
bool startPrimeProvided = _cliArguments.StartPrimeProvided;
|
||||||
int threadCount = Math.Max(1, _cliArguments.ThreadCount);
|
int threadCount = Math.Max(1, _cliArguments.ThreadCount);
|
||||||
int gpuPrimeBatch = Math.Max(1, _cliArguments.GpuPrimeBatch);
|
int gpuPrimeBatch = Math.Max(1, _cliArguments.GpuPrimeBatch);
|
||||||
|
// int gpuPrimeThreads = SharedGpuContext.Device.MaxNumThreads;
|
||||||
int gpuPrimeThreads = Math.Max(1, _cliArguments.GpuPrimeThreads);
|
int gpuPrimeThreads = Math.Max(1, _cliArguments.GpuPrimeThreads);
|
||||||
UnboundedTaskScheduler.ConfigureThreadCount(threadCount);
|
UnboundedTaskScheduler.ConfigureThreadCount(threadCount);
|
||||||
PrimeTester.GpuBatchSize = gpuPrimeBatch;
|
PrimeTester.GpuBatchSize = gpuPrimeBatch;
|
||||||
GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads);
|
GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads);
|
||||||
GpuContextPool.WarmUpPool(threadCount);
|
Console.WriteLine("Warming up GPU kernels");
|
||||||
PrimeTester.WarmUpGpuKernels(gpuPrimeThreads);
|
PrimeTester.WarmUpGpuKernels(1024);
|
||||||
|
// PrimeTester.WarmUpGpuKernels(gpuPrimeThreads >> 4);
|
||||||
Console.WriteLine("Starting up threads...");
|
Console.WriteLine("Starting up threads...");
|
||||||
_ = UnboundedTaskScheduler.Instance;
|
_ = UnboundedTaskScheduler.Instance;
|
||||||
int blockSize = Math.Max(1, _cliArguments.BlockSize);
|
int blockSize = Math.Max(1, _cliArguments.BlockSize);
|
||||||
@@ -176,7 +178,7 @@ internal static class Program
|
|||||||
blockSize = useByDivisor ? 1 : blockSize;
|
blockSize = useByDivisor ? 1 : blockSize;
|
||||||
|
|
||||||
CandidatesCalculator.Initialize(currentP);
|
CandidatesCalculator.Initialize(currentP);
|
||||||
PrimeTesters = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
// PrimeTesters = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
||||||
// Note: --primes-device controls default device for library kernels; p primality remains CPU here.
|
// Note: --primes-device controls default device for library kernels; p primality remains CPU here.
|
||||||
// Initialize per-thread p residue tracker (Identity model) at currentP
|
// Initialize per-thread p residue tracker (Identity model) at currentP
|
||||||
if (!useDivisor && !_cliArguments.UseByDivisor)
|
if (!useDivisor && !_cliArguments.UseByDivisor)
|
||||||
@@ -420,7 +422,7 @@ internal static class Program
|
|||||||
_byDivisorStartPrime,
|
_byDivisorStartPrime,
|
||||||
static () => _lastCompositeP = true,
|
static () => _lastCompositeP = true,
|
||||||
static () => _lastCompositeP = false,
|
static () => _lastCompositeP = false,
|
||||||
static (candidate, searchedMersenne, detailedCheck, passedAllTests) => CalculationResultHandler.HandleResult(candidate, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP),
|
static (candidate, searchedMersenne, detailedCheck, passedAllTests, divisor) => CalculationResultHandler.HandleResult(candidate, divisor, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP),
|
||||||
threadCount,
|
threadCount,
|
||||||
byDivisorPrimeRange);
|
byDivisorPrimeRange);
|
||||||
|
|
||||||
@@ -481,7 +483,7 @@ internal static class Program
|
|||||||
{
|
{
|
||||||
p = buffer[j];
|
p = buffer[j];
|
||||||
passedAllTests = IsEvenPerfectCandidate(p, divisorCyclesSearchLimit, out searchedMersenne, out detailedCheck);
|
passedAllTests = IsEvenPerfectCandidate(p, divisorCyclesSearchLimit, out searchedMersenne, out detailedCheck);
|
||||||
CalculationResultHandler.HandleResult(p, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP);
|
CalculationResultHandler.HandleResult(p, 0UL, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP);
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -504,7 +506,7 @@ internal static class Program
|
|||||||
}
|
}
|
||||||
|
|
||||||
passedAllTests = IsEvenPerfectCandidate(p, divisorCyclesSearchLimit, out searchedMersenne, out detailedCheck);
|
passedAllTests = IsEvenPerfectCandidate(p, divisorCyclesSearchLimit, out searchedMersenne, out detailedCheck);
|
||||||
CalculationResultHandler.HandleResult(p, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP);
|
CalculationResultHandler.HandleResult(p, 0UL, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP);
|
||||||
|
|
||||||
if (p == maxP)
|
if (p == maxP)
|
||||||
{
|
{
|
||||||
@@ -626,7 +628,7 @@ internal static class Program
|
|||||||
{
|
{
|
||||||
if (!(_runPrimesOnCpu
|
if (!(_runPrimesOnCpu
|
||||||
? PrimeTester.IsPrime(p)
|
? PrimeTester.IsPrime(p)
|
||||||
: PrimeTesters.Value!.IsPrimeGpu(p)))
|
: HeuristicCombinedPrimeTester.IsPrimeGpu(p)))
|
||||||
{
|
{
|
||||||
_lastCompositeP = true;
|
_lastCompositeP = true;
|
||||||
return false;
|
return false;
|
||||||
@@ -647,7 +649,7 @@ internal static class Program
|
|||||||
if (_cliArguments.UseByDivisor)
|
if (_cliArguments.UseByDivisor)
|
||||||
{
|
{
|
||||||
// Windowed pow2mod kernel handles by-divisor scans.
|
// Windowed pow2mod kernel handles by-divisor scans.
|
||||||
return _byDivisorTester!.IsPrime(p, out detailedCheck);
|
return _byDivisorTester!.IsPrime(p, out detailedCheck, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
detailedCheck = MersenneTesters.Value!.IsMersennePrime(p);
|
detailedCheck = MersenneTesters.Value!.IsMersennePrime(p);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Buffers.Text;
|
using System.Buffers.Text;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
using FluentAssertions;
|
|
||||||
using ILGPU;
|
|
||||||
using ILGPU.Runtime;
|
|
||||||
using PerfectNumbers.Core.Gpu;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Tests.Gpu;
|
namespace PerfectNumbers.Core.Tests.Gpu;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using FluentAssertions;
|
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Tests.Gpu;
|
namespace PerfectNumbers.Core.Tests.Gpu;
|
||||||
@@ -114,14 +114,10 @@ public class GpuKernelCompilationTests
|
|||||||
typeof(PrimeOrderGpuHeuristics).GetMethod("GetPow2ModKernel", BindingFlags.NonPublic | BindingFlags.Static)
|
typeof(PrimeOrderGpuHeuristics).GetMethod("GetPow2ModKernel", BindingFlags.NonPublic | BindingFlags.Static)
|
||||||
?? throw new InvalidOperationException("PrimeOrderGpuHeuristics.GetPow2ModKernel not found.");
|
?? throw new InvalidOperationException("PrimeOrderGpuHeuristics.GetPow2ModKernel not found.");
|
||||||
|
|
||||||
private static readonly MethodInfo PrimeOrderGetPow2ModWideKernelMethod =
|
|
||||||
typeof(PrimeOrderGpuHeuristics).GetMethod("GetPow2ModWideKernel", BindingFlags.NonPublic | BindingFlags.Static)
|
|
||||||
?? throw new InvalidOperationException("PrimeOrderGpuHeuristics.GetPow2ModWideKernel not found.");
|
|
||||||
|
|
||||||
private static readonly Action<Index1D, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> MersenneDivisorCyclesKernel =
|
private static readonly Action<Index1D, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> MersenneDivisorCyclesKernel =
|
||||||
DivisorCycleKernels.GpuDivisorCycleKernel;
|
DivisorCycleKernels.GpuDivisorCycleKernel;
|
||||||
|
|
||||||
private static readonly Action<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> PrimeTesterSmallPrimeKernel =
|
private static readonly Action<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> PrimeTesterSmallPrimeKernel =
|
||||||
PrimeTesterKernels.SmallPrimeSieveKernel;
|
PrimeTesterKernels.SmallPrimeSieveKernel;
|
||||||
|
|
||||||
private static readonly Action<Index1D, ArrayView<ulong>, ArrayView<byte>> PrimeTesterSharesFactorKernel =
|
private static readonly Action<Index1D, ArrayView<ulong>, ArrayView<byte>> PrimeTesterSharesFactorKernel =
|
||||||
@@ -325,56 +321,67 @@ public class GpuKernelCompilationTests
|
|||||||
|
|
||||||
private static void CompilePrimeOrderPow2ModWideKernel(Accelerator accelerator)
|
private static void CompilePrimeOrderPow2ModWideKernel(Accelerator accelerator)
|
||||||
{
|
{
|
||||||
_ = PrimeOrderGetPow2ModWideKernelMethod.Invoke(null, new object[] { accelerator });
|
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>>(PrimeOrderGpuHeuristics.Pow2ModKernelWide);
|
||||||
|
_ = kernel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CompileMersenneDivisorCyclesKernel(Accelerator accelerator)
|
private static void CompileMersenneDivisorCyclesKernel(Accelerator accelerator)
|
||||||
{
|
{
|
||||||
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(MersenneDivisorCyclesKernel);
|
var kernel = accelerator.LoadAutoGroupedStreamKernel(MersenneDivisorCyclesKernel);
|
||||||
_ = kernel;
|
_ = kernel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CompilePrimeTesterSmallPrimeKernel(Accelerator accelerator)
|
private static void CompilePrimeTesterSmallPrimeKernel(Accelerator accelerator)
|
||||||
{
|
{
|
||||||
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterSmallPrimeKernel);
|
var kernel = accelerator.LoadAutoGroupedStreamKernel(PrimeTesterSmallPrimeKernel);
|
||||||
_ = kernel;
|
_ = kernel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CompilePrimeTesterSharesFactorKernel(Accelerator accelerator)
|
private static void CompilePrimeTesterSharesFactorKernel(Accelerator accelerator)
|
||||||
{
|
{
|
||||||
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterSharesFactorKernel);
|
var kernel = accelerator.LoadAutoGroupedStreamKernel(PrimeTesterSharesFactorKernel);
|
||||||
_ = kernel;
|
_ = kernel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static KernelType _kernelType = KernelType.Pow2ModKernelScan | KernelType.OrderKernelScan | KernelType.IncrementalKernelScan | KernelType.IncrementalOrderKernelScan | KernelType.Pow2ModOrderKernelScan;
|
||||||
private static void CompileGpuKernelPoolKernels(Accelerator accelerator)
|
private static void CompileGpuKernelPoolKernels(Accelerator accelerator)
|
||||||
{
|
{
|
||||||
_ = accelerator;
|
|
||||||
var lease = GpuKernelPool.GetKernel(useGpuOrder: false);
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
|
int acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_ = lease.OrderKernel;
|
var kernels = GpuKernelPool.GetOrAddKernels(acceleratorIndex, stream, _kernelType);
|
||||||
_ = lease.IncrementalKernel;
|
_ = kernels.Order;
|
||||||
_ = lease.Pow2ModKernel;
|
_ = kernels.Incremental;
|
||||||
_ = lease.IncrementalOrderKernel;
|
_ = kernels.Pow2Mod;
|
||||||
_ = lease.Pow2ModOrderKernel;
|
_ = kernels.IncrementalOrder;
|
||||||
|
_ = kernels.Pow2ModOrder;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
lease.Dispose();
|
stream.Synchronize();
|
||||||
|
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||||
|
// GpuPrimeWorkLimiter.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
var orderLease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_ = orderLease.OrderKernel;
|
var kernels = GpuKernelPool.GetOrAddKernels(acceleratorIndex, stream, _kernelType);
|
||||||
_ = orderLease.IncrementalKernel;
|
_ = kernels.Order;
|
||||||
_ = orderLease.Pow2ModKernel;
|
_ = kernels.Incremental;
|
||||||
_ = orderLease.IncrementalOrderKernel;
|
_ = kernels.Pow2Mod;
|
||||||
_ = orderLease.Pow2ModOrderKernel;
|
_ = kernels.IncrementalOrder;
|
||||||
|
_ = kernels.Pow2ModOrder;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
orderLease.Dispose();
|
stream.Synchronize();
|
||||||
|
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||||
|
// GpuPrimeWorkLimiter.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,19 +18,19 @@ public class GpuPrimeLimiterTests
|
|||||||
var startedSecond = new TaskCompletionSource<bool>();
|
var startedSecond = new TaskCompletionSource<bool>();
|
||||||
var sw = new Stopwatch();
|
var sw = new Stopwatch();
|
||||||
|
|
||||||
var first = GpuPrimeWorkLimiter.Acquire();
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
|
|
||||||
var secondTask = Task.Run(() =>
|
var secondTask = Task.Run(() =>
|
||||||
{
|
{
|
||||||
var second = GpuPrimeWorkLimiter.Acquire();
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
startedSecond.TrySetResult(true);
|
startedSecond.TrySetResult(true);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
second.Dispose();
|
GpuPrimeWorkLimiter.Release();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ public class GpuPrimeLimiterTests
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
// Release first after the initial wait so the second task can proceed.
|
// Release first after the initial wait so the second task can proceed.
|
||||||
first.Dispose();
|
GpuPrimeWorkLimiter.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
await secondTask.WaitAsync(TimeSpan.FromSeconds(1));
|
await secondTask.WaitAsync(TimeSpan.FromSeconds(1));
|
||||||
|
|||||||
@@ -150,10 +150,8 @@ public class NttGpuMathTests
|
|||||||
var modulus = new GpuUInt128(0UL, 17UL);
|
var modulus = new GpuUInt128(0UL, 17UL);
|
||||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||||
|
|
||||||
GpuContextPool.GpuContextLease gpu = GpuContextPool.Rent();
|
var accelerator = SharedGpuContext.CreateAccelerator();
|
||||||
try
|
var stream = accelerator.CreateStream();
|
||||||
{
|
|
||||||
var accelerator = gpu.Accelerator;
|
|
||||||
|
|
||||||
var method = typeof(NttGpuMath).GetMethod("GetSquareCache", BindingFlags.NonPublic | BindingFlags.Static);
|
var method = typeof(NttGpuMath).GetMethod("GetSquareCache", BindingFlags.NonPublic | BindingFlags.Static);
|
||||||
method.Should().NotBeNull();
|
method.Should().NotBeNull();
|
||||||
@@ -168,8 +166,11 @@ public class NttGpuMathTests
|
|||||||
int count = 7; // length 8 -> 7 twiddles
|
int count = 7; // length 8 -> 7 twiddles
|
||||||
var forward = new GpuUInt128[count];
|
var forward = new GpuUInt128[count];
|
||||||
var inverse = new GpuUInt128[count];
|
var inverse = new GpuUInt128[count];
|
||||||
twiddlesMont.CopyToCPU(forward);
|
twiddlesMont.CopyToCPU(stream, forward);
|
||||||
twiddlesInvMont.CopyToCPU(inverse);
|
twiddlesInvMont.CopyToCPU(stream, inverse);
|
||||||
|
stream.Synchronize();
|
||||||
|
stream.Dispose();
|
||||||
|
accelerator.Dispose();
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
@@ -177,11 +178,6 @@ public class NttGpuMathTests
|
|||||||
prod.Should().Be(r2);
|
prod.Should().Be(r2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
|
||||||
gpu.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Category", "Fast")]
|
[Trait("Category", "Fast")]
|
||||||
@@ -345,24 +341,15 @@ public class NttGpuMathTests
|
|||||||
values[1] = new GpuUInt128(0UL, 2UL);
|
values[1] = new GpuUInt128(0UL, 2UL);
|
||||||
var modulus = new GpuUInt128(0UL, 17UL);
|
var modulus = new GpuUInt128(0UL, 17UL);
|
||||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||||
GpuContextPool.GpuContextLease gpu = GpuContextPool.Rent();
|
Accelerator accelerator = SharedGpuContext.CreateAccelerator();
|
||||||
try
|
var stream = accelerator.CreateStream();
|
||||||
{
|
var buffer = accelerator.Allocate1D<GpuUInt128>(values.Length);
|
||||||
var buffer = gpu.Accelerator.Allocate1D<GpuUInt128>(values.Length);
|
buffer.View.CopyFromCPU(stream, ref values[0], values.Length);
|
||||||
try
|
NttGpuMath.SquareDevice(accelerator, stream, buffer.View, modulus, primitiveRoot);
|
||||||
{
|
stream.Synchronize();
|
||||||
buffer.View.CopyFromCPU(ref values[0], values.Length);
|
stream.Dispose();
|
||||||
NttGpuMath.SquareDevice(gpu.Accelerator, buffer.View, modulus, primitiveRoot);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
buffer.Dispose();
|
buffer.Dispose();
|
||||||
}
|
accelerator.Dispose();
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
gpu.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that the square cache now holds entries for the accelerator.
|
// Verify that the square cache now holds entries for the accelerator.
|
||||||
var field = typeof(NttGpuMath).GetField("SquareCache", BindingFlags.NonPublic | BindingFlags.Static)!;
|
var field = typeof(NttGpuMath).GetField("SquareCache", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
@@ -370,8 +357,6 @@ public class NttGpuMathTests
|
|||||||
int count = (int)cacheObj.GetType().GetProperty("Count")!.GetValue(cacheObj)!;
|
int count = (int)cacheObj.GetType().GetProperty("Count")!.GetValue(cacheObj)!;
|
||||||
count.Should().BeGreaterThan(0);
|
count.Should().BeGreaterThan(0);
|
||||||
|
|
||||||
GpuContextPool.DisposeAll();
|
|
||||||
|
|
||||||
count = (int)cacheObj.GetType().GetProperty("Count")!.GetValue(cacheObj)!;
|
count = (int)cacheObj.GetType().GetProperty("Count")!.GetValue(cacheObj)!;
|
||||||
count.Should().Be(0);
|
count.Should().Be(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,24 +33,21 @@ public class NttMontgomeryTests
|
|||||||
NttGpuMath.Square(cpu, modulus, primitiveRoot);
|
NttGpuMath.Square(cpu, modulus, primitiveRoot);
|
||||||
|
|
||||||
// Device execution
|
// Device execution
|
||||||
GpuContextPool.GpuContextLease context = GpuContextPool.Rent();
|
var accelerator = SharedGpuContext.CreateAccelerator();
|
||||||
try
|
var stream = accelerator.CreateStream();
|
||||||
{
|
|
||||||
var accelerator = context.Accelerator;
|
|
||||||
using var buffer = accelerator.Allocate1D<GpuUInt128>(gpu.Length);
|
using var buffer = accelerator.Allocate1D<GpuUInt128>(gpu.Length);
|
||||||
buffer.View.CopyFromCPU(ref gpu[0], gpu.Length);
|
buffer.View.CopyFromCPU(stream, ref gpu[0], gpu.Length);
|
||||||
NttGpuMath.SquareDevice(accelerator, buffer.View, modulus, primitiveRoot);
|
NttGpuMath.SquareDevice(accelerator, stream, buffer.View, modulus, primitiveRoot);
|
||||||
buffer.View.CopyToCPU(ref gpu[0], gpu.Length);
|
buffer.View.CopyToCPU(stream, ref gpu[0], gpu.Length);
|
||||||
|
stream.Synchronize();
|
||||||
|
stream.Dispose();
|
||||||
|
accelerator.Dispose();
|
||||||
|
|
||||||
for (int i = 0; i < cpu.Length; i++)
|
for (int i = 0; i < cpu.Length; i++)
|
||||||
{
|
{
|
||||||
gpu[i].Should().Be(cpu[i]);
|
gpu[i].Should().Be(cpu[i]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
context.Dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PerfectNumbers.Core.Gpu;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Tests.Gpu;
|
namespace PerfectNumbers.Core.Tests.Gpu;
|
||||||
@@ -9,37 +8,23 @@ public class PrimeTesterGpuTests
|
|||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void IsPrimeGpu_accepts_known_primes()
|
public void IsPrimeGpu_accepts_known_primes()
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
var tester = new PrimeTester();
|
var tester = new PrimeTester();
|
||||||
ulong[] primes = [31UL, 61UL, 89UL, 107UL, 127UL, 521UL];
|
ulong[] primes = [31UL, 61UL, 89UL, 107UL, 127UL, 521UL];
|
||||||
foreach (ulong prime in primes)
|
foreach (ulong prime in primes)
|
||||||
{
|
{
|
||||||
tester.IsPrimeGpu(prime).Should().BeTrue();
|
PrimeTester.IsPrimeGpu(prime).Should().BeTrue();
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
GpuContextPool.DisposeAll();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void IsPrimeGpu_rejects_composites()
|
public void IsPrimeGpu_rejects_composites()
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
var tester = new PrimeTester();
|
var tester = new PrimeTester();
|
||||||
ulong[] composites = [33UL, 39UL, 51UL, 77UL, 91UL];
|
ulong[] composites = [33UL, 39UL, 51UL, 77UL, 91UL];
|
||||||
foreach (ulong composite in composites)
|
foreach (ulong composite in composites)
|
||||||
{
|
{
|
||||||
tester.IsPrimeGpu(composite).Should().BeFalse();
|
PrimeTester.IsPrimeGpu(composite).Should().BeFalse();
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
GpuContextPool.DisposeAll();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PerfectNumbers.Core;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Tests.Gpu;
|
namespace PerfectNumbers.Core.Tests.Gpu;
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using PerfectNumbers.Core;
|
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
|||||||
{
|
{
|
||||||
var candidates = new List<ulong> { 5UL, 7UL, 11UL, 13UL };
|
var candidates = new List<ulong> { 5UL, 7UL, 11UL, 13UL };
|
||||||
var tester = new FakeTester();
|
var tester = new FakeTester();
|
||||||
var results = new List<(ulong Prime, bool Searched, bool Detailed, bool Passed)>();
|
var results = new List<(ulong Prime, bool Searched, bool Detailed, bool Passed, ulong Divisor)>();
|
||||||
int compositeMarks = 0;
|
int compositeMarks = 0;
|
||||||
int clearedMarks = 0;
|
int clearedMarks = 0;
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
|||||||
startPrime: 0UL,
|
startPrime: 0UL,
|
||||||
markComposite: () => compositeMarks++,
|
markComposite: () => compositeMarks++,
|
||||||
clearComposite: () => clearedMarks++,
|
clearComposite: () => clearedMarks++,
|
||||||
printResult: (prime, searched, detailed, passed) => results.Add((prime, searched, detailed, passed)),
|
printResult: (prime, searched, detailed, passed, divisor) => results.Add((prime, searched, detailed, passed, divisor)),
|
||||||
threadCount: 1,
|
threadCount: 1,
|
||||||
primesPerTask: 2);
|
primesPerTask: 2);
|
||||||
|
|
||||||
@@ -32,10 +32,10 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
|||||||
results.Should().Equal(
|
results.Should().Equal(
|
||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
(5UL, true, true, true),
|
(5UL, true, true, true, 0UL),
|
||||||
(7UL, true, true, false),
|
(7UL, true, true, false, 7UL),
|
||||||
(11UL, true, true, false),
|
(11UL, true, true, false, 11UL),
|
||||||
(13UL, true, true, true),
|
(13UL, true, true, true, 0UL),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,10 +57,23 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
|||||||
return DivisorLimit;
|
return DivisorLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPrime(ulong prime, out bool divisorsExhausted)
|
public bool IsPrime(ulong prime, out bool divisorsExhausted, out ulong divisor)
|
||||||
{
|
{
|
||||||
divisorsExhausted = true;
|
divisorsExhausted = true;
|
||||||
return prime % 7UL != 0UL && prime % 11UL != 0UL;
|
if (prime % 7UL == 0UL)
|
||||||
|
{
|
||||||
|
divisor = 7UL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prime % 11UL == 0UL)
|
||||||
|
{
|
||||||
|
divisor = 11UL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
divisor = 0UL;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession()
|
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession()
|
||||||
@@ -83,7 +96,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
|||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Return()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,14 +13,17 @@ public class MersenneNumberDivisorCpuTesterTests
|
|||||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||||
tester.ConfigureFromMaxPrime(11UL);
|
tester.ConfigureFromMaxPrime(11UL);
|
||||||
|
|
||||||
tester.IsPrime(5UL, out bool divisorsExhausted).Should().BeTrue();
|
tester.IsPrime(5UL, out bool divisorsExhausted, out ulong divisor).Should().BeTrue();
|
||||||
divisorsExhausted.Should().BeTrue();
|
divisorsExhausted.Should().BeTrue();
|
||||||
|
divisor.Should().Be(0UL);
|
||||||
|
|
||||||
tester.IsPrime(7UL, out divisorsExhausted).Should().BeTrue();
|
tester.IsPrime(7UL, out divisorsExhausted, out divisor).Should().BeTrue();
|
||||||
divisorsExhausted.Should().BeTrue();
|
divisorsExhausted.Should().BeTrue();
|
||||||
|
divisor.Should().Be(0UL);
|
||||||
|
|
||||||
tester.IsPrime(11UL, out divisorsExhausted).Should().BeFalse();
|
tester.IsPrime(11UL, out divisorsExhausted, out divisor).Should().BeFalse();
|
||||||
divisorsExhausted.Should().BeTrue();
|
divisorsExhausted.Should().BeTrue();
|
||||||
|
divisor.Should().BeGreaterThan(0UL);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -47,7 +50,7 @@ public class MersenneNumberDivisorCpuTesterTests
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
session.Dispose();
|
session.Return();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +75,7 @@ public class MersenneNumberDivisorCpuTesterTests
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
session.Dispose();
|
session.Return();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +100,7 @@ public class MersenneNumberDivisorCpuTesterTests
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
session.Dispose();
|
session.Return();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,14 +73,17 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||||
tester.ConfigureFromMaxPrime(43UL);
|
tester.ConfigureFromMaxPrime(43UL);
|
||||||
|
|
||||||
tester.IsPrime(31UL, out bool exhausted).Should().BeTrue();
|
tester.IsPrime(31UL, out bool exhausted, out ulong divisor).Should().BeTrue();
|
||||||
exhausted.Should().BeTrue();
|
exhausted.Should().BeTrue();
|
||||||
|
divisor.Should().Be(0UL);
|
||||||
|
|
||||||
tester.IsPrime(37UL, out exhausted).Should().BeFalse();
|
tester.IsPrime(37UL, out exhausted, out divisor).Should().BeFalse();
|
||||||
exhausted.Should().BeTrue();
|
exhausted.Should().BeTrue();
|
||||||
|
divisor.Should().BeGreaterThan(0UL);
|
||||||
|
|
||||||
tester.IsPrime(43UL, out exhausted).Should().BeFalse();
|
tester.IsPrime(43UL, out exhausted, out divisor).Should().BeFalse();
|
||||||
exhausted.Should().BeTrue();
|
exhausted.Should().BeTrue();
|
||||||
|
divisor.Should().BeGreaterThan(0UL);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -105,12 +108,15 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||||
tester.ConfigureFromMaxPrime(41UL);
|
tester.ConfigureFromMaxPrime(41UL);
|
||||||
|
|
||||||
tester.IsPrime(31UL, out _).Should().BeTrue();
|
tester.IsPrime(31UL, out _, out ulong divisor).Should().BeTrue();
|
||||||
tester.IsPrime(37UL, out bool exhausted).Should().BeFalse();
|
divisor.Should().Be(0UL);
|
||||||
|
tester.IsPrime(37UL, out bool exhausted, out divisor).Should().BeFalse();
|
||||||
exhausted.Should().BeTrue();
|
exhausted.Should().BeTrue();
|
||||||
|
divisor.Should().BeGreaterThan(0UL);
|
||||||
|
|
||||||
tester.IsPrime(41UL, out bool divisorsExhausted).Should().BeFalse();
|
tester.IsPrime(41UL, out bool divisorsExhausted, out divisor).Should().BeFalse();
|
||||||
divisorsExhausted.Should().BeTrue();
|
divisorsExhausted.Should().BeTrue();
|
||||||
|
divisor.Should().BeGreaterThan(0UL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -124,8 +130,16 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||||
tester.ConfigureFromMaxPrime(exponent);
|
tester.ConfigureFromMaxPrime(exponent);
|
||||||
|
|
||||||
tester.IsPrime(exponent, out bool exhausted).Should().Be(expectedPrime);
|
tester.IsPrime(exponent, out bool exhausted, out ulong divisor).Should().Be(expectedPrime);
|
||||||
exhausted.Should().BeTrue();
|
exhausted.Should().BeTrue();
|
||||||
|
if (expectedPrime)
|
||||||
|
{
|
||||||
|
divisor.Should().Be(0UL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
divisor.Should().BeGreaterThan(0UL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Category", "Fast")]
|
[Trait("Category", "Fast")]
|
||||||
@@ -138,8 +152,9 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
|
|
||||||
tester.ConfigureFromMaxPrime(43UL);
|
tester.ConfigureFromMaxPrime(43UL);
|
||||||
|
|
||||||
tester.IsPrime(41UL, out bool divisorsExhausted).Should().BeFalse();
|
tester.IsPrime(41UL, out bool divisorsExhausted, out ulong divisor).Should().BeFalse();
|
||||||
divisorsExhausted.Should().BeTrue();
|
divisorsExhausted.Should().BeTrue();
|
||||||
|
divisor.Should().BeGreaterThan(0UL);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -161,7 +176,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
session.Dispose();
|
session.Return();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +204,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
session.Dispose();
|
session.Return();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +232,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
session.Dispose();
|
session.Return();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +261,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
session.Dispose();
|
session.Return();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +290,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
session.Dispose();
|
session.Return();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,15 +324,15 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
.GetField("_divisorLimit", BindingFlags.NonPublic | BindingFlags.Instance)!
|
.GetField("_divisorLimit", BindingFlags.NonPublic | BindingFlags.Instance)!
|
||||||
.SetValue(tester, 200UL);
|
.SetValue(tester, 200UL);
|
||||||
|
|
||||||
tester.IsPrime(19UL, out bool divisorsExhausted).Should().BeTrue();
|
tester.IsPrime(19UL, out bool divisorsExhausted, out ulong divisor).Should().BeTrue();
|
||||||
divisorsExhausted.Should().BeTrue();
|
divisorsExhausted.Should().BeTrue();
|
||||||
|
divisor.Should().Be(0UL);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
tableField.SetValue(cycles, originalTable);
|
tableField.SetValue(cycles, originalTable);
|
||||||
smallCyclesField.SetValue(cycles, originalSmall);
|
smallCyclesField.SetValue(cycles, originalSmall);
|
||||||
DivisorCycleCache.Shared.RefreshSnapshot();
|
DivisorCycleCache.Shared.RefreshSnapshot();
|
||||||
GpuContextPool.DisposeAll();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,7 +360,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
session.Dispose();
|
session.Return();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PerfectNumbers.Core;
|
|
||||||
using PerfectNumbers.Core.Cpu;
|
using PerfectNumbers.Core.Cpu;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using UInt128 = System.UInt128;
|
using UInt128 = System.UInt128;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PerfectNumbers.Core;
|
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using UInt128 = System.UInt128;
|
using UInt128 = System.UInt128;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PerfectNumbers.Core;
|
|
||||||
using PerfectNumbers.Core.Cpu;
|
using PerfectNumbers.Core.Cpu;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using UInt128 = System.UInt128;
|
using UInt128 = System.UInt128;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PerfectNumbers.Core;
|
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using UInt128 = System.UInt128;
|
using UInt128 = System.UInt128;
|
||||||
@@ -55,7 +54,6 @@ public class MersenneNumberOrderGpuTesterTests
|
|||||||
ulong[]? backupSmall = originalSmall is null ? null : (ulong[])originalSmall.Clone();
|
ulong[]? backupSmall = originalSmall is null ? null : (ulong[])originalSmall.Clone();
|
||||||
|
|
||||||
cycles.LoadFrom(tempPath);
|
cycles.LoadFrom(tempPath);
|
||||||
GpuContextPool.DisposeAll();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -75,7 +73,6 @@ public class MersenneNumberOrderGpuTesterTests
|
|||||||
{
|
{
|
||||||
tableField.SetValue(cycles, originalTable);
|
tableField.SetValue(cycles, originalTable);
|
||||||
smallCyclesField.SetValue(cycles, backupSmall);
|
smallCyclesField.SetValue(cycles, backupSmall);
|
||||||
GpuContextPool.DisposeAll();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PerfectNumbers.Core;
|
|
||||||
using PerfectNumbers.Core.Cpu;
|
using PerfectNumbers.Core.Cpu;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using UInt128 = System.UInt128;
|
using UInt128 = System.UInt128;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PerfectNumbers.Core;
|
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using UInt128 = System.UInt128;
|
using UInt128 = System.UInt128;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class PrimeTesterIsPrimeTests
|
|||||||
{
|
{
|
||||||
var tester = new PrimeTester();
|
var tester = new PrimeTester();
|
||||||
|
|
||||||
tester.IsPrimeGpu(n).Should().Be(expected);
|
PrimeTester.IsPrimeGpu(n).Should().Be(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -64,7 +64,7 @@ public class PrimeTesterIsPrimeTests
|
|||||||
var testers = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
var testers = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
||||||
Parallel.For(0, primes.Length, i =>
|
Parallel.For(0, primes.Length, i =>
|
||||||
{
|
{
|
||||||
results[i] = testers.Value!.IsPrimeGpu(primes[i]);
|
results[i] = PrimeTester.IsPrimeGpu(primes[i]);
|
||||||
});
|
});
|
||||||
|
|
||||||
results.Should().AllBeEquivalentTo(true);
|
results.Should().AllBeEquivalentTo(true);
|
||||||
@@ -73,7 +73,7 @@ public class PrimeTesterIsPrimeTests
|
|||||||
{
|
{
|
||||||
foreach (ulong prime in primes)
|
foreach (ulong prime in primes)
|
||||||
{
|
{
|
||||||
testers.Value!.IsPrimeGpu(prime).Should().BeTrue();
|
PrimeTester.IsPrimeGpu(prime).Should().BeTrue();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
namespace PerfectNumbers.Core;
|
namespace PerfectNumbers.Core.Cpu;
|
||||||
|
|
||||||
internal static class DeterministicRandom
|
internal static class DeterministicRandomCpu
|
||||||
{
|
{
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static ulong s_threadState;
|
private static ulong s_threadState;
|
||||||
@@ -62,7 +62,7 @@ internal sealed class MersenneCpuDivisorScanSession : IMersenneNumberDivisorByDi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Return()
|
||||||
{
|
{
|
||||||
ThreadStaticPools.ReturnMersenneCpuDivisorSession(this);
|
ThreadStaticPools.ReturnMersenneCpuDivisorSession(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using PerfectNumbers.Core;
|
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Cpu;
|
namespace PerfectNumbers.Core.Cpu;
|
||||||
|
|
||||||
@@ -43,7 +42,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public ulong GetAllowedMaxDivisor(ulong prime) => ComputeAllowedMaxDivisor(prime, _divisorLimit);
|
public ulong GetAllowedMaxDivisor(ulong prime) => ComputeAllowedMaxDivisor(prime, _divisorLimit);
|
||||||
|
|
||||||
public bool IsPrime(ulong prime, out bool divisorsExhausted)
|
public bool IsPrime(ulong prime, out bool divisorsExhausted, out ulong divisor)
|
||||||
{
|
{
|
||||||
ulong allowedMax = ComputeAllowedMaxDivisor(prime, _divisorLimit);
|
ulong allowedMax = ComputeAllowedMaxDivisor(prime, _divisorLimit);
|
||||||
|
|
||||||
@@ -61,7 +60,8 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
bool composite = CheckDivisors(
|
bool composite = CheckDivisors(
|
||||||
prime,
|
prime,
|
||||||
allowedMax,
|
allowedMax,
|
||||||
out processedAll);
|
out processedAll,
|
||||||
|
out divisor);
|
||||||
|
|
||||||
if (composite)
|
if (composite)
|
||||||
{
|
{
|
||||||
@@ -70,6 +70,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
}
|
}
|
||||||
|
|
||||||
divisorsExhausted = processedAll || composite;
|
divisorsExhausted = processedAll || composite;
|
||||||
|
divisor = 0UL;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,10 +98,10 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
private bool CheckDivisors(
|
private bool CheckDivisors(
|
||||||
ulong prime,
|
ulong prime,
|
||||||
ulong allowedMax,
|
ulong allowedMax,
|
||||||
out bool processedAll)
|
out bool processedAll,
|
||||||
|
out ulong foundDivisor)
|
||||||
{
|
{
|
||||||
processedAll = false;
|
foundDivisor = 0UL;
|
||||||
|
|
||||||
// The EvenPerfectBitScanner feeds primes >= 138,000,000 here, so allowedMax >= 3 in production runs.
|
// The EvenPerfectBitScanner feeds primes >= 138,000,000 here, so allowedMax >= 3 in production runs.
|
||||||
// Keeping the guard commented out documents the reasoning for benchmarks and tests.
|
// Keeping the guard commented out documents the reasoning for benchmarks and tests.
|
||||||
// if (allowedMax < 3UL)
|
// if (allowedMax < 3UL)
|
||||||
@@ -126,12 +127,13 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
if (divisor.CompareTo(limit) > 0)
|
if (divisor.CompareTo(limit) > 0)
|
||||||
{
|
{
|
||||||
processedAll = true;
|
processedAll = true;
|
||||||
|
foundDivisor = 0UL;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intentionally recomputes factorizations without a per-thread cache.
|
// Intentionally recomputes factorizations without a per-thread cache.
|
||||||
// The previous factor cache recorded virtually no hits and only slowed down the scan.
|
// The previous factor cache recorded virtually no hits and only slowed down the scan.
|
||||||
DivisorCycleCache cycleCache = DivisorCycleCache.Shared;
|
_ = DivisorCycleCache.Shared;
|
||||||
|
|
||||||
ulong stepHigh = step.High;
|
ulong stepHigh = step.High;
|
||||||
ulong stepLow = step.Low;
|
ulong stepLow = step.Low;
|
||||||
@@ -185,7 +187,8 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
remainder3,
|
remainder3,
|
||||||
remainder7,
|
remainder7,
|
||||||
remainder11,
|
remainder11,
|
||||||
out processedAll);
|
out processedAll,
|
||||||
|
out foundDivisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong divisorHigh = divisor.High;
|
ulong divisorHigh = divisor.High;
|
||||||
@@ -201,6 +204,8 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
remainder7 = (byte)((((divisorHigh % 7UL) * 2UL) + (divisorLow % 7UL)) % 7UL);
|
remainder7 = (byte)((((divisorHigh % 7UL) * 2UL) + (divisorLow % 7UL)) % 7UL);
|
||||||
remainder11 = (byte)((((divisorHigh % 11UL) * 5UL) + (divisorLow % 11UL)) % 11UL);
|
remainder11 = (byte)((((divisorHigh % 11UL) * 5UL) + (divisorLow % 11UL)) % 11UL);
|
||||||
|
|
||||||
|
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||||
|
|
||||||
while (divisor.CompareTo(limit) <= 0)
|
while (divisor.CompareTo(limit) <= 0)
|
||||||
{
|
{
|
||||||
bool passesSmallModuli = remainder3 != 0 && remainder7 != 0 && remainder11 != 0;
|
bool passesSmallModuli = remainder3 != 0 && remainder7 != 0 && remainder11 != 0;
|
||||||
@@ -217,6 +222,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
// else
|
// else
|
||||||
{
|
{
|
||||||
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
|
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
|
||||||
|
gpu,
|
||||||
candidate,
|
candidate,
|
||||||
prime,
|
prime,
|
||||||
divisorData,
|
divisorData,
|
||||||
@@ -240,7 +246,9 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
{
|
{
|
||||||
// A cycle equal to the tested exponent (which is prime in this path) guarantees that the candidate divides
|
// A cycle equal to the tested exponent (which is prime in this path) guarantees that the candidate divides
|
||||||
// the corresponding Mersenne number because the order of 2 modulo the divisor is exactly p.
|
// the corresponding Mersenne number because the order of 2 modulo the divisor is exactly p.
|
||||||
|
foundDivisor = candidate;
|
||||||
processedAll = true;
|
processedAll = true;
|
||||||
|
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +265,10 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
remainder7 = AddMod7(remainder7, step7);
|
remainder7 = AddMod7(remainder7, step7);
|
||||||
remainder11 = AddMod11(remainder11, step11);
|
remainder11 = AddMod11(remainder11, step11);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||||
processedAll = true;
|
processedAll = true;
|
||||||
|
foundDivisor = 0UL;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,11 +288,14 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
byte remainder3,
|
byte remainder3,
|
||||||
byte remainder7,
|
byte remainder7,
|
||||||
byte remainder11,
|
byte remainder11,
|
||||||
out bool processedAll)
|
out bool processedAll,
|
||||||
|
out ulong foundDivisor)
|
||||||
{
|
{
|
||||||
processedAll = false;
|
processedAll = false;
|
||||||
|
foundDivisor = 0UL;
|
||||||
|
|
||||||
bool canAdvance = step <= limit;
|
bool canAdvance = step <= limit;
|
||||||
|
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||||
if (!canAdvance)
|
if (!canAdvance)
|
||||||
{
|
{
|
||||||
if (divisor <= limit)
|
if (divisor <= limit)
|
||||||
@@ -291,6 +305,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
|
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
|
||||||
ulong divisorCycle;
|
ulong divisorCycle;
|
||||||
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
|
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
|
||||||
|
gpu,
|
||||||
divisor,
|
divisor,
|
||||||
prime,
|
prime,
|
||||||
divisorData,
|
divisorData,
|
||||||
@@ -309,7 +324,9 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
if (divisorCycle == prime)
|
if (divisorCycle == prime)
|
||||||
{
|
{
|
||||||
|
foundDivisor = divisor;
|
||||||
processedAll = true;
|
processedAll = true;
|
||||||
|
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,12 +338,14 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
}
|
}
|
||||||
|
|
||||||
processedAll = true;
|
processedAll = true;
|
||||||
|
foundDivisor = 0UL;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (divisor > limit)
|
if (divisor > limit)
|
||||||
{
|
{
|
||||||
processedAll = true;
|
processedAll = true;
|
||||||
|
foundDivisor = 0UL;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,6 +359,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
|
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
|
||||||
ulong divisorCycle;
|
ulong divisorCycle;
|
||||||
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
|
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
|
||||||
|
gpu,
|
||||||
divisor,
|
divisor,
|
||||||
prime,
|
prime,
|
||||||
divisorData,
|
divisorData,
|
||||||
@@ -358,7 +378,9 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
if (divisorCycle == prime)
|
if (divisorCycle == prime)
|
||||||
{
|
{
|
||||||
|
foundDivisor = divisor;
|
||||||
processedAll = true;
|
processedAll = true;
|
||||||
|
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,6 +394,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
if (remainingIterations == 0UL)
|
if (remainingIterations == 0UL)
|
||||||
{
|
{
|
||||||
processedAll = true;
|
processedAll = true;
|
||||||
|
foundDivisor = 0UL;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
using System.Buffers;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core;
|
namespace PerfectNumbers.Core;
|
||||||
|
|
||||||
@@ -14,8 +11,9 @@ public sealed class DivisorCycleCache
|
|||||||
private const byte ByteZero = 0;
|
private const byte ByteZero = 0;
|
||||||
private const byte ByteOne = 1;
|
private const byte ByteOne = 1;
|
||||||
private const int StackBufferThreshold = 128;
|
private const int StackBufferThreshold = 128;
|
||||||
|
private readonly Accelerator _accelerator;
|
||||||
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>> _gpuKernelCache = new(AcceleratorReferenceComparer.Instance);
|
private readonly int _acceleratorIndex;
|
||||||
|
private readonly Action<AcceleratorStream, Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>> _gpuKernel;
|
||||||
private volatile ulong[] _snapshot;
|
private volatile ulong[] _snapshot;
|
||||||
private volatile bool _useGpuGeneration = true;
|
private volatile bool _useGpuGeneration = true;
|
||||||
private readonly int _divisorCyclesBatchSize;
|
private readonly int _divisorCyclesBatchSize;
|
||||||
@@ -36,9 +34,16 @@ public sealed class DivisorCycleCache
|
|||||||
|
|
||||||
public int PreferredBatchSize => _divisorCyclesBatchSize;
|
public int PreferredBatchSize => _divisorCyclesBatchSize;
|
||||||
|
|
||||||
|
// private static readonly Accelerator[] _accelerators;
|
||||||
|
|
||||||
private DivisorCycleCache(int divisorCyclesBatchSize)
|
private DivisorCycleCache(int divisorCyclesBatchSize)
|
||||||
{
|
{
|
||||||
_divisorCyclesBatchSize = Math.Max(1, divisorCyclesBatchSize);
|
_divisorCyclesBatchSize = Math.Max(1, divisorCyclesBatchSize);
|
||||||
|
var acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
|
_acceleratorIndex = acceleratorIndex;
|
||||||
|
Accelerator accelerator = AcceleratorPool.Shared.Accelerators[acceleratorIndex];
|
||||||
|
_accelerator = accelerator;
|
||||||
|
_gpuKernel = LoadKernel(accelerator);
|
||||||
_snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
_snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +84,6 @@ public sealed class DivisorCycleCache
|
|||||||
throw new InvalidDataException($"Divisor cycle is missing for {divisor}");
|
throw new InvalidDataException($"Divisor cycle is missing for {divisor}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObserveCycleCacheDivisor(divisor);
|
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +98,7 @@ public sealed class DivisorCycleCache
|
|||||||
int[]? rentedMissing = null;
|
int[]? rentedMissing = null;
|
||||||
Span<int> missingBuffer = length <= StackBufferThreshold
|
Span<int> missingBuffer = length <= StackBufferThreshold
|
||||||
? stackalloc int[length]
|
? stackalloc int[length]
|
||||||
: new Span<int>(rentedMissing = ArrayPool<int>.Shared.Rent(length), 0, length);
|
: new Span<int>(rentedMissing = ThreadStaticPools.IntPool.Rent(length), 0, length);
|
||||||
|
|
||||||
int missingCount = 0;
|
int missingCount = 0;
|
||||||
|
|
||||||
@@ -114,7 +118,6 @@ public sealed class DivisorCycleCache
|
|||||||
throw new InvalidDataException($"Divisor cycle is missing for {divisor}");
|
throw new InvalidDataException($"Divisor cycle is missing for {divisor}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObserveCycleCacheDivisor(divisor);
|
|
||||||
cycles[i] = cached;
|
cycles[i] = cached;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -138,7 +141,7 @@ public sealed class DivisorCycleCache
|
|||||||
|
|
||||||
if (rentedMissing is not null)
|
if (rentedMissing is not null)
|
||||||
{
|
{
|
||||||
ArrayPool<int>.Shared.Return(rentedMissing, clearArray: false);
|
ThreadStaticPools.IntPool.Return(rentedMissing, clearArray: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,12 +200,12 @@ public sealed class DivisorCycleCache
|
|||||||
ulong[]? rentedDivisors = null;
|
ulong[]? rentedDivisors = null;
|
||||||
Span<ulong> workDivisors = count <= StackBufferThreshold
|
Span<ulong> workDivisors = count <= StackBufferThreshold
|
||||||
? stackalloc ulong[count]
|
? stackalloc ulong[count]
|
||||||
: new Span<ulong>(rentedDivisors = ArrayPool<ulong>.Shared.Rent(count), 0, count);
|
: new Span<ulong>(rentedDivisors = ThreadStaticPools.UlongPool.Rent(count), 0, count);
|
||||||
|
|
||||||
ulong[]? rentedResults = null;
|
ulong[]? rentedResults = null;
|
||||||
Span<ulong> workResults = count <= StackBufferThreshold
|
Span<ulong> workResults = count <= StackBufferThreshold
|
||||||
? stackalloc ulong[count]
|
? stackalloc ulong[count]
|
||||||
: new Span<ulong>(rentedResults = ArrayPool<ulong>.Shared.Rent(count), 0, count);
|
: new Span<ulong>(rentedResults = ThreadStaticPools.UlongPool.Rent(count), 0, count);
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
@@ -218,47 +221,80 @@ public sealed class DivisorCycleCache
|
|||||||
|
|
||||||
if (rentedDivisors is not null)
|
if (rentedDivisors is not null)
|
||||||
{
|
{
|
||||||
ArrayPool<ulong>.Shared.Return(rentedDivisors, clearArray: false);
|
ThreadStaticPools.UlongPool.Return(rentedDivisors, clearArray: false);
|
||||||
ArrayPool<ulong>.Shared.Return(rentedResults!, clearArray: false);
|
ThreadStaticPools.UlongPool.Return(rentedResults!, clearArray: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const int DefaultCapacity = 128;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static MemoryBuffer1D<ulong, Stride1D.Dense>? divisorBuffer;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static MemoryBuffer1D<ulong, Stride1D.Dense>? powBuffer;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static MemoryBuffer1D<ulong, Stride1D.Dense>? orderBuffer;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static MemoryBuffer1D<ulong, Stride1D.Dense>? resultBuffer;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static MemoryBuffer1D<byte, Stride1D.Dense>? statusBuffer;
|
||||||
|
|
||||||
|
private void EnsureCapacity(int requiredCapacity)
|
||||||
|
{
|
||||||
|
if (requiredCapacity <= (divisorBuffer?.Length ?? 0))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (divisorBuffer != null)
|
||||||
|
{
|
||||||
|
divisorBuffer.Dispose();
|
||||||
|
powBuffer!.Dispose();
|
||||||
|
orderBuffer!.Dispose();
|
||||||
|
resultBuffer!.Dispose();
|
||||||
|
statusBuffer!.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Accelerator accelerator = _accelerator;
|
||||||
|
lock(accelerator)
|
||||||
|
{
|
||||||
|
divisorBuffer = accelerator.Allocate1D<ulong>(requiredCapacity);
|
||||||
|
powBuffer = accelerator.Allocate1D<ulong>(requiredCapacity);
|
||||||
|
orderBuffer = accelerator.Allocate1D<ulong>(requiredCapacity);
|
||||||
|
resultBuffer = accelerator.Allocate1D<ulong>(requiredCapacity);
|
||||||
|
statusBuffer = accelerator.Allocate1D<byte>(requiredCapacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ComputeCyclesGpuCore(ReadOnlySpan<ulong> divisors, Span<ulong> destination)
|
private void ComputeCyclesGpuCore(ReadOnlySpan<ulong> divisors, Span<ulong> destination)
|
||||||
{
|
{
|
||||||
int length = divisors.Length;
|
int length = divisors.Length;
|
||||||
var gpuLease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
|
||||||
|
|
||||||
Accelerator accelerator = gpuLease.Accelerator;
|
EnsureCapacity(length);
|
||||||
var kernel = _gpuKernelCache.GetOrAdd(accelerator, LoadKernel);
|
|
||||||
|
|
||||||
using MemoryBuffer1D<ulong, Stride1D.Dense> divisorBuffer = accelerator.Allocate1D<ulong>(length);
|
|
||||||
using MemoryBuffer1D<ulong, Stride1D.Dense> powBuffer = accelerator.Allocate1D<ulong>(length);
|
|
||||||
using MemoryBuffer1D<ulong, Stride1D.Dense> orderBuffer = accelerator.Allocate1D<ulong>(length);
|
|
||||||
using MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = accelerator.Allocate1D<ulong>(length);
|
|
||||||
using MemoryBuffer1D<byte, Stride1D.Dense> statusBuffer = accelerator.Allocate1D<byte>(length);
|
|
||||||
|
|
||||||
ulong[]? rentedPow = null;
|
ulong[]? rentedPow = null;
|
||||||
Span<ulong> powSpan = length <= StackBufferThreshold
|
Span<ulong> powSpan = length <= StackBufferThreshold
|
||||||
? stackalloc ulong[length]
|
? stackalloc ulong[length]
|
||||||
: new Span<ulong>(rentedPow = ArrayPool<ulong>.Shared.Rent(length), 0, length);
|
: new Span<ulong>(rentedPow = ThreadStaticPools.UlongPool.Rent(length), 0, length);
|
||||||
|
|
||||||
ulong[]? rentedOrder = null;
|
ulong[]? rentedOrder = null;
|
||||||
Span<ulong> orderSpan = length <= StackBufferThreshold
|
Span<ulong> orderSpan = length <= StackBufferThreshold
|
||||||
? stackalloc ulong[length]
|
? stackalloc ulong[length]
|
||||||
: new Span<ulong>(rentedOrder = ArrayPool<ulong>.Shared.Rent(length), 0, length);
|
: new Span<ulong>(rentedOrder = ThreadStaticPools.UlongPool.Rent(length), 0, length);
|
||||||
|
|
||||||
ulong[]? rentedResult = null;
|
ulong[]? rentedResult = null;
|
||||||
Span<ulong> resultSpan = length <= StackBufferThreshold
|
Span<ulong> resultSpan = length <= StackBufferThreshold
|
||||||
? stackalloc ulong[length]
|
? stackalloc ulong[length]
|
||||||
: new Span<ulong>(rentedResult = ArrayPool<ulong>.Shared.Rent(length), 0, length);
|
: new Span<ulong>(rentedResult = ThreadStaticPools.UlongPool.Rent(length), 0, length);
|
||||||
|
|
||||||
byte[]? rentedStatus = null;
|
byte[]? rentedStatus = null;
|
||||||
Span<byte> statusSpan = length <= StackBufferThreshold
|
Span<byte> statusSpan = length <= StackBufferThreshold
|
||||||
? stackalloc byte[length]
|
? stackalloc byte[length]
|
||||||
: new Span<byte>(rentedStatus = ArrayPool<byte>.Shared.Rent(length), 0, length);
|
: new Span<byte>(rentedStatus = ThreadStaticPools.BytePool.Rent(length), 0, length);
|
||||||
|
|
||||||
ref ulong divisorRef = ref MemoryMarshal.GetReference(divisors);
|
|
||||||
divisorBuffer.View.CopyFromCPU(ref divisorRef, length);
|
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
@@ -275,18 +311,25 @@ public sealed class DivisorCycleCache
|
|||||||
statusSpan[i] = ByteZero;
|
statusSpan[i] = ByteZero;
|
||||||
}
|
}
|
||||||
|
|
||||||
powBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(powSpan), length);
|
// GpuPrimeWorkLimiter.Acquire();
|
||||||
orderBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(orderSpan), length);
|
|
||||||
resultBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(resultSpan), length);
|
|
||||||
statusBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(statusSpan), length);
|
|
||||||
|
|
||||||
int pending;
|
int pending;
|
||||||
|
var kernel = _gpuKernel;
|
||||||
|
var acceleratorIndex = _acceleratorIndex;
|
||||||
|
var stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
|
|
||||||
|
divisorBuffer!.View.CopyFromCPU(stream, divisors);
|
||||||
|
powBuffer!.View.CopyFromCPU(stream, powSpan);
|
||||||
|
orderBuffer!.View.CopyFromCPU(stream, orderSpan);
|
||||||
|
resultBuffer!.View.CopyFromCPU(stream, resultSpan);
|
||||||
|
statusBuffer!.View.CopyFromCPU(stream, statusSpan);
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
kernel(length, _divisorCyclesBatchSize, divisorBuffer.View, powBuffer.View, orderBuffer.View, resultBuffer.View, statusBuffer.View);
|
kernel(stream, length, _divisorCyclesBatchSize, divisorBuffer.View, powBuffer.View, orderBuffer.View, resultBuffer.View, statusBuffer.View);
|
||||||
accelerator.Synchronize();
|
|
||||||
|
statusBuffer.View.CopyToCPU(stream, statusSpan);
|
||||||
|
stream.Synchronize();
|
||||||
|
|
||||||
statusBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(statusSpan), length);
|
|
||||||
pending = 0;
|
pending = 0;
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
@@ -298,22 +341,27 @@ public sealed class DivisorCycleCache
|
|||||||
}
|
}
|
||||||
while (pending > 0);
|
while (pending > 0);
|
||||||
|
|
||||||
resultBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(resultSpan), length);
|
resultBuffer.View.CopyToCPU(stream, resultSpan);
|
||||||
|
stream.Synchronize();
|
||||||
|
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||||
|
|
||||||
resultSpan.CopyTo(destination);
|
resultSpan.CopyTo(destination);
|
||||||
if (rentedPow is not null)
|
if (rentedPow is not null)
|
||||||
{
|
{
|
||||||
ArrayPool<ulong>.Shared.Return(rentedPow, clearArray: false);
|
ThreadStaticPools.UlongPool.Return(rentedPow, clearArray: false);
|
||||||
ArrayPool<ulong>.Shared.Return(rentedOrder!, clearArray: false);
|
ThreadStaticPools.UlongPool.Return(rentedOrder!, clearArray: false);
|
||||||
ArrayPool<ulong>.Shared.Return(rentedResult!, clearArray: false);
|
ThreadStaticPools.UlongPool.Return(rentedResult!, clearArray: false);
|
||||||
ArrayPool<byte>.Shared.Return(rentedStatus!, clearArray: false);
|
ThreadStaticPools.BytePool.Return(rentedStatus!, clearArray: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
gpuLease.Dispose();
|
// GpuPrimeWorkLimiter.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Action<Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>> LoadKernel(Accelerator accelerator)
|
private static Action<AcceleratorStream, Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>> LoadKernel(Accelerator accelerator)
|
||||||
{
|
{
|
||||||
return accelerator.LoadAutoGroupedStreamKernel<Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>(DivisorCycleKernels.GpuAdvanceDivisorCyclesKernel);
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>(DivisorCycleKernels.GpuAdvanceDivisorCyclesKernel);
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class AcceleratorReferenceComparer : IEqualityComparer<Accelerator>
|
private sealed class AcceleratorReferenceComparer : IEqualityComparer<Accelerator>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core;
|
namespace PerfectNumbers.Core;
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
using ILGPU;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu
|
||||||
|
{
|
||||||
|
public struct AcceleratorPool
|
||||||
|
{
|
||||||
|
internal readonly Context Context;
|
||||||
|
internal readonly Device Device;
|
||||||
|
internal readonly Accelerator[] Accelerators;
|
||||||
|
|
||||||
|
private int _index;
|
||||||
|
|
||||||
|
public int Rent()
|
||||||
|
{
|
||||||
|
Atomic.CompareExchange(ref _index, Capacity, 0);
|
||||||
|
var index = Atomic.Add(ref _index, 1);
|
||||||
|
if (index >= Capacity)
|
||||||
|
{
|
||||||
|
index -= Capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Return(int acceleratorIndex)
|
||||||
|
{
|
||||||
|
// Intentionally left empty - there's nothing to do here. We're not really renting anything.
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly AcceleratorPool Shared = new(PerfectNumberConstants.RollingAccelerators);
|
||||||
|
|
||||||
|
private readonly int Capacity;
|
||||||
|
|
||||||
|
public AcceleratorPool(int capacity)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Creating accelerators");
|
||||||
|
Capacity = capacity;
|
||||||
|
var context = Context.CreateDefault();
|
||||||
|
var device = context.GetPreferredDevice(false);
|
||||||
|
Context = context;
|
||||||
|
Device = device;
|
||||||
|
int acceleratorsInContext = 0;
|
||||||
|
int acceleratorIndex = 0;
|
||||||
|
Accelerators = [.. Enumerable.Range(0, capacity).Select(_ =>
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Preparing accelerator {acceleratorIndex++}");
|
||||||
|
Accelerator accelerator = device.CreateAccelerator(context);
|
||||||
|
acceleratorsInContext++;
|
||||||
|
if (acceleratorsInContext >= 128)
|
||||||
|
{
|
||||||
|
context = Context.CreateDefault();
|
||||||
|
device = context.GetPreferredDevice(false);
|
||||||
|
acceleratorsInContext = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return accelerator;
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
|
internal sealed class AcceleratorReferenceComparer : IEqualityComparer<Accelerator>
|
||||||
|
{
|
||||||
|
internal static AcceleratorReferenceComparer Instance { get; } = new();
|
||||||
|
|
||||||
|
public bool Equals(Accelerator? x, Accelerator? y) => ReferenceEquals(x, y);
|
||||||
|
|
||||||
|
public int GetHashCode(Accelerator obj) => RuntimeHelpers.GetHashCode(obj);
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using ILGPU;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
|
public static class AcceleratorExtensions
|
||||||
|
{
|
||||||
|
public static MemoryBuffer1D<ulong, Stride1D.Dense> CopySpanToDevice(this Accelerator accelerator, AcceleratorStream stream, in ulong[] span)
|
||||||
|
{
|
||||||
|
MemoryBuffer1D<ulong, Stride1D.Dense>? buffer;
|
||||||
|
buffer = accelerator.Allocate1D(stream, span);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MemoryBuffer1D<ulong, Stride1D.Dense> CopyUintSpanToDevice(this Accelerator accelerator, AcceleratorStream stream, ReadOnlySpan<uint> span)
|
||||||
|
{
|
||||||
|
MemoryBuffer1D<ulong, Stride1D.Dense>? buffer;
|
||||||
|
buffer = accelerator.Allocate1D<ulong>(span.Length);
|
||||||
|
|
||||||
|
if (!span.IsEmpty)
|
||||||
|
{
|
||||||
|
var converted = new ulong[span.Length];
|
||||||
|
for (int i = 0; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
converted[i] = span[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.View.CopyFromCPU(stream, converted);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
|
public static class AcceleratorStreamPool
|
||||||
|
{
|
||||||
|
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
private static readonly SemaphoreSlim[] _locks = new SemaphoreSlim[PerfectNumberConstants.RollingAccelerators];
|
||||||
|
private static readonly ConcurrentQueue<AcceleratorStream>[] _streams = new ConcurrentQueue<AcceleratorStream> [PerfectNumberConstants.RollingAccelerators];
|
||||||
|
|
||||||
|
private static SemaphoreSlim CreateLock() => new(PerfectNumberConstants.ThreadsByAccelerator);
|
||||||
|
|
||||||
|
|
||||||
|
// private static int _rented;
|
||||||
|
|
||||||
|
public static AcceleratorStream Rent(int acceleratorIndex)
|
||||||
|
{
|
||||||
|
var streamLock = _locks[acceleratorIndex];
|
||||||
|
var queue = _streams[acceleratorIndex];
|
||||||
|
|
||||||
|
streamLock.Wait();
|
||||||
|
|
||||||
|
return queue.TryDequeue(out var stream)
|
||||||
|
? stream
|
||||||
|
: _accelerators[acceleratorIndex].CreateStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Return(int acceleratorIndex, AcceleratorStream stream)
|
||||||
|
{
|
||||||
|
var streamLock = _locks[acceleratorIndex];
|
||||||
|
var queue = _streams[acceleratorIndex];
|
||||||
|
queue.Enqueue(stream);
|
||||||
|
streamLock.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WarmUp(int acceleratorIndex)
|
||||||
|
{
|
||||||
|
_streams[acceleratorIndex] = new();
|
||||||
|
_locks[acceleratorIndex] = CreateLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
using ILGPU;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
|
internal readonly struct HeuristicCombinedGpuTables
|
||||||
|
{
|
||||||
|
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
|
||||||
|
private static readonly HeuristicCombinedGpuTables[] _sharedTables = new HeuristicCombinedGpuTables[PerfectNumberConstants.RollingAccelerators];
|
||||||
|
|
||||||
|
internal static void WarmUp(int acceleratorIndex, AcceleratorStream stream) => _sharedTables[acceleratorIndex] = new(_accelerators[acceleratorIndex], stream);
|
||||||
|
|
||||||
|
internal static HeuristicCombinedGpuTables GetStaticTables(int acceleratorIndex) => _sharedTables[acceleratorIndex];
|
||||||
|
|
||||||
|
internal HeuristicCombinedGpuTables(Accelerator accelerator, AcceleratorStream stream)
|
||||||
|
{
|
||||||
|
var combinedEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1;
|
||||||
|
HeuristicCombinedDivisorsEnding1 = accelerator.CopySpanToDevice(stream, combinedEnding1);
|
||||||
|
var combinedSquaresEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1Squares;
|
||||||
|
HeuristicCombinedDivisorSquaresEnding1 = accelerator.CopySpanToDevice(stream, combinedSquaresEnding1);
|
||||||
|
|
||||||
|
var combinedEnding3 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding3;
|
||||||
|
HeuristicCombinedDivisorsEnding3 = accelerator.CopySpanToDevice(stream, combinedEnding3);
|
||||||
|
var combinedSquaresEnding3 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding3Squares;
|
||||||
|
HeuristicCombinedDivisorSquaresEnding3 = accelerator.CopySpanToDevice(stream, combinedSquaresEnding3);
|
||||||
|
|
||||||
|
var combinedEnding7 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding7;
|
||||||
|
HeuristicCombinedDivisorsEnding7 = accelerator.CopySpanToDevice(stream, combinedEnding7);
|
||||||
|
var combinedSquaresEnding7 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding7Squares;
|
||||||
|
HeuristicCombinedDivisorSquaresEnding7 = accelerator.CopySpanToDevice(stream, combinedSquaresEnding7);
|
||||||
|
|
||||||
|
var combinedEnding9 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding9;
|
||||||
|
HeuristicCombinedDivisorsEnding9 = accelerator.CopySpanToDevice(stream, combinedEnding9);
|
||||||
|
var combinedSquaresEnding9 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding9Squares;
|
||||||
|
HeuristicCombinedDivisorSquaresEnding9 = accelerator.CopySpanToDevice(stream, combinedSquaresEnding9);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding1;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding1;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding3;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding3;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding7;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding7;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorsEnding9;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicCombinedDivisorSquaresEnding9;
|
||||||
|
|
||||||
|
internal HeuristicCombinedGpuViews CreateViews() => new(
|
||||||
|
HeuristicCombinedDivisorsEnding1.View,
|
||||||
|
HeuristicCombinedDivisorsEnding3.View,
|
||||||
|
HeuristicCombinedDivisorsEnding7.View,
|
||||||
|
HeuristicCombinedDivisorsEnding9.View,
|
||||||
|
HeuristicCombinedDivisorSquaresEnding1.View,
|
||||||
|
HeuristicCombinedDivisorSquaresEnding3.View,
|
||||||
|
HeuristicCombinedDivisorSquaresEnding7.View,
|
||||||
|
HeuristicCombinedDivisorSquaresEnding9.View);
|
||||||
|
|
||||||
|
internal void Dispose()
|
||||||
|
{
|
||||||
|
HeuristicCombinedDivisorsEnding1.Dispose();
|
||||||
|
HeuristicCombinedDivisorSquaresEnding1.Dispose();
|
||||||
|
HeuristicCombinedDivisorsEnding3.Dispose();
|
||||||
|
HeuristicCombinedDivisorSquaresEnding3.Dispose();
|
||||||
|
HeuristicCombinedDivisorsEnding7.Dispose();
|
||||||
|
HeuristicCombinedDivisorSquaresEnding7.Dispose();
|
||||||
|
HeuristicCombinedDivisorsEnding9.Dispose();
|
||||||
|
HeuristicCombinedDivisorSquaresEnding9.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using ILGPU;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
|
public readonly struct HeuristicCombinedGpuViews(
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding1,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding3,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding7,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding9,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding1,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding3,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding7,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding9)
|
||||||
|
{
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding1 = combinedDivisorsEnding1;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding3 = combinedDivisorsEnding3;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding7 = combinedDivisorsEnding7;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding9 = combinedDivisorsEnding9;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding1 = combinedDivisorSquaresEnding1;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding3 = combinedDivisorSquaresEnding3;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding7 = combinedDivisorSquaresEnding7;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding9 = combinedDivisorSquaresEnding9;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ArrayView1D<ulong, Stride1D.Dense> SelectDivisors(byte ending) => ending switch
|
||||||
|
{
|
||||||
|
1 => CombinedDivisorsEnding1,
|
||||||
|
3 => CombinedDivisorsEnding3,
|
||||||
|
7 => CombinedDivisorsEnding7,
|
||||||
|
9 => CombinedDivisorsEnding9,
|
||||||
|
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
|
||||||
|
};
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ArrayView1D<ulong, Stride1D.Dense> SelectDivisorSquares(byte ending) => ending switch
|
||||||
|
{
|
||||||
|
1 => CombinedDivisorSquaresEnding1,
|
||||||
|
3 => CombinedDivisorSquaresEnding3,
|
||||||
|
7 => CombinedDivisorSquaresEnding7,
|
||||||
|
9 => CombinedDivisorSquaresEnding9,
|
||||||
|
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
using ILGPU;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
|
internal sealed class HeuristicGroupABGpuTables
|
||||||
|
{
|
||||||
|
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
|
||||||
|
private static readonly HeuristicGroupABGpuTables[] _sharedTables = new HeuristicGroupABGpuTables[PerfectNumberConstants.RollingAccelerators];
|
||||||
|
|
||||||
|
private static readonly SemaphoreSlim[] _locks = [..Enumerable.Range(1, PerfectNumberConstants.RollingAccelerators).Select(_ => new SemaphoreSlim(1))];
|
||||||
|
|
||||||
|
internal static HeuristicGroupABGpuTables EnsureStaticTables(int acceleratorIndex, AcceleratorStream stream)
|
||||||
|
{
|
||||||
|
var @lock = _locks[acceleratorIndex];
|
||||||
|
@lock.Wait();
|
||||||
|
if (_sharedTables[acceleratorIndex] is {} existing)
|
||||||
|
{
|
||||||
|
@lock.Release();
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
|
existing = new(accelerator, stream);
|
||||||
|
_sharedTables[acceleratorIndex] = existing;
|
||||||
|
@lock.Release();
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal HeuristicGroupABGpuTables(Accelerator accelerator, AcceleratorStream stream)
|
||||||
|
{
|
||||||
|
var heuristicGroupA = HeuristicPrimeSieves.GroupADivisorsStorage;
|
||||||
|
HeuristicGroupADivisors = accelerator.CopySpanToDevice(stream, heuristicGroupA);
|
||||||
|
|
||||||
|
var heuristicGroupASquares = HeuristicPrimeSieves.GroupADivisorSquaresStorage;
|
||||||
|
HeuristicGroupADivisorSquares = accelerator.CopySpanToDevice(stream, heuristicGroupASquares);
|
||||||
|
|
||||||
|
HeuristicGroupBDivisorsEnding1 = accelerator.CopyUintSpanToDevice(stream, DivisorGenerator.SmallPrimesLastOneWithoutLastThree);
|
||||||
|
HeuristicGroupBDivisorSquaresEnding1 = accelerator.CopySpanToDevice(stream, DivisorGenerator.SmallPrimesPow2LastOneWithoutLastThree);
|
||||||
|
|
||||||
|
HeuristicGroupBDivisorsEnding7 = accelerator.CopyUintSpanToDevice(stream, DivisorGenerator.SmallPrimesLastSevenWithoutLastThree);
|
||||||
|
HeuristicGroupBDivisorSquaresEnding7 = accelerator.CopySpanToDevice(stream, DivisorGenerator.SmallPrimesPow2LastSevenWithoutLastThree);
|
||||||
|
|
||||||
|
HeuristicGroupBDivisorsEnding9 = accelerator.CopyUintSpanToDevice(stream, DivisorGenerator.SmallPrimesLastNineWithoutLastThree);
|
||||||
|
HeuristicGroupBDivisorSquaresEnding9 = accelerator.CopySpanToDevice(stream, DivisorGenerator.SmallPrimesPow2LastNineWithoutLastThree);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupADivisors;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupADivisorSquares;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding1;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding1;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding7;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding7;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding9;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding9;
|
||||||
|
|
||||||
|
internal HeuristicGroupABGpuViews CreateViews() => new(
|
||||||
|
HeuristicGroupADivisors.View,
|
||||||
|
HeuristicGroupADivisorSquares.View,
|
||||||
|
HeuristicGroupBDivisorsEnding1.View,
|
||||||
|
HeuristicGroupBDivisorSquaresEnding1.View,
|
||||||
|
HeuristicGroupBDivisorsEnding7.View,
|
||||||
|
HeuristicGroupBDivisorSquaresEnding7.View,
|
||||||
|
HeuristicGroupBDivisorsEnding9.View,
|
||||||
|
HeuristicGroupBDivisorSquaresEnding9.View);
|
||||||
|
|
||||||
|
internal void Dispose()
|
||||||
|
{
|
||||||
|
HeuristicGroupADivisors.Dispose();
|
||||||
|
HeuristicGroupADivisorSquares.Dispose();
|
||||||
|
HeuristicGroupBDivisorsEnding1.Dispose();
|
||||||
|
HeuristicGroupBDivisorSquaresEnding1.Dispose();
|
||||||
|
HeuristicGroupBDivisorsEnding7.Dispose();
|
||||||
|
HeuristicGroupBDivisorSquaresEnding7.Dispose();
|
||||||
|
HeuristicGroupBDivisorsEnding9.Dispose();
|
||||||
|
HeuristicGroupBDivisorSquaresEnding9.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using ILGPU;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
|
public readonly struct HeuristicGroupABGpuViews(
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> groupADivisors,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> groupADivisorSquares,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding1,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding1,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding7,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding7,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding9,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding9)
|
||||||
|
{
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupADivisors = groupADivisors;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupADivisorSquares = groupADivisorSquares;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding1 = groupBDivisorsEnding1;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding1 = groupBDivisorSquaresEnding1;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding7 = groupBDivisorsEnding7;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding7 = groupBDivisorSquaresEnding7;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding9 = groupBDivisorsEnding9;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding9 = groupBDivisorSquaresEnding9;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ArrayView1D<ulong, Stride1D.Dense> SelectGroupB(byte ending) => ending switch
|
||||||
|
{
|
||||||
|
1 => GroupBDivisorsEnding1,
|
||||||
|
7 => GroupBDivisorsEnding7,
|
||||||
|
9 => GroupBDivisorsEnding9,
|
||||||
|
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
|
||||||
|
};
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ArrayView1D<ulong, Stride1D.Dense> SelectGroupBSquares(byte ending) => ending switch
|
||||||
|
{
|
||||||
|
1 => GroupBDivisorSquaresEnding1,
|
||||||
|
7 => GroupBDivisorSquaresEnding7,
|
||||||
|
9 => GroupBDivisorSquaresEnding9,
|
||||||
|
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
|
||||||
|
};
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ArrayView1D<ulong, Stride1D.Dense> SelectDivisors(HeuristicGpuDivisorTableKind kind) => kind switch
|
||||||
|
{
|
||||||
|
HeuristicGpuDivisorTableKind.GroupA => GroupADivisors,
|
||||||
|
HeuristicGpuDivisorTableKind.GroupBEnding1 => GroupBDivisorsEnding1,
|
||||||
|
HeuristicGpuDivisorTableKind.GroupBEnding7 => GroupBDivisorsEnding7,
|
||||||
|
HeuristicGpuDivisorTableKind.GroupBEnding9 => GroupBDivisorsEnding9,
|
||||||
|
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
|
||||||
|
};
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ArrayView1D<ulong, Stride1D.Dense> SelectDivisorSquares(HeuristicGpuDivisorTableKind kind) => kind switch
|
||||||
|
{
|
||||||
|
HeuristicGpuDivisorTableKind.GroupA => GroupADivisorSquares,
|
||||||
|
HeuristicGpuDivisorTableKind.GroupBEnding1 => GroupBDivisorSquaresEnding1,
|
||||||
|
HeuristicGpuDivisorTableKind.GroupBEnding7 => GroupBDivisorSquaresEnding7,
|
||||||
|
HeuristicGpuDivisorTableKind.GroupBEnding9 => GroupBDivisorSquaresEnding9,
|
||||||
|
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
using ILGPU;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
|
internal sealed class HeuristicGroupABPrimeTesterAccelerator
|
||||||
|
{
|
||||||
|
#region Pool
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Queue<HeuristicGroupABPrimeTesterAccelerator>? Pool;
|
||||||
|
|
||||||
|
internal static void WarmUp()
|
||||||
|
{
|
||||||
|
Accelerator[]? accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
int acceleratorCount = accelerators.Length;
|
||||||
|
for (var i = 0; i < acceleratorCount; i++)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Preparing heuristic accelerator {i}...");
|
||||||
|
var accelerator = accelerators[i];
|
||||||
|
AcceleratorStream stream = accelerator.CreateStream();
|
||||||
|
_ = HeuristicGroupABGpuTables.EnsureStaticTables(i, stream);
|
||||||
|
// _ = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.None);
|
||||||
|
// KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream);
|
||||||
|
// GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream);
|
||||||
|
stream.Synchronize();
|
||||||
|
stream.Dispose();
|
||||||
|
Console.WriteLine($"Heuristic accelerator {i} is ready");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static HeuristicGroupABPrimeTesterAccelerator Rent(int minBufferCapacity = 1)
|
||||||
|
{
|
||||||
|
var pool = Pool ??= new();
|
||||||
|
if (!pool.TryDequeue(out var lease))
|
||||||
|
{
|
||||||
|
lease = new HeuristicGroupABPrimeTesterAccelerator(minBufferCapacity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lease.EnsureCapacity(minBufferCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lease;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void Return(HeuristicGroupABPrimeTesterAccelerator lease)
|
||||||
|
{
|
||||||
|
var pool = Pool ??= new();
|
||||||
|
pool.Enqueue(lease);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void DisposeAll()
|
||||||
|
{
|
||||||
|
if (Pool != null)
|
||||||
|
{
|
||||||
|
while (Pool.TryDequeue(out var lease))
|
||||||
|
{
|
||||||
|
lease.DisposeResources();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void Clear()
|
||||||
|
{
|
||||||
|
if (Pool != null)
|
||||||
|
{
|
||||||
|
var retained = new List<HeuristicGroupABPrimeTesterAccelerator>();
|
||||||
|
while (Pool.TryDequeue(out var lease))
|
||||||
|
{
|
||||||
|
lease.DisposeResources();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public readonly HeuristicGroupABGpuViews DivisorTables;
|
||||||
|
internal readonly Context Context;
|
||||||
|
public readonly Accelerator Accelerator;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
public static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>? SmallPrimeSieveKernel;
|
||||||
|
|
||||||
|
internal static Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> GetSmallPrimeSieveKernel(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
var pool = SmallPrimeSieveKernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
cached = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>();
|
||||||
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly Kernel HeuristicTrialDivisionKernel;
|
||||||
|
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense>? Input = null;
|
||||||
|
public MemoryBuffer1D<byte, Stride1D.Dense>? Output = null;
|
||||||
|
public MemoryBuffer1D<int, Stride1D.Dense>? HeuristicFlag = null!;
|
||||||
|
public int BufferCapacity;
|
||||||
|
|
||||||
|
private HeuristicGroupABGpuTables _sharedTables;
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupADivisors => _sharedTables.HeuristicGroupADivisors;
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupADivisorSquares => _sharedTables.HeuristicGroupADivisorSquares;
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding1 => _sharedTables.HeuristicGroupBDivisorsEnding1;
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding1 => _sharedTables.HeuristicGroupBDivisorSquaresEnding1;
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding7 => _sharedTables.HeuristicGroupBDivisorsEnding7;
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding7 => _sharedTables.HeuristicGroupBDivisorSquaresEnding7;
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorsEnding9 => _sharedTables.HeuristicGroupBDivisorsEnding9;
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense> HeuristicGroupBDivisorSquaresEnding9 => _sharedTables.HeuristicGroupBDivisorSquaresEnding9;
|
||||||
|
|
||||||
|
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
internal readonly int AcceleratorIndex;
|
||||||
|
|
||||||
|
internal HeuristicGroupABPrimeTesterAccelerator(int minBufferCapacity)
|
||||||
|
{
|
||||||
|
var acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
|
AcceleratorIndex = acceleratorIndex;
|
||||||
|
Accelerator accelerator = _accelerators[acceleratorIndex];
|
||||||
|
Context = accelerator.Context;
|
||||||
|
|
||||||
|
Accelerator = accelerator;
|
||||||
|
EnsureCapacity(minBufferCapacity);
|
||||||
|
|
||||||
|
HeuristicTrialDivisionKernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<int>, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGroupABGpuViews>(PrimeTesterKernels.HeuristicTrialGroupABDivisionKernel));
|
||||||
|
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
|
|
||||||
|
// GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream);
|
||||||
|
|
||||||
|
_sharedTables = HeuristicGroupABGpuTables.EnsureStaticTables(acceleratorIndex, stream);
|
||||||
|
DivisorTables = _sharedTables.CreateViews();
|
||||||
|
|
||||||
|
stream.Synchronize();
|
||||||
|
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnsureCapacity(int minCapacity)
|
||||||
|
{
|
||||||
|
if (minCapacity <= BufferCapacity)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Input?.Dispose();
|
||||||
|
Output?.Dispose();
|
||||||
|
HeuristicFlag?.Dispose();
|
||||||
|
|
||||||
|
Input = Accelerator.Allocate1D<ulong>(minCapacity);
|
||||||
|
Output = Accelerator.Allocate1D<byte>(minCapacity);
|
||||||
|
HeuristicFlag = Accelerator.Allocate1D<int>(minCapacity);
|
||||||
|
|
||||||
|
BufferCapacity = minCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Return(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void DisposeResources()
|
||||||
|
{
|
||||||
|
Input?.Dispose();
|
||||||
|
Output?.Dispose();
|
||||||
|
HeuristicFlag?.Dispose();
|
||||||
|
// ReleaseSharedTables(_sharedTables);
|
||||||
|
// These resources are shared between GPU leases
|
||||||
|
// Accelerator.Dispose();
|
||||||
|
// AcceleratorContext.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using ILGPU;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
|
internal readonly struct LastDigitGpuTables
|
||||||
|
{
|
||||||
|
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
private static readonly LastDigitGpuTables[] _sharedTables = new LastDigitGpuTables[PerfectNumberConstants.RollingAccelerators];
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static LastDigitGpuTables GetStaticTables(int acceleratorIndex) => _sharedTables[acceleratorIndex];
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static void WarmUp(int acceleratorIndex, AcceleratorStream stream) => _sharedTables[acceleratorIndex] = new LastDigitGpuTables(_accelerators[acceleratorIndex], stream);
|
||||||
|
|
||||||
|
internal LastDigitGpuTables(Accelerator accelerator, AcceleratorStream stream)
|
||||||
|
{
|
||||||
|
var primesLastOne = DivisorGenerator.SmallPrimesLastOne;
|
||||||
|
var primesLastSeven = DivisorGenerator.SmallPrimesLastSeven;
|
||||||
|
var primesLastThree = DivisorGenerator.SmallPrimesLastThree;
|
||||||
|
var primesLastNine = DivisorGenerator.SmallPrimesLastNine;
|
||||||
|
var primesPow2LastOne = DivisorGenerator.SmallPrimesPow2LastOne;
|
||||||
|
var primesPow2LastSeven = DivisorGenerator.SmallPrimesPow2LastSeven;
|
||||||
|
var primesPow2LastThree = DivisorGenerator.SmallPrimesPow2LastThree;
|
||||||
|
var primesPow2LastNine = DivisorGenerator.SmallPrimesPow2LastNine;
|
||||||
|
DevicePrimesLastOne = accelerator.Allocate1D(stream, primesLastOne);
|
||||||
|
DevicePrimesLastSeven = accelerator.Allocate1D(stream, primesLastSeven);
|
||||||
|
DevicePrimesLastThree = accelerator.Allocate1D(stream, primesLastThree);
|
||||||
|
DevicePrimesLastNine = accelerator.Allocate1D(stream, primesLastNine);
|
||||||
|
DevicePrimesPow2LastOne = accelerator.Allocate1D(stream, primesPow2LastOne);
|
||||||
|
DevicePrimesPow2LastSeven = accelerator.Allocate1D(stream, primesPow2LastSeven);
|
||||||
|
DevicePrimesPow2LastThree = accelerator.Allocate1D(stream, primesPow2LastThree);
|
||||||
|
DevicePrimesPow2LastNine = accelerator.Allocate1D(stream, primesPow2LastNine);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal readonly MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastOne;
|
||||||
|
internal readonly MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastSeven;
|
||||||
|
internal readonly MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastThree;
|
||||||
|
internal readonly MemoryBuffer1D<uint, Stride1D.Dense> DevicePrimesLastNine;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastOne;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastSeven;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastThree;
|
||||||
|
internal readonly MemoryBuffer1D<ulong, Stride1D.Dense> DevicePrimesPow2LastNine;
|
||||||
|
|
||||||
|
internal void Dispose()
|
||||||
|
{
|
||||||
|
DevicePrimesLastOne.Dispose();
|
||||||
|
DevicePrimesLastSeven.Dispose();
|
||||||
|
DevicePrimesLastThree.Dispose();
|
||||||
|
DevicePrimesLastNine.Dispose();
|
||||||
|
DevicePrimesPow2LastOne.Dispose();
|
||||||
|
DevicePrimesPow2LastSeven.Dispose();
|
||||||
|
DevicePrimesPow2LastThree.Dispose();
|
||||||
|
DevicePrimesPow2LastNine.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,300 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using ILGPU;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
using PerfectNumbers.Core.Gpu.Kernels;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
|
public sealed class PrimeOrderCalculatorAccelerator
|
||||||
|
{
|
||||||
|
#region Pool
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Queue<PrimeOrderCalculatorAccelerator>? _pool;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static PrimeOrderCalculatorAccelerator Rent(int primeTesterCapacity)
|
||||||
|
{
|
||||||
|
var pool = _pool ??= [];
|
||||||
|
if (!pool.TryDequeue(out var gpu))
|
||||||
|
{
|
||||||
|
int acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
|
gpu = new(acceleratorIndex, PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity, PerfectNumberConstants.DefaultSmallPrimeFactorSlotCount, PerfectNumberConstants.DefaultSpecialMaxFactorCapacity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gpu.EnsureCapacity(PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return gpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void Return(PrimeOrderCalculatorAccelerator gpu) => _pool!.Enqueue(gpu);
|
||||||
|
|
||||||
|
internal static void Clear()
|
||||||
|
{
|
||||||
|
Queue<PrimeOrderCalculatorAccelerator>? pool = _pool;
|
||||||
|
if (pool != null)
|
||||||
|
{
|
||||||
|
while (pool.TryDequeue(out var gpu))
|
||||||
|
{
|
||||||
|
gpu.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void DisposeAll()
|
||||||
|
{
|
||||||
|
Queue<PrimeOrderCalculatorAccelerator>? pool = _pool;
|
||||||
|
if (pool != null)
|
||||||
|
{
|
||||||
|
while (pool.TryDequeue(out var gpu))
|
||||||
|
{
|
||||||
|
gpu.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void WarmUp()
|
||||||
|
{
|
||||||
|
Accelerator[] accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
int acceleratorCount = accelerators.Length;
|
||||||
|
for (var i = 0; i < acceleratorCount; i++)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Preparing accelerator {i}...");
|
||||||
|
var accelerator = accelerators[i];
|
||||||
|
AcceleratorStreamPool.WarmUp(i);
|
||||||
|
// Don't take this from the pool as quick uploads of data to the accelerator consumes much of GPU's memory and throws.
|
||||||
|
AcceleratorStream stream = accelerator.CreateStream();
|
||||||
|
// TODO: Review which tables are needed when final execution path is defined.
|
||||||
|
LastDigitGpuTables.WarmUp(i, stream);
|
||||||
|
HeuristicCombinedGpuTables.WarmUp(i, stream);
|
||||||
|
// SmallPrimeFactorGpuTables.WarmUp(i, stream);
|
||||||
|
// SharedHeuristicGpuTables.EnsureStaticTables(accelerator, stream);
|
||||||
|
// _ = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.None);
|
||||||
|
// KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream);
|
||||||
|
// GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream);
|
||||||
|
stream.Synchronize();
|
||||||
|
stream.Dispose();
|
||||||
|
accelerator.Synchronize();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
#region Static Tables
|
||||||
|
|
||||||
|
public readonly ArrayView1D<uint, Stride1D.Dense> DevicePrimesLastOne;
|
||||||
|
public readonly ArrayView1D<uint, Stride1D.Dense> DevicePrimesLastSeven;
|
||||||
|
public readonly ArrayView1D<uint, Stride1D.Dense> DevicePrimesLastThree;
|
||||||
|
public readonly ArrayView1D<uint, Stride1D.Dense> DevicePrimesLastNine;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> DevicePrimesPow2LastOne;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> DevicePrimesPow2LastSeven;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> DevicePrimesPow2LastThree;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> DevicePrimesPow2LastNine;
|
||||||
|
|
||||||
|
public readonly HeuristicCombinedGpuViews DivisorTables;
|
||||||
|
|
||||||
|
public readonly ArrayView1D<uint, Stride1D.Dense> SmallPrimeFactorPrimes;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> SmallPrimeFactorSquares;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
|
||||||
|
public readonly Accelerator Accelerator;
|
||||||
|
public readonly int AcceleratorIndex;
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense> Input;
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense> OutputUlong;
|
||||||
|
public MemoryBuffer1D<byte, Stride1D.Dense> OutputByte;
|
||||||
|
public MemoryBuffer1D<int, Stride1D.Dense> OutputInt;
|
||||||
|
public MemoryBuffer1D<KeyValuePair<ulong, int>, Stride1D.Dense> Pow2ModEntriesToTestOnDevice;
|
||||||
|
public readonly MemoryBuffer1D<int, Stride1D.Dense> SmallPrimeFactorCountSlot;
|
||||||
|
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> SpecialMaxResult;
|
||||||
|
|
||||||
|
public Dictionary<ulong, int> Pow2ModEntriesToTestOnHost = [];
|
||||||
|
|
||||||
|
public readonly Action<AcceleratorStream, int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>> CheckFactorsKernelLauncher;
|
||||||
|
|
||||||
|
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>> ConvertToStandardKernelLauncher;
|
||||||
|
|
||||||
|
public readonly Action<AcceleratorStream, Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews> HeuristicCombinedTrialDivisionKernelLauncher;
|
||||||
|
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>> KeepMontgomeryKernelLauncher;
|
||||||
|
public readonly Action<AcceleratorStream, ulong, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> PollardRhoKernelLauncher;
|
||||||
|
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>> Pow2ModWideKernelLauncher;
|
||||||
|
public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<byte>> SharesFactorKernelLauncher;
|
||||||
|
public readonly Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SmallPrimeFactorKernelLauncher;
|
||||||
|
public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> SmallPrimeSieveKernelLauncher;
|
||||||
|
public readonly Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SpecialMaxKernelLauncher;
|
||||||
|
|
||||||
|
public void EnsureCapacity(int factorsCount, int primeTesterCapacity)
|
||||||
|
{
|
||||||
|
Accelerator accelerator = Accelerator;
|
||||||
|
|
||||||
|
MemoryBuffer1D<KeyValuePair<ulong, int>, Stride1D.Dense> pow2ModEntriesToTestOnDevice = Pow2ModEntriesToTestOnDevice;
|
||||||
|
if (pow2ModEntriesToTestOnDevice.Length < factorsCount)
|
||||||
|
{
|
||||||
|
lock (accelerator)
|
||||||
|
{
|
||||||
|
pow2ModEntriesToTestOnDevice.Dispose();
|
||||||
|
Pow2ModEntriesToTestOnDevice = accelerator.Allocate1D<KeyValuePair<ulong, int>>(factorsCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryBuffer1D<ulong, Stride1D.Dense> input = Input;
|
||||||
|
if (input.Length < primeTesterCapacity)
|
||||||
|
{
|
||||||
|
lock (accelerator)
|
||||||
|
{
|
||||||
|
input.Dispose();
|
||||||
|
Input = accelerator.Allocate1D<ulong>(primeTesterCapacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryBuffer1D<byte, Stride1D.Dense> outputByte = OutputByte;
|
||||||
|
if (outputByte.Length < primeTesterCapacity)
|
||||||
|
{
|
||||||
|
lock (accelerator)
|
||||||
|
{
|
||||||
|
outputByte.Dispose();
|
||||||
|
OutputByte = accelerator.Allocate1D<byte>(primeTesterCapacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryBuffer1D<int, Stride1D.Dense> outputInt = OutputInt;
|
||||||
|
if (outputInt.Length < primeTesterCapacity)
|
||||||
|
{
|
||||||
|
lock (accelerator)
|
||||||
|
{
|
||||||
|
outputInt.Dispose();
|
||||||
|
OutputInt = accelerator.Allocate1D<int>(primeTesterCapacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnsureSmallPrimeFactorSlotsCapacity(int newSize)
|
||||||
|
{
|
||||||
|
// Console.WriteLine($"Resizing GPU scratch buffer from pool ({buffer.SmallPrimeFactorPrimeSlots.Length} / {smallPrimeFactorSlotCount}), ({buffer.SpecialMaxFactors.Length}/{specialMaxFactorCapacity})");
|
||||||
|
var accelerator = Accelerator;
|
||||||
|
MemoryBuffer1D<ulong, Stride1D.Dense> outputUlong = OutputUlong;
|
||||||
|
if (outputUlong.Length < newSize)
|
||||||
|
{
|
||||||
|
lock (accelerator)
|
||||||
|
{
|
||||||
|
outputUlong.Dispose();
|
||||||
|
OutputUlong = accelerator.Allocate1D<ulong>(newSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryBuffer1D<int, Stride1D.Dense> outputInt = OutputInt;
|
||||||
|
if (outputInt.Length < newSize)
|
||||||
|
{
|
||||||
|
lock (accelerator)
|
||||||
|
{
|
||||||
|
outputInt.Dispose();
|
||||||
|
OutputInt = accelerator.Allocate1D<int>(newSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnsureSpecialMaxFactorsCapacity(int newSize)
|
||||||
|
{
|
||||||
|
var accelerator = Accelerator;
|
||||||
|
MemoryBuffer1D<ulong, Stride1D.Dense> input = Input;
|
||||||
|
if (input.Length < newSize)
|
||||||
|
{
|
||||||
|
lock (accelerator)
|
||||||
|
{
|
||||||
|
input.Dispose();
|
||||||
|
Input = accelerator.Allocate1D<ulong>(newSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryBuffer1D<ulong, Stride1D.Dense> outputUlong = OutputUlong;
|
||||||
|
if (outputUlong.Length < newSize)
|
||||||
|
{
|
||||||
|
lock (accelerator)
|
||||||
|
{
|
||||||
|
outputUlong.Dispose();
|
||||||
|
OutputUlong = accelerator.Allocate1D<ulong>(newSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrimeOrderCalculatorAccelerator(int acceleratorIndex, int factorsCount, int primeTesterCapacity, int smallPrimeFactorSlotCount, int specialMaxFactorCapacity)
|
||||||
|
{
|
||||||
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
|
Accelerator = accelerator;
|
||||||
|
AcceleratorIndex = acceleratorIndex;
|
||||||
|
lock (accelerator)
|
||||||
|
{
|
||||||
|
Input = accelerator.Allocate1D<ulong>(Math.Max(specialMaxFactorCapacity, primeTesterCapacity));
|
||||||
|
OutputByte = Accelerator.Allocate1D<byte>(primeTesterCapacity);
|
||||||
|
OutputInt = Accelerator.Allocate1D<int>(Math.Max(primeTesterCapacity, smallPrimeFactorSlotCount));
|
||||||
|
OutputUlong = accelerator.Allocate1D<ulong>(Math.Max(specialMaxFactorCapacity, smallPrimeFactorSlotCount));
|
||||||
|
Pow2ModEntriesToTestOnDevice = accelerator.Allocate1D<KeyValuePair<ulong, int>>(factorsCount);
|
||||||
|
SmallPrimeFactorCountSlot = accelerator.Allocate1D<int>(1);
|
||||||
|
SpecialMaxResult = accelerator.Allocate1D<ulong>(1);
|
||||||
|
|
||||||
|
LastDigitGpuTables lastDigitSharedTables = LastDigitGpuTables.GetStaticTables(acceleratorIndex);
|
||||||
|
DevicePrimesLastOne = lastDigitSharedTables.DevicePrimesLastOne.View;
|
||||||
|
DevicePrimesLastSeven = lastDigitSharedTables.DevicePrimesLastSeven.View;
|
||||||
|
DevicePrimesLastThree = lastDigitSharedTables.DevicePrimesLastThree.View;
|
||||||
|
DevicePrimesLastNine = lastDigitSharedTables.DevicePrimesLastNine.View;
|
||||||
|
DevicePrimesPow2LastOne = lastDigitSharedTables.DevicePrimesPow2LastOne.View;
|
||||||
|
DevicePrimesPow2LastSeven = lastDigitSharedTables.DevicePrimesPow2LastSeven.View;
|
||||||
|
DevicePrimesPow2LastThree = lastDigitSharedTables.DevicePrimesPow2LastThree.View;
|
||||||
|
DevicePrimesPow2LastNine = lastDigitSharedTables.DevicePrimesPow2LastNine.View;
|
||||||
|
|
||||||
|
DivisorTables = HeuristicCombinedGpuTables
|
||||||
|
.GetStaticTables(acceleratorIndex)
|
||||||
|
.CreateViews();
|
||||||
|
|
||||||
|
var smallPrimeFactorTables = SmallPrimeFactorGpuTables.GetStaticTables(acceleratorIndex);
|
||||||
|
|
||||||
|
if (smallPrimeFactorTables.HasValue)
|
||||||
|
{
|
||||||
|
SmallPrimeFactorGpuTables tables = smallPrimeFactorTables.Value;
|
||||||
|
SmallPrimeFactorPrimes = tables.Primes.View;
|
||||||
|
SmallPrimeFactorSquares = tables.Squares.View;
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckFactorsKernelLauncher = KernelUtil.GetKernel(accelerator.LoadStreamKernel<int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderGpuHeuristics.CheckFactorsKernel)).CreateLauncherDelegate<Action<AcceleratorStream, int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||||
|
|
||||||
|
ConvertToStandardKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelConvertToStandard)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||||
|
|
||||||
|
HeuristicCombinedTrialDivisionKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews>(PrimeTesterKernels.HeuristicTrialCombinedDivisionKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews>>();
|
||||||
|
|
||||||
|
KeepMontgomeryKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelKeepMontgomery)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||||
|
|
||||||
|
PollardRhoKernelLauncher = KernelUtil.GetKernel(accelerator.LoadStreamKernel<ulong, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.TryPollardRhoKernel)).CreateLauncherDelegate<Action<AcceleratorStream, ulong, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||||
|
|
||||||
|
Pow2ModWideKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>>(PrimeOrderGpuHeuristics.Pow2ModKernelWide)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>>>();
|
||||||
|
|
||||||
|
SharesFactorKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SharesFactorKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<byte>>>();
|
||||||
|
|
||||||
|
SmallPrimeFactorKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(SmallPrimeFactorKernels.SmallPrimeFactorKernelScan)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||||
|
|
||||||
|
SmallPrimeSieveKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>();
|
||||||
|
|
||||||
|
SpecialMaxKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Input.Dispose();
|
||||||
|
OutputByte.Dispose();
|
||||||
|
OutputInt.Dispose();
|
||||||
|
OutputUlong.Dispose();
|
||||||
|
Pow2ModEntriesToTestOnDevice.Dispose();
|
||||||
|
SmallPrimeFactorCountSlot.Dispose();
|
||||||
|
SpecialMaxResult.Dispose();
|
||||||
|
|
||||||
|
// These resources are shared between GPU leases
|
||||||
|
// Stream.Dispose();
|
||||||
|
// Accelerator.Dispose();
|
||||||
|
// Context.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using ILGPU;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
|
public static class ThreadStaticDeterministicRandomGpu
|
||||||
|
{
|
||||||
|
[ThreadStatic]
|
||||||
|
private static DeterministicRandomGpu? _exclusive;
|
||||||
|
internal static DeterministicRandomGpu Exclusive => _exclusive ?? new DeterministicRandomGpu();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal struct DeterministicRandomGpu
|
||||||
|
{
|
||||||
|
private static long s_seedCounter;
|
||||||
|
|
||||||
|
private ulong _state;
|
||||||
|
public readonly ulong State
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeterministicRandomGpu()
|
||||||
|
{
|
||||||
|
_state = CreateThreadState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeterministicRandomGpu(ulong initialState)
|
||||||
|
{
|
||||||
|
_state = initialState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong NextUInt64()
|
||||||
|
{
|
||||||
|
ulong state = _state;
|
||||||
|
state ^= state >> 12;
|
||||||
|
state ^= state << 25;
|
||||||
|
state ^= state >> 27;
|
||||||
|
_state = state;
|
||||||
|
return state * 2685821657736338717UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GpuUInt128 NextUInt128()
|
||||||
|
{
|
||||||
|
GpuUInt128 high = (GpuUInt128)NextUInt64();
|
||||||
|
ReadOnlyGpuUInt128 low = (ReadOnlyGpuUInt128)NextUInt64();
|
||||||
|
high.ShiftLeft(64);
|
||||||
|
high.Add(low);
|
||||||
|
return high;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetState(ulong state)
|
||||||
|
{
|
||||||
|
_state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ulong CreateThreadState()
|
||||||
|
{
|
||||||
|
ulong seed = (ulong)Atomic.Add(ref s_seedCounter, 1);
|
||||||
|
seed += 0x9E3779B97F4A7C15UL;
|
||||||
|
seed = (seed ^ (seed >> 30)) * 0xBF58476D1CE4E5B9UL;
|
||||||
|
seed = (seed ^ (seed >> 27)) * 0x94D049BB133111EBUL;
|
||||||
|
seed ^= seed >> 31;
|
||||||
|
if (seed == 0UL)
|
||||||
|
{
|
||||||
|
seed = 0x9E3779B97F4A7C15UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using PerfectNumbers.Core;
|
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
@@ -25,10 +24,7 @@ internal struct ExponentRemainderStepperGpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool MatchesDivisor(in GpuDivisorPartialData divisor)
|
public readonly bool MatchesDivisor(in GpuDivisorPartialData divisor) => _modulus == divisor.Modulus;
|
||||||
{
|
|
||||||
return _modulus == divisor.Modulus;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clears the cached residue so the stepper can be reused for another divisor.
|
/// Clears the cached residue so the stepper can be reused for another divisor.
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using ILGPU;
|
|
||||||
using ILGPU.Runtime;
|
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
|
||||||
|
|
||||||
public static class GpuContextPool
|
|
||||||
{
|
|
||||||
// The pool intentionally skips pre-loading ProcessEightBitWindows kernels so accelerator initialization
|
|
||||||
// stays stable; GpuKernelPool JIT-compiles them on first use alongside the small-cycle uploads.
|
|
||||||
// Default device preference for generic GPU kernels (prime scans, NTT, etc.)
|
|
||||||
internal sealed class PooledContext
|
|
||||||
{
|
|
||||||
public readonly Context Context;
|
|
||||||
public readonly Accelerator Accelerator;
|
|
||||||
|
|
||||||
private static readonly Context _sharedContext = Context.CreateDefault();
|
|
||||||
private static readonly Device _sharedDevice = _sharedContext.GetPreferredDevice(false);
|
|
||||||
private static readonly Accelerator _sharedAccelerator = _sharedDevice.CreateAccelerator(_sharedContext);
|
|
||||||
|
|
||||||
public PooledContext()
|
|
||||||
{
|
|
||||||
Context = _sharedContext;
|
|
||||||
Accelerator = _sharedAccelerator;
|
|
||||||
// NOTE: Avoid loading/compiling any kernel here to prevent implicit
|
|
||||||
// CL stream/queue creation during accelerator construction.
|
|
||||||
// Some OpenCL drivers are fragile when a queue is created immediately
|
|
||||||
// after device init. Real kernels will be JIT-loaded at first use.
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
// Ensure all cached GPU buffers for this accelerator are released.
|
|
||||||
NttGpuMath.ClearCaches(Accelerator);
|
|
||||||
// Release PrimeTester GPU state for this accelerator (kernel/device primes)
|
|
||||||
// Clear any per-accelerator cached resources to avoid releasing
|
|
||||||
// after the accelerator is destroyed.
|
|
||||||
PrimeTester.ClearGpuCaches(Accelerator);
|
|
||||||
|
|
||||||
// These resources are shared between GPU leases
|
|
||||||
// Accelerator.Dispose();
|
|
||||||
// Context.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly ConcurrentQueue<PooledContext> GpuPool = new();
|
|
||||||
private static int WarmedGpuContextCount;
|
|
||||||
|
|
||||||
// Allows callers to choose CPU/GPU per use-case, decoupled from ForceCpu.
|
|
||||||
public static GpuContextLease Rent()
|
|
||||||
{
|
|
||||||
if (GpuPool.TryDequeue(out var gpu))
|
|
||||||
{
|
|
||||||
return new GpuContextLease(gpu);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new accelerator when the pool does not have one available.
|
|
||||||
return new GpuContextLease(new PooledContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static void WarmUpPool(int threadCount)
|
|
||||||
{
|
|
||||||
if (threadCount <= WarmedGpuContextCount)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int toCreate = threadCount - WarmedGpuContextCount;
|
|
||||||
Console.WriteLine($"Warming up GPU context pool to {toCreate} contexts...");
|
|
||||||
var contexts = new PooledContext[toCreate];
|
|
||||||
var pool = GpuPool;
|
|
||||||
PooledContext context;
|
|
||||||
for (int i = 0; i < toCreate; i++)
|
|
||||||
{
|
|
||||||
context = new PooledContext();
|
|
||||||
contexts[i] = context;
|
|
||||||
pool.Enqueue(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
WarmedGpuContextCount = threadCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DisposeAll()
|
|
||||||
{
|
|
||||||
while (GpuPool.TryDequeue(out var gpu))
|
|
||||||
{
|
|
||||||
gpu.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
PrimeTester.DisposeGpuContexts();
|
|
||||||
WarmedGpuContextCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Return(PooledContext ctx)
|
|
||||||
{
|
|
||||||
ctx.Accelerator.Synchronize();
|
|
||||||
GpuPool.Enqueue(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct GpuContextLease
|
|
||||||
{
|
|
||||||
private PooledContext? _ctx;
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
internal GpuContextLease(PooledContext ctx)
|
|
||||||
{
|
|
||||||
_ctx = ctx;
|
|
||||||
_disposed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Context Context => _ctx!.Context;
|
|
||||||
public Accelerator Accelerator => _ctx!.Accelerator;
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
Return(_ctx!);
|
|
||||||
_ctx = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using ILGPU;
|
|
||||||
using ILGPU.Runtime;
|
|
||||||
using static PerfectNumbers.Core.Gpu.GpuContextPool;
|
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
|
||||||
|
|
||||||
public sealed class GpuKernelLease
|
|
||||||
{
|
|
||||||
private static readonly ConcurrentQueue<GpuKernelLease> Pool = new();
|
|
||||||
|
|
||||||
private Action? _releaseLimiter;
|
|
||||||
private GpuContextLease? _gpu;
|
|
||||||
private KernelContainer? _kernels;
|
|
||||||
private AcceleratorStream? _stream;
|
|
||||||
|
|
||||||
private GpuKernelLease()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static GpuKernelLease Rent(GpuPrimeWorkLimiter.Lease limiter, GpuContextLease gpu, KernelContainer kernels)
|
|
||||||
{
|
|
||||||
if (!Pool.TryDequeue(out var lease))
|
|
||||||
{
|
|
||||||
lease = new GpuKernelLease();
|
|
||||||
}
|
|
||||||
|
|
||||||
lease._releaseLimiter = limiter.Dispose;
|
|
||||||
lease._gpu = gpu;
|
|
||||||
lease._kernels = kernels;
|
|
||||||
lease._stream = null;
|
|
||||||
return lease;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Accelerator Accelerator => _gpu?.Accelerator ?? throw new NullReferenceException("GPU context is already released");
|
|
||||||
|
|
||||||
public AcceleratorStream Stream
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_stream is { } stream)
|
|
||||||
{
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
// It feels that we can combine these into 1 return statement, but that increases the max stack frame size to 3 from 2
|
|
||||||
stream = Accelerator.CreateStream();
|
|
||||||
_stream = stream;
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Action<AcceleratorStream, Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>> OrderKernel
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var accel = Accelerator; // avoid capturing 'this' in lambda
|
|
||||||
return KernelContainer.InitOnce(ref _kernels!.Order, () =>
|
|
||||||
{
|
|
||||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>(OrderKernels.OrderKernelScan);
|
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
|
||||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>>();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public 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
|
|
||||||
{
|
|
||||||
var accel = Accelerator;
|
|
||||||
return KernelContainer.InitOnce(ref _kernels!.Pow2Mod, () =>
|
|
||||||
{
|
|
||||||
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>>(Pow2ModKernels.Pow2ModKernelScan);
|
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
|
||||||
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 Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>> IncrementalKernel
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var accel = Accelerator;
|
|
||||||
return KernelContainer.InitOnce(ref _kernels!.Incremental, () =>
|
|
||||||
{
|
|
||||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalKernelScan);
|
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
|
||||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>> IncrementalOrderKernel
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var accel = Accelerator;
|
|
||||||
return KernelContainer.InitOnce(ref _kernels!.IncrementalOrder, () =>
|
|
||||||
{
|
|
||||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalOrderKernelScan);
|
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
|
||||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>> Pow2ModOrderKernel
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var accel = Accelerator;
|
|
||||||
return KernelContainer.InitOnce(ref _kernels!.Pow2ModOrder, () =>
|
|
||||||
{
|
|
||||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernels.Pow2ModOrderKernelScan);
|
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
|
||||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SmallPrimeFactorKernel
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var accel = Accelerator;
|
|
||||||
return KernelContainer.InitOnce(ref _kernels!.SmallPrimeFactor, () =>
|
|
||||||
{
|
|
||||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(SmallPrimeFactorKernels.SmallPrimeFactorKernelScan);
|
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
|
||||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_stream?.Dispose();
|
|
||||||
_stream = null;
|
|
||||||
|
|
||||||
_gpu?.Dispose();
|
|
||||||
_gpu = null;
|
|
||||||
|
|
||||||
_releaseLimiter?.Invoke();
|
|
||||||
_releaseLimiter = null;
|
|
||||||
|
|
||||||
_kernels = null;
|
|
||||||
Pool.Enqueue(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,188 +1,188 @@
|
|||||||
using System.Collections.Concurrent;
|
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
using static PerfectNumbers.Core.Gpu.GpuContextPool;
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
public readonly struct ResidueAutomatonArgs
|
|
||||||
{
|
|
||||||
public readonly ulong Q0M10;
|
|
||||||
public readonly ulong Step10;
|
|
||||||
public readonly ulong Q0M8;
|
|
||||||
public readonly ulong Step8;
|
|
||||||
public readonly ulong Q0M3;
|
|
||||||
public readonly ulong Step3;
|
|
||||||
public readonly ulong Q0M5;
|
|
||||||
public readonly ulong Step5;
|
|
||||||
|
|
||||||
public ResidueAutomatonArgs(ulong q0m10, ulong step10, ulong q0m8, ulong step8, ulong q0m3, ulong step3, ulong q0m5, ulong step5)
|
|
||||||
{
|
|
||||||
Q0M10 = q0m10; Step10 = step10; Q0M8 = q0m8; Step8 = step8; Q0M3 = q0m3; Step3 = step3; Q0M5 = q0m5; Step5 = step5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class KernelContainer
|
|
||||||
{
|
|
||||||
// Serializes first-time initialization of kernels/buffers per accelerator.
|
|
||||||
public Action<AcceleratorStream, Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>? Order;
|
|
||||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
|
||||||
ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>? Incremental;
|
|
||||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
|
||||||
ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>,
|
|
||||||
ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2Mod;
|
|
||||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
|
||||||
ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>? IncrementalOrder;
|
|
||||||
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
|
||||||
ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2ModOrder;
|
|
||||||
public Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>? SmallPrimeFactor;
|
|
||||||
|
|
||||||
// Optional device buffer with small divisor cycles (<= 4M). Index = divisor, value = cycle length.
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallCycles;
|
|
||||||
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimeFactorsPrimes;
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimeFactorsSquares;
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimeFactorPrimeSlots;
|
|
||||||
public MemoryBuffer1D<int, Stride1D.Dense>? SmallPrimeFactorExponentSlots;
|
|
||||||
public MemoryBuffer1D<int, Stride1D.Dense>? SmallPrimeFactorCountSlot;
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimeFactorRemainingSlot;
|
|
||||||
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastOne;
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2LastOne;
|
|
||||||
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastSeven;
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2LastSeven;
|
|
||||||
|
|
||||||
public static T InitOnce<T>(ref T? slot, Func<T> factory) where T : class
|
|
||||||
{
|
|
||||||
var current = Volatile.Read(ref slot);
|
|
||||||
if (current is not null)
|
|
||||||
{
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
current = Volatile.Read(ref slot);
|
|
||||||
if (current is null)
|
|
||||||
{
|
|
||||||
current = factory();
|
|
||||||
Volatile.Write(ref slot, current);
|
|
||||||
}
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly struct ResiduePrimeViews(
|
|
||||||
ArrayView1D<uint, Stride1D.Dense> lastOne,
|
|
||||||
ArrayView1D<uint, Stride1D.Dense> lastSeven,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> lastOnePow2,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> lastSevenPow2)
|
|
||||||
{
|
|
||||||
public readonly ArrayView1D<uint, Stride1D.Dense> LastOne = lastOne;
|
|
||||||
public readonly ArrayView1D<uint, Stride1D.Dense> LastSeven = lastSeven;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> LastOnePow2 = lastOnePow2;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> LastSevenPow2 = lastSevenPow2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly struct SmallPrimeFactorTables
|
|
||||||
{
|
|
||||||
public readonly MemoryBuffer1D<uint, Stride1D.Dense> Primes;
|
|
||||||
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> Squares;
|
|
||||||
public readonly int Count;
|
|
||||||
|
|
||||||
public SmallPrimeFactorTables(
|
|
||||||
MemoryBuffer1D<uint, Stride1D.Dense> primes,
|
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense> squares,
|
|
||||||
int count)
|
|
||||||
{
|
|
||||||
Primes = primes;
|
|
||||||
Squares = squares;
|
|
||||||
Count = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayView1D<uint, Stride1D.Dense> PrimesView => Primes.View;
|
|
||||||
public ArrayView1D<ulong, Stride1D.Dense> SquaresView => Squares.View;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly struct SmallPrimeFactorScratch
|
|
||||||
{
|
|
||||||
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> PrimeSlots;
|
|
||||||
public readonly MemoryBuffer1D<int, Stride1D.Dense> ExponentSlots;
|
|
||||||
public readonly MemoryBuffer1D<int, Stride1D.Dense> CountSlot;
|
|
||||||
public readonly MemoryBuffer1D<ulong, Stride1D.Dense> RemainingSlot;
|
|
||||||
|
|
||||||
public SmallPrimeFactorScratch(
|
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense> primeSlots,
|
|
||||||
MemoryBuffer1D<int, Stride1D.Dense> exponentSlots,
|
|
||||||
MemoryBuffer1D<int, Stride1D.Dense> countSlot,
|
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense> remainingSlot)
|
|
||||||
{
|
|
||||||
PrimeSlots = primeSlots;
|
|
||||||
ExponentSlots = exponentSlots;
|
|
||||||
CountSlot = countSlot;
|
|
||||||
RemainingSlot = remainingSlot;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
PrimeSlots.MemSetToZero();
|
|
||||||
ExponentSlots.MemSetToZero();
|
|
||||||
CountSlot.MemSetToZero();
|
|
||||||
RemainingSlot.MemSetToZero();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GpuKernelPool
|
public class GpuKernelPool
|
||||||
{
|
{
|
||||||
private static readonly ConcurrentDictionary<Accelerator, KernelContainer> KernelCache = new(); // TODO: Replace this concurrent map with a simple accelerator-indexed lookup once kernel launchers are prewarmed during startup so we can drop the thread-safe wrapper entirely.
|
[ThreadStatic]
|
||||||
|
private static KernelContainer[]? _pool;
|
||||||
|
|
||||||
private static KernelContainer GetKernels(Accelerator accelerator)
|
public static T InitOnce<T>(ref T? target, Func<T> valueFactory) where T : class
|
||||||
{
|
{
|
||||||
return KernelCache.GetOrAdd(accelerator, _ => new KernelContainer());
|
if (target is { } value)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var newValue = valueFactory();
|
||||||
|
return Interlocked.CompareExchange(ref target, newValue, null) ?? newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
|
||||||
|
|
||||||
|
internal static KernelContainer GetOrAddKernels(int acceleratorIndex, AcceleratorStream stream, KernelType kernelType)
|
||||||
|
{
|
||||||
|
var pool = _pool ??= [];
|
||||||
|
if (pool[acceleratorIndex] is not { } kernels)
|
||||||
|
{
|
||||||
|
kernels = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
Accelerator accelerator = _accelerators[acceleratorIndex];
|
||||||
|
if (kernelType.HasFlag(KernelType.OrderKernelScan))
|
||||||
|
{
|
||||||
|
InitOrderKernelScan(accelerator, kernels);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kernelType.HasFlag(KernelType.Pow2ModKernelScan))
|
||||||
|
{
|
||||||
|
InitPow2ModKernelScan(accelerator, kernels);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kernelType.HasFlag(KernelType.IncrementalKernelScan))
|
||||||
|
{
|
||||||
|
InitIncrementalKernelScan(accelerator, kernels);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kernelType.HasFlag(KernelType.IncrementalOrderKernelScan))
|
||||||
|
{
|
||||||
|
IncrementalOrderKernelScan(accelerator, kernels);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kernelType.HasFlag(KernelType.Pow2ModOrderKernelScan))
|
||||||
|
{
|
||||||
|
InitPow2ModOrderKernelScan(accelerator, kernels);
|
||||||
|
}
|
||||||
|
|
||||||
|
PreloadStaticTables(accelerator, kernels, stream, kernelType);
|
||||||
|
|
||||||
|
pool[acceleratorIndex] = kernels;
|
||||||
|
return kernels;
|
||||||
|
|
||||||
|
static void InitOrderKernelScan(Accelerator accelerator, KernelContainer kernels)
|
||||||
|
{
|
||||||
|
InitOnce(ref kernels.Order, () =>
|
||||||
|
{
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>(OrderKernels.OrderKernelScan);
|
||||||
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitPow2ModKernelScan(Accelerator accelerator, KernelContainer kernels)
|
||||||
|
{
|
||||||
|
InitOnce(ref kernels.Pow2Mod, () =>
|
||||||
|
{
|
||||||
|
var loaded = accelerator.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>>(Pow2ModKernels.Pow2ModKernelScan);
|
||||||
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
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>>>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitIncrementalKernelScan(Accelerator accelerator, KernelContainer kernels)
|
||||||
|
{
|
||||||
|
InitOnce(ref kernels.Incremental, () =>
|
||||||
|
{
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalKernelScan);
|
||||||
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void IncrementalOrderKernelScan(Accelerator accelerator, KernelContainer kernels)
|
||||||
|
{
|
||||||
|
InitOnce(ref kernels.IncrementalOrder, () =>
|
||||||
|
{
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(IncrementalKernels.IncrementalOrderKernelScan);
|
||||||
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte,
|
||||||
|
ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitPow2ModOrderKernelScan(Accelerator accelerator, KernelContainer kernels)
|
||||||
|
{
|
||||||
|
InitOnce(ref kernels.Pow2ModOrder, () =>
|
||||||
|
{
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernels.Pow2ModOrderKernelScan);
|
||||||
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong, ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void PreloadStaticTables(Accelerator accelerator, KernelContainer kernels, AcceleratorStream stream, KernelType kernelType)
|
||||||
|
{
|
||||||
|
if (kernelType.HasFlag(KernelType.SmallCycles))
|
||||||
|
{
|
||||||
|
EnsureSmallCyclesOnDevice(accelerator, kernels, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kernelType.HasFlag(KernelType.SmallPrimes))
|
||||||
|
{
|
||||||
|
EnsureSmallPrimesOnDevice(accelerator, kernels, stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayView1D<ulong, Stride1D.Dense> GetSmallCyclesOnDevice(KernelContainer kernels) => kernels.SmallCycles!;
|
||||||
|
|
||||||
// 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<ulong, Stride1D.Dense> EnsureSmallCyclesOnDevice(Accelerator accelerator)
|
private static void EnsureSmallCyclesOnDevice(Accelerator accelerator, KernelContainer kernels, AcceleratorStream stream)
|
||||||
{
|
{
|
||||||
var kernels = GetKernels(accelerator);
|
if (kernels.SmallCycles is { })
|
||||||
if (kernels.SmallCycles is { } buffer)
|
|
||||||
{
|
{
|
||||||
return buffer.View;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure single upload per accelerator even if multiple threads race here.
|
var host = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
||||||
lock (kernels) // TODO: Remove this lock by pre-uploading the immutable small-cycle snapshot during initialization; once no mutation happens at runtime, the pool must expose a simple reference without synchronization.
|
MemoryBuffer1D<ulong, Stride1D.Dense>? device;
|
||||||
|
|
||||||
|
lock (accelerator)
|
||||||
{
|
{
|
||||||
if (kernels.SmallCycles is { } existing)
|
device = accelerator.Allocate1D<ulong>(host.Length);
|
||||||
{
|
|
||||||
return existing.View;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var host = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot(); // TODO: Preload this device buffer during startup and keep it immutable so we can delete the lock above in favor of the preloaded snapshot.
|
device.View.CopyFromCPU(stream, host);
|
||||||
var device = accelerator.Allocate1D<ulong>(host.Length);
|
|
||||||
device.View.CopyFromCPU(host);
|
|
||||||
kernels.SmallCycles = device;
|
kernels.SmallCycles = device;
|
||||||
return device.View;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResiduePrimeViews EnsureSmallPrimesOnDevice(Accelerator accelerator)
|
public static ResiduePrimeViews GetSmallPrimesOnDevice(KernelContainer kernels) => new(
|
||||||
|
kernels.SmallPrimesLastOne!,
|
||||||
|
kernels.SmallPrimesLastSeven!,
|
||||||
|
kernels.SmallPrimesPow2LastOne!,
|
||||||
|
kernels.SmallPrimesPow2LastSeven!
|
||||||
|
);
|
||||||
|
|
||||||
|
private static void EnsureSmallPrimesOnDevice(Accelerator accelerator, KernelContainer kernels, AcceleratorStream stream)
|
||||||
{
|
{
|
||||||
var kernels = GetKernels(accelerator);
|
if (kernels.SmallPrimesLastOne is { } &&
|
||||||
if (kernels.SmallPrimesLastOne is { } lastOne &&
|
kernels.SmallPrimesLastSeven is { } &&
|
||||||
kernels.SmallPrimesLastSeven is { } lastSeven &&
|
kernels.SmallPrimesPow2LastOne is { } &&
|
||||||
kernels.SmallPrimesPow2LastOne is { } lastOnePow2 &&
|
kernels.SmallPrimesPow2LastSeven is { })
|
||||||
kernels.SmallPrimesPow2LastSeven is { } lastSevenPow2)
|
|
||||||
{
|
{
|
||||||
return new ResiduePrimeViews(lastOne.View, lastSeven.View, lastOnePow2.View, lastSevenPow2.View);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (kernels) // TODO: Inline these small-prime uploads into startup initialization alongside the small-cycle snapshot so we can drop runtime locking and keep the GPU pool free of synchronization.
|
if (kernels.SmallPrimesLastOne is { } &&
|
||||||
|
kernels.SmallPrimesLastSeven is { } &&
|
||||||
|
kernels.SmallPrimesPow2LastOne is { } &&
|
||||||
|
kernels.SmallPrimesPow2LastSeven is { })
|
||||||
{
|
{
|
||||||
if (kernels.SmallPrimesLastOne is { } existingLastOne &&
|
return;
|
||||||
kernels.SmallPrimesLastSeven is { } existingLastSeven &&
|
|
||||||
kernels.SmallPrimesPow2LastOne is { } existingLastOnePow2 &&
|
|
||||||
kernels.SmallPrimesPow2LastSeven is { } existingLastSevenPow2)
|
|
||||||
{
|
|
||||||
return new ResiduePrimeViews(existingLastOne.View, existingLastSeven.View, existingLastOnePow2.View, existingLastSevenPow2.View);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostLastOne = PrimesGenerator.SmallPrimesLastOne;
|
var hostLastOne = PrimesGenerator.SmallPrimesLastOne;
|
||||||
@@ -190,100 +190,27 @@ public class GpuKernelPool
|
|||||||
var hostLastOnePow2 = PrimesGenerator.SmallPrimesPow2LastOne;
|
var hostLastOnePow2 = PrimesGenerator.SmallPrimesPow2LastOne;
|
||||||
var hostLastSevenPow2 = PrimesGenerator.SmallPrimesPow2LastSeven;
|
var hostLastSevenPow2 = PrimesGenerator.SmallPrimesPow2LastSeven;
|
||||||
|
|
||||||
var deviceLastOne = accelerator.Allocate1D<uint>(hostLastOne.Length);
|
MemoryBuffer1D<uint, Stride1D.Dense>? deviceLastOne;
|
||||||
deviceLastOne.View.CopyFromCPU(hostLastOne);
|
MemoryBuffer1D<uint, Stride1D.Dense>? deviceLastSeven;
|
||||||
var deviceLastSeven = accelerator.Allocate1D<uint>(hostLastSeven.Length);
|
MemoryBuffer1D<ulong, Stride1D.Dense>? deviceLastOnePow2;
|
||||||
deviceLastSeven.View.CopyFromCPU(hostLastSeven);
|
MemoryBuffer1D<ulong, Stride1D.Dense>? deviceLastSevenPow2;
|
||||||
var deviceLastOnePow2 = accelerator.Allocate1D<ulong>(hostLastOnePow2.Length);
|
|
||||||
deviceLastOnePow2.View.CopyFromCPU(hostLastOnePow2);
|
|
||||||
var deviceLastSevenPow2 = accelerator.Allocate1D<ulong>(hostLastSevenPow2.Length);
|
|
||||||
deviceLastSevenPow2.View.CopyFromCPU(hostLastSevenPow2);
|
|
||||||
|
|
||||||
|
lock (accelerator)
|
||||||
|
{
|
||||||
|
deviceLastOne = accelerator.Allocate1D<uint>(hostLastOne.Length);
|
||||||
|
deviceLastSeven = accelerator.Allocate1D<uint>(hostLastSeven.Length);
|
||||||
|
deviceLastOnePow2 = accelerator.Allocate1D<ulong>(hostLastOnePow2.Length);
|
||||||
|
deviceLastSevenPow2 = accelerator.Allocate1D<ulong>(hostLastSevenPow2.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceLastOne.View.CopyFromCPU(stream, hostLastOne);
|
||||||
|
deviceLastSeven.View.CopyFromCPU(stream, hostLastSeven);
|
||||||
|
deviceLastOnePow2.View.CopyFromCPU(stream, hostLastOnePow2);
|
||||||
|
deviceLastSevenPow2.View.CopyFromCPU(stream, hostLastSevenPow2);
|
||||||
kernels.SmallPrimesLastOne = deviceLastOne;
|
kernels.SmallPrimesLastOne = deviceLastOne;
|
||||||
kernels.SmallPrimesLastSeven = deviceLastSeven;
|
kernels.SmallPrimesLastSeven = deviceLastSeven;
|
||||||
kernels.SmallPrimesPow2LastOne = deviceLastOnePow2;
|
kernels.SmallPrimesPow2LastOne = deviceLastOnePow2;
|
||||||
kernels.SmallPrimesPow2LastSeven = deviceLastSevenPow2;
|
kernels.SmallPrimesPow2LastSeven = deviceLastSevenPow2;
|
||||||
|
|
||||||
return new ResiduePrimeViews(deviceLastOne.View, deviceLastSeven.View, deviceLastOnePow2.View, deviceLastSevenPow2.View);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SmallPrimeFactorTables EnsureSmallPrimeFactorTables(Accelerator accelerator)
|
|
||||||
{
|
|
||||||
var kernels = GetKernels(accelerator);
|
|
||||||
if (kernels.SmallPrimeFactorsPrimes is { } primeBuffer && kernels.SmallPrimeFactorsSquares is { } squareBuffer)
|
|
||||||
{
|
|
||||||
return new SmallPrimeFactorTables(primeBuffer, squareBuffer, (int)primeBuffer.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (kernels)
|
|
||||||
{
|
|
||||||
if (kernels.SmallPrimeFactorsPrimes is { } existingPrimes && kernels.SmallPrimeFactorsSquares is { } existingSquares)
|
|
||||||
{
|
|
||||||
return new SmallPrimeFactorTables(existingPrimes, existingSquares, (int)existingPrimes.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
var hostPrimes = PrimesGenerator.SmallPrimes;
|
|
||||||
var hostSquares = PrimesGenerator.SmallPrimesPow2;
|
|
||||||
|
|
||||||
var devicePrimes = accelerator.Allocate1D<uint>(hostPrimes.Length);
|
|
||||||
devicePrimes.View.CopyFromCPU(hostPrimes);
|
|
||||||
var deviceSquares = accelerator.Allocate1D<ulong>(hostSquares.Length);
|
|
||||||
deviceSquares.View.CopyFromCPU(hostSquares);
|
|
||||||
|
|
||||||
kernels.SmallPrimeFactorsPrimes = devicePrimes;
|
|
||||||
kernels.SmallPrimeFactorsSquares = deviceSquares;
|
|
||||||
|
|
||||||
return new SmallPrimeFactorTables(devicePrimes, deviceSquares, hostPrimes.Length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SmallPrimeFactorScratch EnsureSmallPrimeFactorScratch(Accelerator accelerator, int slotCount)
|
|
||||||
{
|
|
||||||
var kernels = GetKernels(accelerator);
|
|
||||||
lock (kernels)
|
|
||||||
{
|
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense>? primeSlots = kernels.SmallPrimeFactorPrimeSlots;
|
|
||||||
if (primeSlots is null || primeSlots.Length < slotCount)
|
|
||||||
{
|
|
||||||
primeSlots?.Dispose();
|
|
||||||
primeSlots = accelerator.Allocate1D<ulong>(slotCount);
|
|
||||||
kernels.SmallPrimeFactorPrimeSlots = primeSlots;
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryBuffer1D<int, Stride1D.Dense>? exponentSlots = kernels.SmallPrimeFactorExponentSlots;
|
|
||||||
if (exponentSlots is null || exponentSlots.Length < slotCount)
|
|
||||||
{
|
|
||||||
exponentSlots?.Dispose();
|
|
||||||
exponentSlots = accelerator.Allocate1D<int>(slotCount);
|
|
||||||
kernels.SmallPrimeFactorExponentSlots = exponentSlots;
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryBuffer1D<int, Stride1D.Dense>? countSlot = kernels.SmallPrimeFactorCountSlot;
|
|
||||||
if (countSlot is null)
|
|
||||||
{
|
|
||||||
countSlot = accelerator.Allocate1D<int>(1);
|
|
||||||
kernels.SmallPrimeFactorCountSlot = countSlot;
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryBuffer1D<ulong, Stride1D.Dense>? remainingSlot = kernels.SmallPrimeFactorRemainingSlot;
|
|
||||||
if (remainingSlot is null)
|
|
||||||
{
|
|
||||||
remainingSlot = accelerator.Allocate1D<ulong>(1);
|
|
||||||
kernels.SmallPrimeFactorRemainingSlot = remainingSlot;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SmallPrimeFactorScratch(primeSlots!, exponentSlots!, countSlot!, remainingSlot!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GpuKernelLease GetKernel(bool useGpuOrder)
|
|
||||||
{
|
|
||||||
var limiter = GpuPrimeWorkLimiter.Acquire();
|
|
||||||
var gpu = Rent();
|
|
||||||
var accelerator = gpu.Accelerator;
|
|
||||||
var kernels = GetKernels(accelerator);
|
|
||||||
return GpuKernelLease.Rent(limiter, gpu, kernels);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -292,12 +219,14 @@ public class GpuKernelPool
|
|||||||
/// <param name="action">Action to run with (Accelerator, Stream).</param>
|
/// <param name="action">Action to run with (Accelerator, Stream).</param>
|
||||||
public static void Run(Action<Accelerator, AcceleratorStream> action)
|
public static void Run(Action<Accelerator, AcceleratorStream> action)
|
||||||
{
|
{
|
||||||
var lease = GetKernel(useGpuOrder: true);
|
// GpuPrimeWorkLimiter.Acquire();
|
||||||
var accelerator = lease.Accelerator;
|
var acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
var stream = accelerator.CreateStream();
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
|
var stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
action(accelerator, stream);
|
action(accelerator, stream);
|
||||||
|
stream.Synchronize();
|
||||||
stream.Dispose();
|
stream.Dispose();
|
||||||
lease.Dispose();
|
accelerator.Dispose();
|
||||||
|
// GpuPrimeWorkLimiter.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
public static class GpuPrimeWorkLimiter
|
public static class GpuPrimeWorkLimiter
|
||||||
@@ -8,14 +5,8 @@ public static class GpuPrimeWorkLimiter
|
|||||||
private static SemaphoreSlim _semaphore = new(1, int.MaxValue);
|
private static SemaphoreSlim _semaphore = new(1, int.MaxValue);
|
||||||
private static int _currentLimit = 1;
|
private static int _currentLimit = 1;
|
||||||
|
|
||||||
public static Lease Acquire()
|
public static void Acquire() { } // => _semaphore.Wait();
|
||||||
{
|
public static void Release() { }// => _semaphore.Release();
|
||||||
// TODO: Inline the pooled limiter guard from the GpuLimiterThroughputBenchmarks so prime-sieve
|
|
||||||
// GPU jobs stop allocating Lease instances on every acquisition.
|
|
||||||
var sem = _semaphore;
|
|
||||||
sem.Wait();
|
|
||||||
return new Lease(sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetLimit(int value)
|
public static void SetLimit(int value)
|
||||||
{
|
{
|
||||||
@@ -36,19 +27,4 @@ public static class GpuPrimeWorkLimiter
|
|||||||
_currentLimit = value;
|
_currentLimit = value;
|
||||||
previous.Dispose();
|
previous.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class Lease : IDisposable
|
|
||||||
{
|
|
||||||
private readonly SemaphoreSlim _sem;
|
|
||||||
|
|
||||||
internal Lease(SemaphoreSlim sem)
|
|
||||||
{
|
|
||||||
_sem = sem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_sem.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -261,13 +261,13 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
|
|||||||
public override readonly int GetHashCode() => HashCode.Combine(High, Low);
|
public override readonly int GetHashCode() => HashCode.Combine(High, Low);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Add(in ReadOnlyGpuUInt128 other) => AddInternal(other.High, other.Low);
|
public void Add(in ReadOnlyGpuUInt128 other) => Add(other.High, other.Low);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Add(GpuUInt128 other) => AddInternal(other.High, other.Low);
|
public void Add(GpuUInt128 other) => Add(other.High, other.Low);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void AddInternal(ulong otherHigh, ulong otherLow)
|
public void Add(ulong otherHigh, ulong otherLow)
|
||||||
{
|
{
|
||||||
ulong originalLow = Low;
|
ulong originalLow = Low;
|
||||||
// We're reusing originalLow for carry to avoid an extra local.
|
// We're reusing originalLow for carry to avoid an extra local.
|
||||||
@@ -316,7 +316,7 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AddInternal(valueHigh, valueLow);
|
Add(valueHigh, valueLow);
|
||||||
if (CompareTo(in modulus) >= 0)
|
if (CompareTo(in modulus) >= 0)
|
||||||
{
|
{
|
||||||
Sub(in modulus);
|
Sub(in modulus);
|
||||||
@@ -589,9 +589,6 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static GpuUInt128 BinaryGcd(GpuUInt128 u, GpuUInt128 v)
|
public static GpuUInt128 BinaryGcd(GpuUInt128 u, GpuUInt128 v)
|
||||||
{
|
{
|
||||||
// TODO: Replace this scalar binary GCD with the branchless reduction from
|
|
||||||
// GpuUInt128BinaryGcdBenchmarks so CPU fallbacks stay aligned with the GPU kernel
|
|
||||||
// performance when resolving large divisor residues.
|
|
||||||
if (u.IsZero)
|
if (u.IsZero)
|
||||||
{
|
{
|
||||||
return v;
|
return v;
|
||||||
@@ -602,26 +599,50 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
|
|||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
int shift = TrailingZeroCount(new GpuUInt128(u.High | v.High, u.Low | v.Low));
|
ulong combinedLow = u.Low | v.Low;
|
||||||
|
int shift;
|
||||||
|
if (combinedLow != 0UL)
|
||||||
|
{
|
||||||
|
shift = BitOperations.TrailingZeroCount(combinedLow);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ulong combinedHigh = u.High | v.High;
|
||||||
|
shift = 64 + BitOperations.TrailingZeroCount(combinedHigh);
|
||||||
|
}
|
||||||
|
|
||||||
int zu = TrailingZeroCount(u);
|
int zu = TrailingZeroCount(u);
|
||||||
u >>= zu;
|
u >>= zu;
|
||||||
|
|
||||||
do
|
GpuUInt128 currentV = v;
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
int zv = TrailingZeroCount(v);
|
int zv = TrailingZeroCount(currentV);
|
||||||
v >>= zv;
|
currentV >>= zv;
|
||||||
if (u > v)
|
|
||||||
|
bool swap = u > currentV;
|
||||||
|
GpuUInt128 min = ConditionalSelect(u, currentV, swap);
|
||||||
|
GpuUInt128 max = ConditionalSelect(currentV, u, swap);
|
||||||
|
max.Sub(min);
|
||||||
|
|
||||||
|
if (max.IsZero)
|
||||||
{
|
{
|
||||||
(u, v) = (v, u);
|
return min << shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
v -= u;
|
u = min;
|
||||||
|
currentV = max;
|
||||||
}
|
}
|
||||||
while (!v.IsZero);
|
|
||||||
|
|
||||||
return u << shift;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static GpuUInt128 ConditionalSelect(in GpuUInt128 left, in GpuUInt128 right, bool useRight)
|
||||||
|
{
|
||||||
|
ulong mask = useRight ? ulong.MaxValue : 0UL;
|
||||||
|
return new GpuUInt128(
|
||||||
|
left.High ^ ((left.High ^ right.High) & mask),
|
||||||
|
left.Low ^ ((left.Low ^ right.Low) & mask));
|
||||||
|
}
|
||||||
|
|
||||||
public void Sub(ulong value)
|
public void Sub(ulong value)
|
||||||
{
|
{
|
||||||
@@ -1529,6 +1550,15 @@ public struct GpuUInt128 : IComparable<GpuUInt128>, IEquatable<GpuUInt128>
|
|||||||
high = hi;
|
high = hi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Pow2()
|
||||||
|
{
|
||||||
|
ulong operand = Low;
|
||||||
|
Low = operand * operand;
|
||||||
|
ulong highProduct = operand * High;
|
||||||
|
High = highProduct + MulHigh(operand, operand);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Check if the TODO below is still relevant.
|
// TODO: Check if the TODO below is still relevant.
|
||||||
|
|||||||
@@ -1,146 +0,0 @@
|
|||||||
using System.Runtime.CompilerServices;
|
|
||||||
using ILGPU;
|
|
||||||
using ILGPU.Runtime;
|
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
|
||||||
|
|
||||||
internal readonly struct HeuristicGpuDivisorTables
|
|
||||||
{
|
|
||||||
public static readonly HeuristicGpuDivisorTables Shared = default;
|
|
||||||
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding1;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding3;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding7;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorsEnding9;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding1;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding3;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding7;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> CombinedDivisorSquaresEnding9;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupADivisors;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupADivisorSquares;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding1;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding1;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding7;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding7;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorsEnding9;
|
|
||||||
public readonly ArrayView1D<ulong, Stride1D.Dense> GroupBDivisorSquaresEnding9;
|
|
||||||
|
|
||||||
public HeuristicGpuDivisorTables(
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding1,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding3,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding7,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorsEnding9,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding1,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding3,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding7,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> combinedDivisorSquaresEnding9,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> groupADivisors,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> groupADivisorSquares,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding1,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding1,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding7,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding7,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorsEnding9,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> groupBDivisorSquaresEnding9)
|
|
||||||
{
|
|
||||||
CombinedDivisorsEnding1 = combinedDivisorsEnding1;
|
|
||||||
CombinedDivisorsEnding3 = combinedDivisorsEnding3;
|
|
||||||
CombinedDivisorsEnding7 = combinedDivisorsEnding7;
|
|
||||||
CombinedDivisorsEnding9 = combinedDivisorsEnding9;
|
|
||||||
CombinedDivisorSquaresEnding1 = combinedDivisorSquaresEnding1;
|
|
||||||
CombinedDivisorSquaresEnding3 = combinedDivisorSquaresEnding3;
|
|
||||||
CombinedDivisorSquaresEnding7 = combinedDivisorSquaresEnding7;
|
|
||||||
CombinedDivisorSquaresEnding9 = combinedDivisorSquaresEnding9;
|
|
||||||
GroupADivisors = groupADivisors;
|
|
||||||
GroupADivisorSquares = groupADivisorSquares;
|
|
||||||
GroupBDivisorsEnding1 = groupBDivisorsEnding1;
|
|
||||||
GroupBDivisorSquaresEnding1 = groupBDivisorSquaresEnding1;
|
|
||||||
GroupBDivisorsEnding7 = groupBDivisorsEnding7;
|
|
||||||
GroupBDivisorSquaresEnding7 = groupBDivisorSquaresEnding7;
|
|
||||||
GroupBDivisorsEnding9 = groupBDivisorsEnding9;
|
|
||||||
GroupBDivisorSquaresEnding9 = groupBDivisorSquaresEnding9;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ArrayView1D<ulong, Stride1D.Dense> SelectCombined(byte ending)
|
|
||||||
{
|
|
||||||
return ending switch
|
|
||||||
{
|
|
||||||
1 => CombinedDivisorsEnding1,
|
|
||||||
3 => CombinedDivisorsEnding3,
|
|
||||||
7 => CombinedDivisorsEnding7,
|
|
||||||
9 => CombinedDivisorsEnding9,
|
|
||||||
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ArrayView1D<ulong, Stride1D.Dense> SelectCombinedSquares(byte ending)
|
|
||||||
{
|
|
||||||
return ending switch
|
|
||||||
{
|
|
||||||
1 => CombinedDivisorSquaresEnding1,
|
|
||||||
3 => CombinedDivisorSquaresEnding3,
|
|
||||||
7 => CombinedDivisorSquaresEnding7,
|
|
||||||
9 => CombinedDivisorSquaresEnding9,
|
|
||||||
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ArrayView1D<ulong, Stride1D.Dense> SelectGroupB(byte ending)
|
|
||||||
{
|
|
||||||
return ending switch
|
|
||||||
{
|
|
||||||
1 => GroupBDivisorsEnding1,
|
|
||||||
7 => GroupBDivisorsEnding7,
|
|
||||||
9 => GroupBDivisorsEnding9,
|
|
||||||
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ArrayView1D<ulong, Stride1D.Dense> SelectGroupBSquares(byte ending)
|
|
||||||
{
|
|
||||||
return ending switch
|
|
||||||
{
|
|
||||||
1 => GroupBDivisorSquaresEnding1,
|
|
||||||
7 => GroupBDivisorSquaresEnding7,
|
|
||||||
9 => GroupBDivisorSquaresEnding9,
|
|
||||||
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ArrayView1D<ulong, Stride1D.Dense> SelectDivisors(HeuristicGpuDivisorTableKind kind, byte ending)
|
|
||||||
{
|
|
||||||
return kind switch
|
|
||||||
{
|
|
||||||
HeuristicGpuDivisorTableKind.GroupA => GroupADivisors,
|
|
||||||
HeuristicGpuDivisorTableKind.GroupBEnding1 => GroupBDivisorsEnding1,
|
|
||||||
HeuristicGpuDivisorTableKind.GroupBEnding7 => GroupBDivisorsEnding7,
|
|
||||||
HeuristicGpuDivisorTableKind.GroupBEnding9 => GroupBDivisorsEnding9,
|
|
||||||
HeuristicGpuDivisorTableKind.Combined => SelectCombined(ending),
|
|
||||||
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ArrayView1D<ulong, Stride1D.Dense> SelectDivisorSquares(HeuristicGpuDivisorTableKind kind, byte ending)
|
|
||||||
{
|
|
||||||
return kind switch
|
|
||||||
{
|
|
||||||
HeuristicGpuDivisorTableKind.GroupA => GroupADivisorSquares,
|
|
||||||
HeuristicGpuDivisorTableKind.GroupBEnding1 => GroupBDivisorSquaresEnding1,
|
|
||||||
HeuristicGpuDivisorTableKind.GroupBEnding7 => GroupBDivisorSquaresEnding7,
|
|
||||||
HeuristicGpuDivisorTableKind.GroupBEnding9 => GroupBDivisorSquaresEnding9,
|
|
||||||
HeuristicGpuDivisorTableKind.Combined => SelectCombinedSquares(ending),
|
|
||||||
_ => ArrayView1D<ulong, Stride1D.Dense>.Empty,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void InitializeShared(in HeuristicGpuDivisorTables tables)
|
|
||||||
{
|
|
||||||
Unsafe.AsRef(in Shared) = tables;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
using ILGPU;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
|
public sealed class KernelContainer
|
||||||
|
{
|
||||||
|
// Serializes first-time initialization of kernels/buffers per accelerator.
|
||||||
|
public Action<AcceleratorStream, Index1D, ulong, ulong, ArrayView<GpuUInt128>, ArrayView<ulong>>? Order;
|
||||||
|
|
||||||
|
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
||||||
|
ulong, ulong, ulong, ulong, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>>? Incremental;
|
||||||
|
|
||||||
|
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
||||||
|
ResidueAutomatonArgs, ArrayView<ulong>, ArrayView1D<ulong, Stride1D.Dense>,
|
||||||
|
ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<uint, Stride1D.Dense>,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2Mod;
|
||||||
|
|
||||||
|
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
||||||
|
ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>? IncrementalOrder;
|
||||||
|
|
||||||
|
public Action<AcceleratorStream, Index1D, ulong, GpuUInt128, GpuUInt128, byte, ulong,
|
||||||
|
ResidueAutomatonArgs, ArrayView<int>, ArrayView1D<ulong, Stride1D.Dense>>? Pow2ModOrder;
|
||||||
|
|
||||||
|
public Kernel? SmallPrimeFactor;
|
||||||
|
|
||||||
|
public Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>? SpecialMax;
|
||||||
|
|
||||||
|
// Optional device buffer with small divisor cycles (<= 4M). Index = divisor, value = cycle length.
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallCycles;
|
||||||
|
|
||||||
|
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastOne;
|
||||||
|
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimesLastSeven;
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2LastOne;
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimesPow2LastSeven;
|
||||||
|
|
||||||
|
public MemoryBuffer1D<uint, Stride1D.Dense>? SmallPrimeFactorsPrimes;
|
||||||
|
public MemoryBuffer1D<ulong, Stride1D.Dense>? SmallPrimeFactorsSquares;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Order = null;
|
||||||
|
// Incremental = null;
|
||||||
|
// Pow2Mod = null;
|
||||||
|
// IncrementalOrder = null;
|
||||||
|
// Pow2ModOrder = null;
|
||||||
|
// SmallPrimeFactor = null;
|
||||||
|
// SpecialMax = null;
|
||||||
|
|
||||||
|
// SmallCycles?.Dispose();
|
||||||
|
// SmallPrimesLastOne?.Dispose();
|
||||||
|
// SmallPrimesLastSeven?.Dispose();
|
||||||
|
// SmallPrimesPow2LastOne?.Dispose();
|
||||||
|
// SmallPrimesPow2LastSeven?.Dispose();
|
||||||
|
// SmallPrimeFactorsPrimes?.Dispose();
|
||||||
|
// SmallPrimeFactorsSquares?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum KernelType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
SmallCycles,
|
||||||
|
SmallPrimes,
|
||||||
|
Pow2ModKernelScan,
|
||||||
|
OrderKernelScan,
|
||||||
|
IncrementalKernelScan,
|
||||||
|
IncrementalOrderKernelScan,
|
||||||
|
Pow2ModOrderKernelScan
|
||||||
|
}
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Algorithms;
|
|
||||||
using ILGPU.Runtime;
|
|
||||||
using PerfectNumbers.Core;
|
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Algorithms;
|
using ILGPU.Algorithms;
|
||||||
using ILGPU.Runtime;
|
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Algorithms;
|
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
|
||||||
using UInt128 = System.UInt128;
|
using UInt128 = System.UInt128;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
@@ -305,7 +303,7 @@ internal static class LucasLehmerKernels
|
|||||||
states[idx] = s;
|
states[idx] = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Kernel(Index1D index, ulong exponent, GpuUInt128 modulus, ArrayView<GpuUInt128> state)
|
public static void Kernel(Index1D _, ulong exponent, GpuUInt128 modulus, ArrayView<GpuUInt128> state)
|
||||||
{
|
{
|
||||||
// Lucas–Lehmer iteration in the field GF(2^p-1).
|
// Lucas–Lehmer iteration in the field GF(2^p-1).
|
||||||
// For p < 128, use a Mersenne-specific squaring + reduction to avoid
|
// For p < 128, use a Mersenne-specific squaring + reduction to avoid
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ internal static class NttBarrettKernels
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Barrett-based squaring: v = v^2 mod n for 128-bit n
|
// Barrett-based squaring: v = v^2 mod n for 128-bit n
|
||||||
public static void SquareBarrett128Kernel(Index1D index, ArrayView<GpuUInt128> data, ulong modHigh, ulong modLow, ulong muHigh, ulong muLow)
|
public static void SquareBarrett128Kernel(Index1D index, AcceleratorStream stream, ArrayView<GpuUInt128> data, ulong modHigh, ulong modLow, ulong muHigh, ulong muLow)
|
||||||
{
|
{
|
||||||
var v = data[index];
|
var v = data[index];
|
||||||
Mul128Full(v.High, v.Low, v.High, v.Low, out var z3, out var z2, out var z1, out var z0);
|
Mul128Full(v.High, v.Low, v.High, v.Low, out var z3, out var z2, out var z1, out var z0);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ internal static class NttMontgomeryKernels
|
|||||||
data[index] = new GpuUInt128(0UL, v);
|
data[index] = new GpuUInt128(0UL, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SquareMont64Kernel(Index1D index, ArrayView<GpuUInt128> data, ulong modulus, ulong nPrime)
|
public static void SquareMont64Kernel(Index1D index, AcceleratorStream stream, ArrayView<GpuUInt128> data, ulong modulus, ulong nPrime)
|
||||||
{
|
{
|
||||||
ulong aR = data[index].Low;
|
ulong aR = data[index].Low;
|
||||||
ulong v = MontMul64(aR, aR, modulus, nPrime);
|
ulong v = MontMul64(aR, aR, modulus, nPrime);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Algorithms;
|
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|||||||
@@ -1,18 +1,26 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
using PerfectNumbers.Core;
|
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu.Kernels;
|
||||||
|
|
||||||
internal static class Pow2MontgomeryKernels
|
internal static class Pow2MontgomeryKernels
|
||||||
{
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static GpuUInt128 AdvancePolynomialGpu(GpuUInt128 x, ulong c, ulong modulus)
|
||||||
|
{
|
||||||
|
x.Pow2();
|
||||||
|
x.AddMod(c, modulus);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
public static void Pow2MontgomeryKernelKeepMontgomery(
|
public static void Pow2MontgomeryKernelKeepMontgomery(
|
||||||
Index1D index,
|
Index1D index,
|
||||||
ArrayView1D<ulong, Stride1D.Dense> exponents,
|
ArrayView1D<ulong, Stride1D.Dense> exponents,
|
||||||
MontgomeryDivisorData divisor,
|
MontgomeryDivisorData divisor,
|
||||||
ArrayView1D<ulong, Stride1D.Dense> results)
|
ArrayView1D<ulong, Stride1D.Dense> results)
|
||||||
{
|
{
|
||||||
results[index] = ULongExtensions.Pow2MontgomeryModWindowedGpuKeepMontgomery(divisor, exponents[index]);
|
results[0] = ULongExtensions.Pow2MontgomeryModWindowedGpuKeepMontgomery(divisor, exponents[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Pow2MontgomeryKernelConvertToStandard(
|
public static void Pow2MontgomeryKernelConvertToStandard(
|
||||||
@@ -21,6 +29,52 @@ internal static class Pow2MontgomeryKernels
|
|||||||
MontgomeryDivisorData divisor,
|
MontgomeryDivisorData divisor,
|
||||||
ArrayView1D<ulong, Stride1D.Dense> results)
|
ArrayView1D<ulong, Stride1D.Dense> results)
|
||||||
{
|
{
|
||||||
results[index] = ULongExtensions.Pow2MontgomeryModWindowedGpuConvertToStandard(divisor, exponents[index]);
|
results[0] = ULongExtensions.Pow2MontgomeryModWindowedGpuConvertToStandard(divisor, exponents[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void TryPollardRhoKernel(ulong n, int limit, ArrayView1D<ulong, Stride1D.Dense> randomState, ArrayView1D<byte, Stride1D.Dense> factored, ArrayView1D<ulong, Stride1D.Dense> factor)
|
||||||
|
{
|
||||||
|
factored[0] = 0;
|
||||||
|
if ((n & 1UL) == 0UL)
|
||||||
|
{
|
||||||
|
factor[0] = 2UL;
|
||||||
|
factored[0] = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int attempt = 0;
|
||||||
|
GpuUInt128 nGpuUInt128 = (GpuUInt128)n;
|
||||||
|
DeterministicRandomGpu random = new(randomState[0]);
|
||||||
|
while (attempt < limit)
|
||||||
|
{
|
||||||
|
ulong c = (random.NextUInt64() % (n - 1UL)) + 1UL;
|
||||||
|
GpuUInt128 x = (random.NextUInt64() % (n - 2UL)) + 2UL;
|
||||||
|
GpuUInt128 y = x;
|
||||||
|
GpuUInt128 d = GpuUInt128.One;
|
||||||
|
|
||||||
|
while (d.CompareTo(GpuUInt128.One) == 0)
|
||||||
|
{
|
||||||
|
x = AdvancePolynomialGpu(x, c, modulus: n);
|
||||||
|
y = AdvancePolynomialGpu(y, c, n);
|
||||||
|
y = AdvancePolynomialGpu(y, c, n);
|
||||||
|
GpuUInt128 diff = x.CompareTo(y) > 0 ? x - y : y - x;
|
||||||
|
d = GpuUInt128.BinaryGcd(diff, nGpuUInt128);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d != n)
|
||||||
|
{
|
||||||
|
if (d.High == 0)
|
||||||
|
{
|
||||||
|
factor[0] = d.Low;
|
||||||
|
factored[0] = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attempt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
randomState[0] = random.State;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,51 @@
|
|||||||
using System;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Algorithms.Random;
|
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
using PerfectNumbers.Core;
|
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
internal static partial class PrimeOrderGpuHeuristics
|
internal static partial class PrimeOrderGpuHeuristics
|
||||||
{
|
{
|
||||||
|
internal static void CheckFactorsKernel(
|
||||||
|
int count,
|
||||||
|
ulong phi,
|
||||||
|
ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense> toTest,
|
||||||
|
MontgomeryDivisorData divisorData,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> result
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ulong order = phi;
|
||||||
|
int entryCount = count;
|
||||||
|
|
||||||
|
for (int i = 0; i < entryCount; i++)
|
||||||
|
{
|
||||||
|
(ulong primeFactor, int exponent) = toTest[i];
|
||||||
|
|
||||||
|
for (int iteration = 0; iteration < exponent; iteration++)
|
||||||
|
{
|
||||||
|
ulong candidate = order / primeFactor;
|
||||||
|
ulong remainder = order - (candidate * primeFactor);
|
||||||
|
// ulong remainder = order % primeFactor;
|
||||||
|
if (remainder != 0UL)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ulong candidate = order / primeFactor;
|
||||||
|
if (ULongExtensions.Pow2MontgomeryModWindowedGpuConvertToStandard(divisorData, candidate) == 1UL)
|
||||||
|
{
|
||||||
|
order = candidate;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result[0] = order;
|
||||||
|
}
|
||||||
|
|
||||||
/// This kernel always sets the result and operates within the allowed / required bounds, never accessing elements outside of them.
|
/// This kernel always sets the result and operates within the allowed / required bounds, never accessing elements outside of them.
|
||||||
/// It sets the value only when it needs to. The callers don't need to clear the output / input buffers.
|
/// It sets the value only when it needs to. The callers don't need to clear the output / input buffers.
|
||||||
internal static void PartialFactorKernel(
|
internal static void PartialFactorKernel(
|
||||||
@@ -1170,16 +1206,6 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
remainders[index] = ULongExtensions.Pow2MontgomeryModWindowedGpuConvertToStandard(divisor, exponent);
|
remainders[index] = ULongExtensions.Pow2MontgomeryModWindowedGpuConvertToStandard(divisor, exponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Action<AcceleratorStream, Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>> GetPow2ModWideKernel(Accelerator accelerator)
|
|
||||||
{
|
|
||||||
return Pow2ModKernelWideCache.GetOrAdd(accelerator, static accel =>
|
|
||||||
{
|
|
||||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>>(Pow2ModKernelWide);
|
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
|
||||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>>>();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This kernel always sets the result of the corresponding element. Callers don't need to clear the output buffers.
|
/// This kernel always sets the result of the corresponding element. Callers don't need to clear the output buffers.
|
||||||
internal static void Pow2ModKernelWide(Index1D index, ArrayView1D<GpuUInt128, Stride1D.Dense> exponents, GpuUInt128 modulus, ArrayView1D<GpuUInt128, Stride1D.Dense> remainders)
|
internal static void Pow2ModKernelWide(Index1D index, ArrayView1D<GpuUInt128, Stride1D.Dense> exponents, GpuUInt128 modulus, ArrayView1D<GpuUInt128, Stride1D.Dense> remainders)
|
||||||
{
|
{
|
||||||
@@ -1367,5 +1393,4 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
ulong directMask = (1UL << windowBitCount) - 1UL;
|
ulong directMask = (1UL << windowBitCount) - 1UL;
|
||||||
return exponent.Low & directMask;
|
return exponent.Low & directMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
using ILGPU;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
|
internal static partial class PrimeOrderGpuHeuristics
|
||||||
|
{
|
||||||
|
internal static void EvaluateSpecialMaxCandidatesKernel(
|
||||||
|
Index1D index,
|
||||||
|
ulong phi,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> factors,
|
||||||
|
int factorCount,
|
||||||
|
MontgomeryDivisorData divisor,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> candidates,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> resultOut)
|
||||||
|
{
|
||||||
|
if (index != 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int actual = 0;
|
||||||
|
for (int i = 0; i < factorCount; i++)
|
||||||
|
{
|
||||||
|
ulong factor = factors[i];
|
||||||
|
if (factor <= 1UL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong reduced = phi / factor;
|
||||||
|
if (reduced == 0UL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidates[actual] = reduced;
|
||||||
|
actual++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actual == 0)
|
||||||
|
{
|
||||||
|
resultOut[0] = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < actual; i++)
|
||||||
|
{
|
||||||
|
ulong current = candidates[i];
|
||||||
|
int insert = i - 1;
|
||||||
|
while (insert >= 0 && candidates[insert] > current)
|
||||||
|
{
|
||||||
|
candidates[insert + 1] = candidates[insert];
|
||||||
|
insert--;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidates[insert + 1] = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
var divisorPartial = new GpuDivisorPartialData(divisor.Modulus);
|
||||||
|
var stepper = new ExponentRemainderStepperGpu(divisorPartial);
|
||||||
|
if (stepper.InitializeIsUnityGpu(candidates[0]))
|
||||||
|
{
|
||||||
|
Atomic.Exchange(ref resultOut[0], 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < actual; i++)
|
||||||
|
{
|
||||||
|
if (stepper.ComputeNextIsUnityGpu(candidates[i]))
|
||||||
|
{
|
||||||
|
Atomic.Exchange(ref resultOut[0], 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Atomic.Exchange(ref resultOut[0], 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,16 +2,16 @@ using System.Runtime.CompilerServices;
|
|||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Algorithms;
|
using ILGPU.Algorithms;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
internal enum HeuristicGpuDivisorTableKind : byte
|
public enum HeuristicGpuDivisorTableKind : byte
|
||||||
{
|
{
|
||||||
GroupA = 0,
|
GroupA = 0,
|
||||||
GroupBEnding1 = 1,
|
GroupBEnding1 = 1,
|
||||||
GroupBEnding7 = 7,
|
GroupBEnding7 = 7,
|
||||||
GroupBEnding9 = 9,
|
GroupBEnding9 = 9,
|
||||||
Combined = 255,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static class PrimeTesterKernels
|
internal static class PrimeTesterKernels
|
||||||
@@ -20,12 +20,10 @@ internal static class PrimeTesterKernels
|
|||||||
public static void SmallPrimeSieveKernel(
|
public static void SmallPrimeSieveKernel(
|
||||||
Index1D index,
|
Index1D index,
|
||||||
ArrayView<ulong> numbers,
|
ArrayView<ulong> numbers,
|
||||||
ArrayView<uint> smallPrimes,
|
|
||||||
ArrayView<uint> smallPrimesLastOne,
|
ArrayView<uint> smallPrimesLastOne,
|
||||||
ArrayView<uint> smallPrimesLastSeven,
|
ArrayView<uint> smallPrimesLastSeven,
|
||||||
ArrayView<uint> smallPrimesLastThree,
|
ArrayView<uint> smallPrimesLastThree,
|
||||||
ArrayView<uint> smallPrimesLastNine,
|
ArrayView<uint> smallPrimesLastNine,
|
||||||
ArrayView<ulong> smallPrimesPow2,
|
|
||||||
ArrayView<ulong> smallPrimesPow2LastOne,
|
ArrayView<ulong> smallPrimesPow2LastOne,
|
||||||
ArrayView<ulong> smallPrimesPow2LastSeven,
|
ArrayView<ulong> smallPrimesPow2LastSeven,
|
||||||
ArrayView<ulong> smallPrimesPow2LastThree,
|
ArrayView<ulong> smallPrimesPow2LastThree,
|
||||||
@@ -35,8 +33,8 @@ internal static class PrimeTesterKernels
|
|||||||
ulong n = numbers[index];
|
ulong n = numbers[index];
|
||||||
byte result = 1;
|
byte result = 1;
|
||||||
|
|
||||||
ArrayView<uint> primes = smallPrimes;
|
ArrayView<uint> primes;
|
||||||
ArrayView<ulong> primeSquares = smallPrimesPow2;
|
ArrayView<ulong> primeSquares;
|
||||||
ulong lastDigit = n % 10UL;
|
ulong lastDigit = n % 10UL;
|
||||||
switch (lastDigit)
|
switch (lastDigit)
|
||||||
{
|
{
|
||||||
@@ -56,6 +54,8 @@ internal static class PrimeTesterKernels
|
|||||||
primes = smallPrimesLastNine;
|
primes = smallPrimesLastNine;
|
||||||
primeSquares = smallPrimesPow2LastNine;
|
primeSquares = smallPrimesPow2LastNine;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int length = (int)primes.Length;
|
int length = (int)primes.Length;
|
||||||
@@ -78,17 +78,49 @@ internal static class PrimeTesterKernels
|
|||||||
results[index] = result;
|
results[index] = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void HeuristicTrialDivisionKernel(
|
public static void HeuristicTrialCombinedDivisionKernel(
|
||||||
|
Index1D index,
|
||||||
|
byte nMod10,
|
||||||
|
ArrayView<int> resultFlag,
|
||||||
|
ulong n,
|
||||||
|
ulong maxDivisorSquare,
|
||||||
|
HeuristicCombinedGpuViews tables)
|
||||||
|
{
|
||||||
|
// byte nMod10 = (byte)(n % 10UL);
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> divisors = tables.SelectDivisors(nMod10);
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> divisorSquares = tables.SelectDivisorSquares(nMod10);
|
||||||
|
|
||||||
|
int divisorLength = (int)divisorSquares.Length;
|
||||||
|
int threadIndex = index;
|
||||||
|
if (threadIndex >= divisorLength)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong divisorSquare = divisorSquares[threadIndex];
|
||||||
|
if (divisorSquare > maxDivisorSquare)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong divisor = divisors[threadIndex];
|
||||||
|
divisor = n % divisor;
|
||||||
|
if (divisor == 0UL)
|
||||||
|
{
|
||||||
|
resultFlag[0] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HeuristicTrialGroupABDivisionKernel(
|
||||||
Index1D index,
|
Index1D index,
|
||||||
ArrayView<int> resultFlag,
|
ArrayView<int> resultFlag,
|
||||||
ulong n,
|
ulong n,
|
||||||
ulong maxDivisorSquare,
|
ulong maxDivisorSquare,
|
||||||
HeuristicGpuDivisorTableKind tableKind,
|
HeuristicGpuDivisorTableKind tableKind,
|
||||||
HeuristicGpuDivisorTables tables)
|
HeuristicGroupABGpuViews tables)
|
||||||
{
|
{
|
||||||
byte nMod10 = (byte)(n % 10UL);
|
ArrayView1D<ulong, Stride1D.Dense> divisors = tables.SelectDivisors(tableKind);
|
||||||
ArrayView1D<ulong, Stride1D.Dense> divisors = tables.SelectDivisors(tableKind, nMod10);
|
ArrayView1D<ulong, Stride1D.Dense> divisorSquares = tables.SelectDivisorSquares(tableKind);
|
||||||
ArrayView1D<ulong, Stride1D.Dense> divisorSquares = tables.SelectDivisorSquares(tableKind, nMod10);
|
|
||||||
|
|
||||||
int divisorLength = (int)divisorSquares.Length;
|
int divisorLength = (int)divisorSquares.Length;
|
||||||
int threadIndex = index;
|
int threadIndex = index;
|
||||||
@@ -121,35 +153,37 @@ internal static class PrimeTesterKernels
|
|||||||
|
|
||||||
private static ulong BinaryGcdGpu(ulong u, ulong v)
|
private static ulong BinaryGcdGpu(ulong u, ulong v)
|
||||||
{
|
{
|
||||||
// TODO: Replace this inline GPU binary GCD with the kernel extracted from
|
|
||||||
// GpuUInt128BinaryGcdBenchmarks via GpuKernelPool so device callers reuse the
|
|
||||||
// fully unrolled ladder instead of this branchy fallback.
|
|
||||||
bool uIsZero = u == 0UL;
|
bool uIsZero = u == 0UL;
|
||||||
bool vIsZero = v == 0UL;
|
bool vIsZero = v == 0UL;
|
||||||
bool eitherZero = uIsZero | vIsZero;
|
bool eitherZero = uIsZero | vIsZero;
|
||||||
ulong zeroResult = uIsZero ? v : u;
|
ulong zeroResult = uIsZero ? v : u;
|
||||||
|
|
||||||
ulong combined = u | v | 1UL;
|
ulong combined = (u | v) | 1UL;
|
||||||
int shift = XMath.TrailingZeroCount(combined);
|
int shift = XMath.TrailingZeroCount(combined);
|
||||||
|
ulong currentU = u >> XMath.TrailingZeroCount(u | 1UL);
|
||||||
|
ulong currentV = v >> XMath.TrailingZeroCount(v | 1UL);
|
||||||
|
|
||||||
ulong normalizedU = u >> XMath.TrailingZeroCount(u | 1UL);
|
while (true)
|
||||||
ulong normalizedV = v >> XMath.TrailingZeroCount(v | 1UL);
|
|
||||||
|
|
||||||
ulong currentU = normalizedU;
|
|
||||||
ulong currentV = normalizedV;
|
|
||||||
bool loopCondition = currentV != 0UL;
|
|
||||||
|
|
||||||
while (loopCondition)
|
|
||||||
{
|
{
|
||||||
currentV >>= XMath.TrailingZeroCount(currentV);
|
currentV >>= XMath.TrailingZeroCount(currentV);
|
||||||
ulong minValue = XMath.Min(currentU, currentV);
|
bool swap = currentU > currentV;
|
||||||
ulong maxValue = XMath.Max(currentU, currentV);
|
ulong minValue = ConditionalSelect(currentU, currentV, swap);
|
||||||
currentU = minValue;
|
ulong maxValue = ConditionalSelect(currentV, currentU, swap);
|
||||||
currentV = maxValue - minValue;
|
currentV = maxValue - minValue;
|
||||||
loopCondition = currentV != 0UL;
|
currentU = minValue;
|
||||||
}
|
if (currentV == 0UL)
|
||||||
|
{
|
||||||
ulong gcd = currentU << shift;
|
ulong gcd = currentU << shift;
|
||||||
return eitherZero ? zeroResult : gcd;
|
return eitherZero ? zeroResult : gcd;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static ulong ConditionalSelect(ulong left, ulong right, bool useRight)
|
||||||
|
{
|
||||||
|
ulong mask = useRight ? ulong.MaxValue : 0UL;
|
||||||
|
return left ^ ((left ^ right) & mask);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
using System;
|
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
|
||||||
using PerfectNumbers.Core;
|
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
@@ -23,15 +21,26 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
private ulong _divisorLimit;
|
private ulong _divisorLimit;
|
||||||
// private bool _isConfigured;
|
// private bool _isConfigured;
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>> _kernelCache = new(AcceleratorReferenceComparer.Instance);
|
[ThreadStatic]
|
||||||
private readonly ConcurrentBag<BatchResources> _resourcePool = new();
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>>? _kernel;
|
||||||
private readonly ConcurrentBag<GpuContextPool.GpuContextLease> _acceleratorPool = new();
|
|
||||||
private readonly ConcurrentBag<DivisorScanSession> _sessionPool = new();
|
|
||||||
|
|
||||||
private Action<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> GetKernel(Accelerator accelerator) =>
|
private readonly ConcurrentBag<BatchResources> _resourcePool = [];
|
||||||
_kernelCache.GetOrAdd(accelerator, acc =>
|
private readonly ConcurrentBag<DivisorScanSession> _sessionPool = [];
|
||||||
acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>(DivisorByDivisorKernels.CheckKernel));
|
|
||||||
|
|
||||||
|
private static Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> GetKernel(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
var pool = _kernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>(DivisorByDivisorKernels.CheckKernel);
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>>();
|
||||||
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
public int GpuBatchSize
|
public int GpuBatchSize
|
||||||
{
|
{
|
||||||
@@ -59,7 +68,9 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
// _isConfigured = true;
|
// _isConfigured = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPrime(ulong prime, out bool divisorsExhausted)
|
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
|
||||||
|
public bool IsPrime(ulong prime, out bool divisorsExhausted, out ulong divisor)
|
||||||
{
|
{
|
||||||
ulong allowedMax;
|
ulong allowedMax;
|
||||||
int batchCapacity;
|
int batchCapacity;
|
||||||
@@ -91,8 +102,13 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
ulong processedCount;
|
ulong processedCount;
|
||||||
ulong lastProcessed;
|
ulong lastProcessed;
|
||||||
|
|
||||||
var gpuLease = RentAccelerator();
|
// GpuPrimeWorkLimiter();
|
||||||
var accelerator = gpuLease.Accelerator;
|
// int acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
|
// var accelerator = _accelerators[acceleratorIndex];
|
||||||
|
var gpu = PrimeOrderCalculatorAccelerator.Rent(batchCapacity);
|
||||||
|
var acceleratorIndex = gpu.AcceleratorIndex;
|
||||||
|
var accelerator = gpu.Accelerator;
|
||||||
|
var stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
|
|
||||||
// Monitor.Enter(gpuLease.ExecutionLock);
|
// Monitor.Enter(gpuLease.ExecutionLock);
|
||||||
|
|
||||||
@@ -100,9 +116,11 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
var resources = RentBatchResources(accelerator, batchCapacity);
|
var resources = RentBatchResources(accelerator, batchCapacity);
|
||||||
|
|
||||||
composite = CheckDivisors(
|
composite = CheckDivisors(
|
||||||
|
gpu,
|
||||||
prime,
|
prime,
|
||||||
allowedMax,
|
allowedMax,
|
||||||
kernel,
|
kernel,
|
||||||
|
stream,
|
||||||
resources.DivisorDataBuffer,
|
resources.DivisorDataBuffer,
|
||||||
resources.OffsetBuffer,
|
resources.OffsetBuffer,
|
||||||
resources.CountBuffer,
|
resources.CountBuffer,
|
||||||
@@ -120,17 +138,21 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
out coveredRange,
|
out coveredRange,
|
||||||
out processedCount);
|
out processedCount);
|
||||||
|
|
||||||
|
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||||
ReturnBatchResources(resources);
|
ReturnBatchResources(resources);
|
||||||
|
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||||
// Monitor.Exit(gpuLease.ExecutionLock);
|
// Monitor.Exit(gpuLease.ExecutionLock);
|
||||||
ReturnAccelerator(gpuLease);
|
// GpuPrimeWorkLimiter.Release();
|
||||||
|
|
||||||
if (composite)
|
if (composite)
|
||||||
{
|
{
|
||||||
divisorsExhausted = true;
|
divisorsExhausted = true;
|
||||||
|
divisor = lastProcessed;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
divisorsExhausted = coveredRange;
|
divisorsExhausted = coveredRange;
|
||||||
|
divisor = 0UL;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,9 +186,11 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static bool CheckDivisors(
|
private static bool CheckDivisors(
|
||||||
|
PrimeOrderCalculatorAccelerator gpu,
|
||||||
ulong prime,
|
ulong prime,
|
||||||
ulong allowedMax,
|
ulong allowedMax,
|
||||||
Action<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> kernel,
|
Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> kernel,
|
||||||
|
AcceleratorStream stream,
|
||||||
MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense> divisorDataBuffer,
|
MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense> divisorDataBuffer,
|
||||||
MemoryBuffer1D<int, Stride1D.Dense> offsetBuffer,
|
MemoryBuffer1D<int, Stride1D.Dense> offsetBuffer,
|
||||||
MemoryBuffer1D<int, Stride1D.Dense> countBuffer,
|
MemoryBuffer1D<int, Stride1D.Dense> countBuffer,
|
||||||
@@ -186,7 +210,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
{
|
{
|
||||||
int batchCapacity = (int)divisorDataBuffer.Length;
|
int batchCapacity = (int)divisorDataBuffer.Length;
|
||||||
bool composite = false;
|
bool composite = false;
|
||||||
bool processedAll = false;
|
bool processedAll;
|
||||||
processedCount = 0UL;
|
processedCount = 0UL;
|
||||||
lastProcessed = 0UL;
|
lastProcessed = 0UL;
|
||||||
|
|
||||||
@@ -284,7 +308,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
{
|
{
|
||||||
ulong divisorValue = (ulong)nextDivisor128;
|
ulong divisorValue = (ulong)nextDivisor128;
|
||||||
MontgomeryDivisorData montgomeryData = MontgomeryDivisorData.FromModulus(divisorValue);
|
MontgomeryDivisorData montgomeryData = MontgomeryDivisorData.FromModulus(divisorValue);
|
||||||
ulong divisorCycle = ResolveDivisorCycle(divisorValue, prime, in montgomeryData);
|
ulong divisorCycle = ResolveDivisorCycle(gpu, divisorValue, prime, in montgomeryData);
|
||||||
if (divisorCycle == prime)
|
if (divisorCycle == prime)
|
||||||
{
|
{
|
||||||
processedCount += (ulong)(i + 1);
|
processedCount += (ulong)(i + 1);
|
||||||
@@ -328,19 +352,20 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
divisorDataView.CopyFromCPU(ref divisorDataRef, admissibleCount);
|
divisorDataView.CopyFromCPU(stream, ref divisorDataRef, admissibleCount);
|
||||||
offsetView.CopyFromCPU(ref offsetRef, admissibleCount);
|
offsetView.CopyFromCPU(stream, ref offsetRef, admissibleCount);
|
||||||
countView.CopyFromCPU(ref countRef, admissibleCount);
|
countView.CopyFromCPU(stream, ref countRef, admissibleCount);
|
||||||
cycleView.CopyFromCPU(ref cycleRef, admissibleCount);
|
cycleView.CopyFromCPU(stream, ref cycleRef, admissibleCount);
|
||||||
exponentViewDevice.CopyFromCPU(ref exponentRef, admissibleCount);
|
exponentViewDevice.CopyFromCPU(stream, ref exponentRef, admissibleCount);
|
||||||
|
|
||||||
int sentinel = int.MaxValue;
|
int sentinel = int.MaxValue;
|
||||||
hitIndexView.CopyFromCPU(ref sentinel, 1);
|
hitIndexView.CopyFromCPU(stream, ref sentinel, 1);
|
||||||
|
|
||||||
kernel(admissibleCount, divisorDataView, offsetView, countView, exponentViewDevice, cycleView, hitsView, hitIndexView);
|
kernel(stream, new Index1D(admissibleCount), divisorDataView, offsetView, countView, exponentViewDevice, cycleView, hitsView, hitIndexView);
|
||||||
|
|
||||||
int firstHit = sentinel;
|
int firstHit = sentinel;
|
||||||
hitIndexView.CopyToCPU(ref firstHit, 1);
|
hitIndexView.CopyToCPU(stream, ref firstHit, 1);
|
||||||
|
stream.Synchronize();
|
||||||
int hitIndex = firstHit >= admissibleCount ? -1 : firstHit;
|
int hitIndex = firstHit >= admissibleCount ? -1 : firstHit;
|
||||||
bool hitFound = hitIndex >= 0;
|
bool hitFound = hitIndex >= 0;
|
||||||
composite = hitFound;
|
composite = hitFound;
|
||||||
@@ -355,9 +380,9 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static ulong ResolveDivisorCycle(ulong divisor, ulong prime, in MontgomeryDivisorData divisorData)
|
private static ulong ResolveDivisorCycle(PrimeOrderCalculatorAccelerator gpu, ulong divisor, ulong prime, in MontgomeryDivisorData divisorData)
|
||||||
{
|
{
|
||||||
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(divisor, prime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL)
|
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(gpu, divisor, prime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL)
|
||||||
{
|
{
|
||||||
return MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed);
|
return MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed);
|
||||||
}
|
}
|
||||||
@@ -476,9 +501,10 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
public sealed class DivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
|
public sealed class DivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
|
||||||
{
|
{
|
||||||
private readonly MersenneNumberDivisorByDivisorGpuTester _owner;
|
private readonly MersenneNumberDivisorByDivisorGpuTester _owner;
|
||||||
private readonly GpuContextPool.GpuContextLease _lease;
|
private readonly PrimeOrderCalculatorAccelerator _primeTesterAccelerator;
|
||||||
private readonly Accelerator _accelerator;
|
private readonly Accelerator _accelerator;
|
||||||
private Action<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> _kernel = null!;
|
private readonly AcceleratorStream _stream;
|
||||||
|
private Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> _kernel = null!;
|
||||||
private MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense> _divisorBuffer = null!;
|
private MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense> _divisorBuffer = null!;
|
||||||
private MemoryBuffer1D<int, Stride1D.Dense> _offsetBuffer = null!;
|
private MemoryBuffer1D<int, Stride1D.Dense> _offsetBuffer = null!;
|
||||||
private MemoryBuffer1D<int, Stride1D.Dense> _countBuffer = null!;
|
private MemoryBuffer1D<int, Stride1D.Dense> _countBuffer = null!;
|
||||||
@@ -489,11 +515,15 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
private ulong[] _hostBuffer = null!;
|
private ulong[] _hostBuffer = null!;
|
||||||
private int _capacity;
|
private int _capacity;
|
||||||
|
|
||||||
|
|
||||||
internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner)
|
internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner)
|
||||||
{
|
{
|
||||||
_owner = owner;
|
_owner = owner;
|
||||||
_lease = GpuContextPool.Rent();
|
// GpuPrimeWorkLimiter.Acquire();
|
||||||
_accelerator = _lease.Accelerator;
|
_primeTesterAccelerator = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||||
|
Accelerator accelerator = _primeTesterAccelerator.Accelerator;
|
||||||
|
_accelerator = accelerator;
|
||||||
|
_stream = accelerator.CreateStream();
|
||||||
_capacity = Math.Max(1, owner._gpuBatchSize);
|
_capacity = Math.Max(1, owner._gpuBatchSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,18 +533,23 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
private void EnsureExecutionResourcesLocked(int requiredCapacity)
|
private void EnsureExecutionResourcesLocked(int requiredCapacity)
|
||||||
{
|
{
|
||||||
|
Accelerator accelerator = _accelerator;
|
||||||
|
|
||||||
if (_kernel == null)
|
if (_kernel == null)
|
||||||
{
|
{
|
||||||
_kernel = _owner.GetKernel(_accelerator);
|
_kernel = GetKernel(_accelerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_divisorBuffer == null)
|
if (_divisorBuffer == null)
|
||||||
{
|
{
|
||||||
_divisorBuffer = _accelerator.Allocate1D<GpuDivisorPartialData>(1);
|
lock(accelerator)
|
||||||
_offsetBuffer = _accelerator.Allocate1D<int>(1);
|
{
|
||||||
_countBuffer = _accelerator.Allocate1D<int>(1);
|
_divisorBuffer = accelerator.Allocate1D<GpuDivisorPartialData>(1);
|
||||||
_cycleBuffer = _accelerator.Allocate1D<ulong>(1);
|
_offsetBuffer = accelerator.Allocate1D<int>(1);
|
||||||
_firstHitBuffer = _accelerator.Allocate1D<int>(1);
|
_countBuffer = accelerator.Allocate1D<int>(1);
|
||||||
|
_cycleBuffer = accelerator.Allocate1D<ulong>(1);
|
||||||
|
_firstHitBuffer = accelerator.Allocate1D<int>(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool allocateBuffers = _exponentsBuffer == null;
|
bool allocateBuffers = _exponentsBuffer == null;
|
||||||
@@ -537,9 +572,13 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
{
|
{
|
||||||
int desiredCapacity = requiredCapacity > _capacity ? requiredCapacity : _capacity;
|
int desiredCapacity = requiredCapacity > _capacity ? requiredCapacity : _capacity;
|
||||||
_capacity = desiredCapacity;
|
_capacity = desiredCapacity;
|
||||||
_exponentsBuffer = _accelerator.Allocate1D<ulong>(_capacity);
|
lock (accelerator)
|
||||||
_hitBuffer = _accelerator.Allocate1D<byte>(_capacity);
|
{
|
||||||
_hostBuffer = ThreadStaticPools.UlongPool.Rent(_capacity);
|
_exponentsBuffer = accelerator.Allocate1D<ulong>(desiredCapacity);
|
||||||
|
_hitBuffer = accelerator.Allocate1D<byte>(desiredCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
_hostBuffer = ThreadStaticPools.UlongPool.Rent(desiredCapacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,7 +604,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
if (cycle == 0UL)
|
if (cycle == 0UL)
|
||||||
{
|
{
|
||||||
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(divisor, firstPrime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL)
|
var gpu = _primeTesterAccelerator;
|
||||||
|
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(gpu, divisor, firstPrime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL)
|
||||||
{
|
{
|
||||||
cycle = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed);
|
cycle = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed);
|
||||||
}
|
}
|
||||||
@@ -592,39 +632,41 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
ArrayView1D<ulong, Stride1D.Dense> exponentView = _exponentsBuffer.View;
|
ArrayView1D<ulong, Stride1D.Dense> exponentView = _exponentsBuffer.View;
|
||||||
ArrayView1D<byte, Stride1D.Dense> hitView = _hitBuffer.View;
|
ArrayView1D<byte, Stride1D.Dense> hitView = _hitBuffer.View;
|
||||||
|
|
||||||
|
AcceleratorStream stream = _stream;
|
||||||
GpuDivisorPartialData partialData = new GpuDivisorPartialData(divisor);
|
GpuDivisorPartialData partialData = new GpuDivisorPartialData(divisor);
|
||||||
divisorView.CopyFromCPU(ref partialData, 1);
|
divisorView.CopyFromCPU(stream, ref partialData, 1);
|
||||||
|
|
||||||
int offsetValue = 0;
|
int offsetValue = 0;
|
||||||
offsetView.CopyFromCPU(ref offsetValue, 1);
|
offsetView.CopyFromCPU(stream, ref offsetValue, 1);
|
||||||
|
|
||||||
int countValue = length;
|
int countValue = length;
|
||||||
countView.CopyFromCPU(ref countValue, 1);
|
countView.CopyFromCPU(stream, ref countValue, 1);
|
||||||
|
|
||||||
ulong cycleValue = cycle;
|
ulong cycleValue = cycle;
|
||||||
cycleView.CopyFromCPU(ref cycleValue, 1);
|
cycleView.CopyFromCPU(stream, ref cycleValue, 1);
|
||||||
|
|
||||||
Span<ulong> hostSpan = _hostBuffer.AsSpan(0, length);
|
Span<ulong> hostSpan = _hostBuffer.AsSpan(0, length);
|
||||||
primes.CopyTo(hostSpan);
|
primes.CopyTo(hostSpan);
|
||||||
ref ulong hostRef = ref MemoryMarshal.GetReference(hostSpan);
|
ref ulong hostRef = ref MemoryMarshal.GetReference(hostSpan);
|
||||||
|
|
||||||
exponentView.CopyFromCPU(ref hostRef, length);
|
exponentView.CopyFromCPU(stream, ref hostRef, length);
|
||||||
|
|
||||||
int sentinel = int.MaxValue;
|
int sentinel = int.MaxValue;
|
||||||
firstHitView.CopyFromCPU(ref sentinel, 1);
|
firstHitView.CopyFromCPU(stream, ref sentinel, 1);
|
||||||
|
|
||||||
var kernel = _kernel!;
|
var kernel = _kernel!;
|
||||||
|
|
||||||
kernel(1, divisorView, offsetView, countView, exponentView, cycleView, hitView, firstHitView);
|
kernel(stream, new Index1D(1), divisorView, offsetView, countView, exponentView, cycleView, hitView, firstHitView);
|
||||||
|
|
||||||
Span<byte> hitSlice = hits.Slice(0, length);
|
Span<byte> hitSlice = hits.Slice(0, length);
|
||||||
ref byte hitRef = ref MemoryMarshal.GetReference(hitSlice);
|
ref byte hitRef = ref MemoryMarshal.GetReference(hitSlice);
|
||||||
hitView.CopyToCPU(ref hitRef, length);
|
hitView.CopyToCPU(stream, ref hitRef, length);
|
||||||
|
stream.Synchronize();
|
||||||
|
|
||||||
// Monitor.Exit(_lease.ExecutionLock);
|
// Monitor.Exit(_lease.ExecutionLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Return()
|
||||||
{
|
{
|
||||||
_owner._sessionPool.Add(this);
|
_owner._sessionPool.Add(this);
|
||||||
}
|
}
|
||||||
@@ -650,17 +692,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private GpuContextPool.GpuContextLease RentAccelerator()
|
|
||||||
{
|
|
||||||
if (_acceleratorPool.TryTake(out var lease))
|
|
||||||
{
|
|
||||||
return lease;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GpuContextPool.Rent();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReturnAccelerator(GpuContextPool.GpuContextLease lease) => _acceleratorPool.Add(lease);
|
|
||||||
|
|
||||||
private BatchResources RentBatchResources(Accelerator accelerator, int capacity)
|
private BatchResources RentBatchResources(Accelerator accelerator, int capacity)
|
||||||
{
|
{
|
||||||
@@ -767,6 +799,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void AllocateDeviceBuffers(Accelerator accelerator, int capacity)
|
private void AllocateDeviceBuffers(Accelerator accelerator, int capacity)
|
||||||
|
{
|
||||||
|
lock (accelerator)
|
||||||
{
|
{
|
||||||
_divisorDataBuffer = accelerator.Allocate1D<GpuDivisorPartialData>(capacity);
|
_divisorDataBuffer = accelerator.Allocate1D<GpuDivisorPartialData>(capacity);
|
||||||
_offsetBuffer = accelerator.Allocate1D<int>(capacity);
|
_offsetBuffer = accelerator.Allocate1D<int>(capacity);
|
||||||
@@ -775,6 +809,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
_exponentBuffer = accelerator.Allocate1D<ulong>(capacity);
|
_exponentBuffer = accelerator.Allocate1D<ulong>(capacity);
|
||||||
_hitsBuffer = accelerator.Allocate1D<byte>(capacity);
|
_hitsBuffer = accelerator.Allocate1D<byte>(capacity);
|
||||||
_hitIndexBuffer = accelerator.Allocate1D<int>(1);
|
_hitIndexBuffer = accelerator.Allocate1D<int>(1);
|
||||||
|
}
|
||||||
|
|
||||||
_deviceCapacity = capacity;
|
_deviceCapacity = capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -900,7 +936,3 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
IMersenneNumberDivisorByDivisorTester.IDivisorScanSession IMersenneNumberDivisorByDivisorTester.CreateDivisorSession() => CreateDivisorSession();
|
IMersenneNumberDivisorByDivisorTester.IDivisorScanSession IMersenneNumberDivisorByDivisorTester.CreateDivisorSession() => CreateDivisorSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Collections.Concurrent;
|
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
@@ -9,11 +8,29 @@ namespace PerfectNumbers.Core.Gpu;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class MersenneNumberDivisorGpuTester
|
public sealed class MersenneNumberDivisorGpuTester
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ReadOnlyGpuUInt128, ArrayView<byte>>> _kernelCache = new();
|
[ThreadStatic]
|
||||||
private readonly ConcurrentDictionary<Accelerator, MemoryBuffer1D<byte, Stride1D.Dense>> _resultBuffers = new();
|
private static Dictionary<Accelerator, Queue<MemoryBuffer1D<byte, Stride1D.Dense>>>? _resultBuffers;
|
||||||
|
|
||||||
private Action<Index1D, ulong, ReadOnlyGpuUInt128, ArrayView<byte>> GetKernel(Accelerator accelerator) =>
|
private static Queue<MemoryBuffer1D<byte, Stride1D.Dense>> GetQueue(Accelerator accelerator)
|
||||||
_kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, ReadOnlyGpuUInt128, ArrayView<byte>>(DivisorKernels.Kernel));
|
{
|
||||||
|
var pool = _resultBuffers ??= [];
|
||||||
|
if (!pool.TryGetValue(accelerator, out var bufferQueue))
|
||||||
|
{
|
||||||
|
bufferQueue = [];
|
||||||
|
pool[accelerator] = bufferQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bufferQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Action<AcceleratorStream, Index1D, ulong, ReadOnlyGpuUInt128, ArrayView<byte>> GetDivisorKernel(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, ReadOnlyGpuUInt128, ArrayView<byte>>(DivisorKernels.Kernel);
|
||||||
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ReadOnlyGpuUInt128, ArrayView<byte>>>();
|
||||||
|
}
|
||||||
|
|
||||||
public static void BuildDivisorCandidates()
|
public static void BuildDivisorCandidates()
|
||||||
{
|
{
|
||||||
@@ -35,27 +52,39 @@ public sealed class MersenneNumberDivisorGpuTester
|
|||||||
_divisorCandidates = count == 0 ? [] : list[..count];
|
_divisorCandidates = count == 0 ? [] : list[..count];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
|
||||||
public bool IsDivisible(ulong exponent, in ReadOnlyGpuUInt128 divisor)
|
public bool IsDivisible(ulong exponent, in ReadOnlyGpuUInt128 divisor)
|
||||||
{
|
{
|
||||||
var gpu = GpuContextPool.Rent();
|
// var accelerator = SharedGpuContext.CreateAccelerator();
|
||||||
var accelerator = gpu.Accelerator;
|
var acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
var kernel = GetKernel(accelerator);
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
var resultBuffer = _resultBuffers.GetOrAdd(accelerator, acc => acc.Allocate1D<byte>(1));
|
var stream = accelerator.CreateStream();
|
||||||
|
Queue<MemoryBuffer1D<byte, Stride1D.Dense>> resultBuffers = GetQueue(accelerator);
|
||||||
|
if (!resultBuffers.TryDequeue(out var resultBuffer))
|
||||||
|
{
|
||||||
|
// lock(accelerator)
|
||||||
|
{
|
||||||
|
resultBuffer = accelerator.Allocate1D<byte>(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// There is no point in clearing this buffer. We always override item [0] and never use it beyond item [0]
|
// There is no point in clearing this buffer. We always override item [0] and never use it beyond item [0]
|
||||||
// resultBuffer.MemSetToZero();
|
// resultBuffer.MemSetToZero(stream);
|
||||||
kernel(1, exponent, divisor, resultBuffer.View);
|
var divisorKernel = GetDivisorKernel(accelerator);
|
||||||
accelerator.Synchronize();
|
divisorKernel(stream, 1, exponent, divisor, resultBuffer.View);
|
||||||
Span<byte> result = stackalloc byte[1];
|
byte result = 0;
|
||||||
resultBuffer.View.CopyToCPU(ref result[0], 1);
|
resultBuffer.View.CopyToCPU(stream, ref result, 1);
|
||||||
bool divisible = result[0] != 0;
|
stream.Synchronize();
|
||||||
result[0] = 0;
|
bool divisible = result != 0;
|
||||||
resultBuffer.View.CopyFromCPU(ref result[0], 1);
|
resultBuffers.Enqueue(resultBuffer);
|
||||||
accelerator.Synchronize();
|
stream.Dispose();
|
||||||
gpu.Dispose();
|
// AcceleratorPool.Shared.Return(accelerator);
|
||||||
|
// accelerator.Dispose();
|
||||||
return divisible;
|
return divisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (ulong divisor, uint cycle)[]? _divisorCandidates = Array.Empty<(ulong divisor, uint cycle)>();
|
private static (ulong divisor, uint cycle)[]? _divisorCandidates = [];
|
||||||
|
|
||||||
public bool IsPrime(ulong p, UInt128 d, ulong divisorCyclesSearchLimit, out bool divisorsExhausted)
|
public bool IsPrime(ulong p, UInt128 d, ulong divisorCyclesSearchLimit, out bool divisorsExhausted)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,36 +12,41 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
|
|||||||
private readonly GpuKernelType _kernelType = kernelType;
|
private readonly GpuKernelType _kernelType = kernelType;
|
||||||
private readonly bool _useGpuOrder = useGpuOrder;
|
private readonly bool _useGpuOrder = useGpuOrder;
|
||||||
|
|
||||||
|
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
|
||||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException($"GPU incremental scanning requires the device cycle heuristics implementation (kernel: {_kernelType}, GPU order: {_useGpuOrder}).");
|
throw new NotImplementedException($"GPU incremental scanning requires the device cycle heuristics implementation (kernel: {_kernelType}, GPU order: {_useGpuOrder}).");
|
||||||
|
|
||||||
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
|
// GpuPrimeWorkLimiter.Acquire();
|
||||||
var accelerator = gpuLease.Accelerator;
|
var acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
var stream = gpuLease.Stream;
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
|
var stream = accelerator.CreateStream();
|
||||||
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy
|
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy
|
||||||
UInt128 kStart = 1UL;
|
UInt128 kStart = 1UL;
|
||||||
ulong divMul = (ulong)((((UInt128)1 << 64) - UInt128.One) / exponent) + 1UL;
|
ulong divMul = (ulong)((((UInt128)1 << 64) - UInt128.One) / exponent) + 1UL;
|
||||||
byte last = lastDigit == LastDigit.Seven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
|
byte last = lastDigit == LastDigit.Seven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
|
||||||
|
|
||||||
var pow2Kernel = gpuLease.Pow2ModKernel;
|
KernelContainer kernels = GpuKernelPool.GetOrAddKernels(acceleratorIndex, stream, KernelType.Pow2ModKernelScan | KernelType.IncrementalKernelScan);
|
||||||
var incKernel = gpuLease.IncrementalKernel;
|
var pow2Kernel = kernels.Pow2Mod!;
|
||||||
|
var incKernel = kernels.Incremental!;
|
||||||
ulong step10 = (exponent.Mod10() << 1).Mod10();
|
ulong step10 = (exponent.Mod10() << 1).Mod10();
|
||||||
ulong step8 = ((exponent & 7UL) << 1) & 7UL;
|
ulong step8 = ((exponent & 7UL) << 1) & 7UL;
|
||||||
ulong step3 = ((exponent % 3UL) << 1) % 3UL;
|
ulong step3 = ((exponent % 3UL) << 1) % 3UL;
|
||||||
ulong step5 = ((exponent % 5UL) << 1) % 5UL;
|
ulong step5 = ((exponent % 5UL) << 1) % 5UL;
|
||||||
GpuUInt128 twoPGpu = (GpuUInt128)twoP;
|
GpuUInt128 twoPGpu = (GpuUInt128)twoP;
|
||||||
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
|
var smallCyclesView = GpuKernelPool.GetSmallCyclesOnDevice(kernels);
|
||||||
ResiduePrimeViews primeViews = default;
|
ResiduePrimeViews primeViews = default;
|
||||||
if (_kernelType == GpuKernelType.Pow2Mod)
|
if (_kernelType == GpuKernelType.Pow2Mod)
|
||||||
{
|
{
|
||||||
primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(accelerator);
|
primeViews = GpuKernelPool.GetSmallPrimesOnDevice(kernels);
|
||||||
}
|
}
|
||||||
|
|
||||||
var orderBuffer = accelerator.Allocate1D<ulong>(batchSize);
|
var orderBuffer = accelerator.Allocate1D<ulong>(batchSize);
|
||||||
// Avoid giant stack allocations that can trigger StackOverflow when batchSize is large.
|
// Avoid giant stack allocations that can trigger StackOverflow when batchSize is large.
|
||||||
// Rent a reusable array from the shared pool instead.
|
// Rent a reusable array from the shared pool instead.
|
||||||
ulong[] orderArray = ArrayPool<ulong>.Shared.Rent(batchSize);
|
ArrayPool<ulong> ulongPool = ThreadStaticPools.UlongPool;
|
||||||
|
ulong[] orderArray = ulongPool.Rent(batchSize);
|
||||||
UInt128 remaining;
|
UInt128 remaining;
|
||||||
int currentSize;
|
int currentSize;
|
||||||
int i;
|
int i;
|
||||||
@@ -92,8 +97,8 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
|
|||||||
smallCyclesView);
|
smallCyclesView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orderBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(orders), currentSize);
|
||||||
stream.Synchronize();
|
stream.Synchronize();
|
||||||
orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize);
|
|
||||||
if (_kernelType == GpuKernelType.Pow2Mod)
|
if (_kernelType == GpuKernelType.Pow2Mod)
|
||||||
{
|
{
|
||||||
for (i = 0; i < currentSize; i++)
|
for (i = 0; i < currentSize; i++)
|
||||||
@@ -120,8 +125,9 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
|
|||||||
kStart += (UInt128)currentSize;
|
kStart += (UInt128)currentSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayPool<ulong>.Shared.Return(orderArray);
|
stream.Dispose();
|
||||||
|
ulongPool.Return(orderArray);
|
||||||
orderBuffer.Dispose();
|
orderBuffer.Dispose();
|
||||||
gpuLease.Dispose();
|
// GpuPrimeWorkLimiter.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,32 +8,125 @@ namespace PerfectNumbers.Core.Gpu;
|
|||||||
|
|
||||||
public class MersenneNumberLucasLehmerGpuTester
|
public class MersenneNumberLucasLehmerGpuTester
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, GpuUInt128, ArrayView<GpuUInt128>>> KernelCache = new();
|
[ThreadStatic]
|
||||||
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ArrayView<GpuUInt128>>> AddSmallKernelCache = new();
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ulong, GpuUInt128, ArrayView<GpuUInt128>>>? _lucasLehmerKernel;
|
||||||
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ArrayView<GpuUInt128>>> SubSmallKernelCache = new();
|
|
||||||
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ArrayView<GpuUInt128>>> ReduceKernelCache = new();
|
|
||||||
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, ArrayView<byte>>> IsZeroKernelCache = new();
|
|
||||||
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<ulong>, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>>> BatchKernelCache = new();
|
|
||||||
|
|
||||||
private Action<Index1D, ulong, GpuUInt128, ArrayView<GpuUInt128>> GetKernel(Accelerator accelerator)
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ulong, ArrayView<GpuUInt128>>>? _addSmallKernel;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ulong, ArrayView<GpuUInt128>>>? _subSmallKernel;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ulong, ArrayView<GpuUInt128>>>? _reduceKernel;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ArrayView<byte>>>? _isZeroKernel;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>>>? _lucasLehmerBatchKernel;
|
||||||
|
|
||||||
|
private static Action<AcceleratorStream, Index1D, ulong, GpuUInt128, ArrayView<GpuUInt128>> GetLucasLehmerKernel(Accelerator accelerator)
|
||||||
{
|
{
|
||||||
return KernelCache.GetOrAdd(accelerator, accel => accel.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, ArrayView<GpuUInt128>>(LucasLehmerKernels.Kernel));
|
var pool = _lucasLehmerKernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Action<Index1D, ulong, ArrayView<GpuUInt128>> GetAddSmallKernel(Accelerator accelerator) =>
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, GpuUInt128, ArrayView<GpuUInt128>>(LucasLehmerKernels.Kernel);
|
||||||
AddSmallKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<GpuUInt128>>(LucasLehmerKernels.AddSmallKernel));
|
|
||||||
|
|
||||||
private Action<Index1D, ulong, ArrayView<GpuUInt128>> GetSubSmallKernel(Accelerator accelerator) =>
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
SubSmallKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<GpuUInt128>>(LucasLehmerKernels.SubtractSmallKernel));
|
|
||||||
|
|
||||||
private Action<Index1D, ulong, ArrayView<GpuUInt128>> GetReduceKernel(Accelerator accelerator) =>
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, GpuUInt128, ArrayView<GpuUInt128>>>();
|
||||||
ReduceKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<GpuUInt128>>(LucasLehmerKernels.ReduceModMersenneKernel));
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
private Action<Index1D, ArrayView<GpuUInt128>, ArrayView<byte>> GetIsZeroKernel(Accelerator accelerator) =>
|
private static Action<AcceleratorStream, Index1D, ulong, ArrayView<GpuUInt128>> GetAddSmallKernel(Accelerator accelerator)
|
||||||
IsZeroKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, ArrayView<byte>>(LucasLehmerKernels.IsZeroKernel));
|
{
|
||||||
|
var pool = _addSmallKernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
private Action<Index1D, ArrayView<ulong>, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>> GetBatchKernel(Accelerator accelerator) =>
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<GpuUInt128>>(LucasLehmerKernels.AddSmallKernel);
|
||||||
BatchKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>>(LucasLehmerKernels.KernelBatch));
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView<GpuUInt128>>>();
|
||||||
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Action<AcceleratorStream, Index1D, ulong, ArrayView<GpuUInt128>> GetSubSmallKernel(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
var pool = _subSmallKernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<GpuUInt128>>(LucasLehmerKernels.SubtractSmallKernel);
|
||||||
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView<GpuUInt128>>>();
|
||||||
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Action<AcceleratorStream, Index1D, ulong, ArrayView<GpuUInt128>> GetReduceKernel(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
var pool = _reduceKernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<GpuUInt128>>(LucasLehmerKernels.ReduceModMersenneKernel);
|
||||||
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView<GpuUInt128>>>();
|
||||||
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ArrayView<byte>> GetIsZeroKernel(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
var pool = _isZeroKernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, ArrayView<byte>>(LucasLehmerKernels.IsZeroKernel);
|
||||||
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ArrayView<byte>>>();
|
||||||
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>> GetLucasLehmerBatchKernel(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
var pool = _lucasLehmerBatchKernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>>(LucasLehmerKernels.KernelBatch);
|
||||||
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>>>();
|
||||||
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
// Configurable LL slice size to keep kernels short. Default 32.
|
// Configurable LL slice size to keep kernels short. Default 32.
|
||||||
public int SliceSize = 32;
|
public int SliceSize = 32;
|
||||||
@@ -54,13 +147,11 @@ public class MersenneNumberLucasLehmerGpuTester
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var limiter = GpuPrimeWorkLimiter.Acquire();
|
|
||||||
bool result;
|
bool result;
|
||||||
if (exponent >= 128UL)
|
if (exponent >= 128UL)
|
||||||
{
|
{
|
||||||
if (!TryGetNttParameters(exponent, out var nttMod, out var primitiveRoot))
|
if (!TryGetNttParameters(exponent, out var nttMod, out var primitiveRoot))
|
||||||
{
|
{
|
||||||
limiter.Dispose();
|
|
||||||
throw new NotSupportedException("NTT parameters for the given exponent are not available.");
|
throw new NotSupportedException("NTT parameters for the given exponent are not available.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,19 +159,22 @@ public class MersenneNumberLucasLehmerGpuTester
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var gpu = GpuContextPool.Rent();
|
// GpuPrimeWorkLimiter.Acquire();
|
||||||
var accelerator = gpu.Accelerator;
|
var acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
var kernel = GetKernel(accelerator);
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
|
var stream = accelerator.CreateStream();
|
||||||
var modulus = new GpuUInt128(((UInt128)1 << (int)exponent) - 1UL); // TODO: Cache these Mersenne moduli per exponent so LL GPU runs skip rebuilding them every launch.
|
var modulus = new GpuUInt128(((UInt128)1 << (int)exponent) - 1UL); // TODO: Cache these Mersenne moduli per exponent so LL GPU runs skip rebuilding them every launch.
|
||||||
var buffer = accelerator.Allocate1D<GpuUInt128>(1);
|
var buffer = accelerator.Allocate1D<GpuUInt128>(1);
|
||||||
kernel(1, exponent, modulus, buffer.View);
|
var lucasLehmerKernel = GetLucasLehmerKernel(accelerator);
|
||||||
accelerator.Synchronize();
|
lucasLehmerKernel(stream, 1, exponent, modulus, buffer.View);
|
||||||
|
stream.Synchronize();
|
||||||
|
// TODO: Replace this with .CopyToCPU with pre-allocated buffer using stream. Synchronize on the stream after copy.
|
||||||
result = buffer.GetAsArray1D()[0].IsZero;
|
result = buffer.GetAsArray1D()[0].IsZero;
|
||||||
buffer.Dispose();
|
buffer.Dispose();
|
||||||
gpu.Dispose();
|
stream.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
limiter.Dispose();
|
// GpuPrimeWorkLimiter.Release();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,16 +194,18 @@ public class MersenneNumberLucasLehmerGpuTester
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var gpu = GpuContextPool.Rent();
|
var acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
var kernel = GetBatchKernel(accelerator);
|
var stream = accelerator.CreateStream();
|
||||||
|
|
||||||
var expBuffer = accelerator.Allocate1D<ulong>(count);
|
var expBuffer = accelerator.Allocate1D<ulong>(count);
|
||||||
ulong[] expArray = ArrayPool<ulong>.Shared.Rent(count);
|
ArrayPool<ulong> ulongPool = ThreadStaticPools.UlongPool;
|
||||||
|
ulong[] expArray = ulongPool.Rent(count);
|
||||||
exponents.CopyTo(expArray);
|
exponents.CopyTo(expArray);
|
||||||
expBuffer.View.CopyFromCPU(ref expArray[0], count);
|
expBuffer.View.CopyFromCPU(stream, ref expArray[0], count);
|
||||||
|
|
||||||
GpuUInt128[] modulusArray = ArrayPool<GpuUInt128>.Shared.Rent(count);
|
ArrayPool<GpuUInt128> gpuUInt128Pool = ThreadStaticPools.GpuUInt128Pool;
|
||||||
|
GpuUInt128[] modulusArray = gpuUInt128Pool.Rent(count);
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
// TODO: Replace this per-exponent shift with a small shared table (one entry per supported exponent < 128)
|
// TODO: Replace this per-exponent shift with a small shared table (one entry per supported exponent < 128)
|
||||||
@@ -118,19 +214,20 @@ public class MersenneNumberLucasLehmerGpuTester
|
|||||||
}
|
}
|
||||||
|
|
||||||
var modBuffer = accelerator.Allocate1D<GpuUInt128>(count);
|
var modBuffer = accelerator.Allocate1D<GpuUInt128>(count);
|
||||||
modBuffer.View.CopyFromCPU(ref modulusArray[0], count);
|
modBuffer.View.CopyFromCPU(stream, ref modulusArray[0], count);
|
||||||
|
|
||||||
var stateBuffer = accelerator.Allocate1D<GpuUInt128>(count);
|
var stateBuffer = accelerator.Allocate1D<GpuUInt128>(count);
|
||||||
kernel(count, expBuffer.View, modBuffer.View, stateBuffer.View);
|
var lucasLehmerBatchKernel = GetLucasLehmerBatchKernel(accelerator);
|
||||||
accelerator.Synchronize();
|
lucasLehmerBatchKernel(stream, count, expBuffer.View, modBuffer.View, stateBuffer.View);
|
||||||
stateBuffer.View.CopyToCPU(ref residues[0], count);
|
stateBuffer.View.CopyToCPU(stream, ref residues[0], count);
|
||||||
|
stream.Synchronize();
|
||||||
|
|
||||||
stateBuffer.Dispose();
|
stateBuffer.Dispose();
|
||||||
modBuffer.Dispose();
|
modBuffer.Dispose();
|
||||||
expBuffer.Dispose();
|
expBuffer.Dispose();
|
||||||
ArrayPool<GpuUInt128>.Shared.Return(modulusArray);
|
gpuUInt128Pool.Return(modulusArray);
|
||||||
ArrayPool<ulong>.Shared.Return(expArray);
|
ulongPool.Return(expArray);
|
||||||
gpu.Dispose();
|
stream.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void IsMersennePrimeBatch(ReadOnlySpan<ulong> exponents, Span<bool> results)
|
public void IsMersennePrimeBatch(ReadOnlySpan<ulong> exponents, Span<bool> results)
|
||||||
@@ -141,23 +238,27 @@ public class MersenneNumberLucasLehmerGpuTester
|
|||||||
throw new ArgumentException("Result span too small", nameof(results));
|
throw new ArgumentException("Result span too small", nameof(results));
|
||||||
}
|
}
|
||||||
|
|
||||||
GpuUInt128[] buffer = ArrayPool<GpuUInt128>.Shared.Rent(count);
|
ArrayPool<GpuUInt128> gpuUInt128Pool = ThreadStaticPools.GpuUInt128Pool;
|
||||||
|
GpuUInt128[] buffer = gpuUInt128Pool.Rent(count);
|
||||||
ComputeResidues(exponents, buffer.AsSpan(0, count));
|
ComputeResidues(exponents, buffer.AsSpan(0, count));
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
results[i] = buffer[i].IsZero;
|
results[i] = buffer[i].IsZero;
|
||||||
}
|
}
|
||||||
ArrayPool<GpuUInt128>.Shared.Return(buffer);
|
gpuUInt128Pool.Return(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
|
||||||
private bool IsMersennePrimeNtt(ulong exponent, GpuUInt128 nttMod, GpuUInt128 primitiveRoot, bool runOnGpu)
|
private bool IsMersennePrimeNtt(ulong exponent, GpuUInt128 nttMod, GpuUInt128 primitiveRoot, bool runOnGpu)
|
||||||
{
|
{
|
||||||
var gpu = GpuContextPool.Rent();
|
var acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
var addKernel = GetAddSmallKernel(accelerator);
|
var stream = accelerator.CreateStream();
|
||||||
|
|
||||||
var subKernel = GetSubSmallKernel(accelerator);
|
var subKernel = GetSubSmallKernel(accelerator);
|
||||||
var reduceKernel = GetReduceKernel(accelerator);
|
var reduceKernel = GetReduceKernel(accelerator);
|
||||||
var zeroKernel = GetIsZeroKernel(accelerator);
|
var isZeroKernel = GetIsZeroKernel(accelerator);
|
||||||
|
|
||||||
int limbCount = (int)((exponent + 127UL) / 128UL);
|
int limbCount = (int)((exponent + 127UL) / 128UL);
|
||||||
int target = limbCount * 2;
|
int target = limbCount * 2;
|
||||||
@@ -167,8 +268,9 @@ public class MersenneNumberLucasLehmerGpuTester
|
|||||||
transformLength <<= 1;
|
transformLength <<= 1;
|
||||||
}
|
}
|
||||||
var stateBuffer = accelerator.Allocate1D<GpuUInt128>(transformLength);
|
var stateBuffer = accelerator.Allocate1D<GpuUInt128>(transformLength);
|
||||||
stateBuffer.MemSetToZero();
|
stateBuffer.MemSetToZero(stream);
|
||||||
addKernel(1, 4UL, stateBuffer.View);
|
var addSmallKernel = GetAddSmallKernel(accelerator);
|
||||||
|
addSmallKernel(stream, 1, 4UL, stateBuffer.View);
|
||||||
|
|
||||||
// TODO(LL-SLICE): Slice Lucas–Lehmer iterations into short batches to
|
// TODO(LL-SLICE): Slice Lucas–Lehmer iterations into short batches to
|
||||||
// avoid long-running kernels and TDR. Example: process 8–64 iterations
|
// avoid long-running kernels and TDR. Example: process 8–64 iterations
|
||||||
@@ -183,27 +285,26 @@ public class MersenneNumberLucasLehmerGpuTester
|
|||||||
for (int s = 0; s < iter; s++)
|
for (int s = 0; s < iter; s++)
|
||||||
{
|
{
|
||||||
// Square in NTT domain with staged kernels (short runtime per kernel).
|
// Square in NTT domain with staged kernels (short runtime per kernel).
|
||||||
NttGpuMath.SquareDevice(accelerator, stateBuffer.View, nttMod, primitiveRoot);
|
NttGpuMath.SquareDevice(accelerator, stream, stateBuffer.View, nttMod, primitiveRoot);
|
||||||
subKernel(1, 2UL, stateBuffer.View);
|
subKernel(stream, 1, 2UL, stateBuffer.View);
|
||||||
reduceKernel(1, exponent, stateBuffer.View);
|
reduceKernel(stream, 1, exponent, stateBuffer.View);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Synchronize between slices to yield to scheduler and avoid TDR.
|
// Synchronize between slices to yield to scheduler and avoid TDR.
|
||||||
accelerator.Synchronize();
|
|
||||||
i += (ulong)iter;
|
i += (ulong)iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
reduceKernel(1, exponent, stateBuffer.View);
|
reduceKernel(stream, 1, exponent, stateBuffer.View);
|
||||||
|
|
||||||
var resultBuffer = accelerator.Allocate1D<byte>(1);
|
var resultBuffer = accelerator.Allocate1D<byte>(1);
|
||||||
zeroKernel(1, stateBuffer.View, resultBuffer.View);
|
isZeroKernel(stream, 1, stateBuffer.View, resultBuffer.View);
|
||||||
accelerator.Synchronize();
|
Span<byte> result = stackalloc byte[1];
|
||||||
byte[] result = new byte[1];
|
resultBuffer.View.CopyToCPU(stream, result);
|
||||||
resultBuffer.View.CopyToCPU(result);
|
stream.Synchronize();
|
||||||
|
|
||||||
bool isPrime = result[0] != 0;
|
bool isPrime = result[0] != 0;
|
||||||
resultBuffer.Dispose();
|
resultBuffer.Dispose();
|
||||||
stateBuffer.Dispose();
|
stateBuffer.Dispose();
|
||||||
gpu.Dispose();
|
stream.Dispose();
|
||||||
return isPrime;
|
return isPrime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,6 +573,4 @@ public class MersenneNumberLucasLehmerGpuTester
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -7,34 +7,46 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
|
|||||||
private readonly GpuKernelType _kernelType = kernelType;
|
private readonly GpuKernelType _kernelType = kernelType;
|
||||||
private readonly bool _useGpuOrder = useGpuOrder;
|
private readonly bool _useGpuOrder = useGpuOrder;
|
||||||
|
|
||||||
|
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
|
||||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||||
{
|
{
|
||||||
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
|
// GpuPrimeWorkLimiter.Acquire();
|
||||||
var accelerator = gpuLease.Accelerator;
|
// var accelerator = SharedGpuContext.Accelerator;
|
||||||
var stream = gpuLease.Stream;
|
var acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
|
var stream = accelerator.CreateStream();
|
||||||
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR
|
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR
|
||||||
UInt128 kStart = UInt128.One;
|
UInt128 kStart = UInt128.One;
|
||||||
ulong divMul = (ulong)((((UInt128)1 << 64) - UInt128.One) / exponent) + 1UL;
|
ulong divMul = (ulong)((((UInt128)1 << 64) - UInt128.One) / exponent) + 1UL;
|
||||||
byte last = lastDigit == LastDigit.Seven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
|
byte last = lastDigit == LastDigit.Seven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
|
||||||
|
|
||||||
|
KernelContainer kernels = GpuKernelPool.GetOrAddKernels(acceleratorIndex, stream, KernelType.Pow2ModOrderKernelScan | KernelType.IncrementalOrderKernelScan);
|
||||||
var kernel = _kernelType switch
|
var kernel = _kernelType switch
|
||||||
{
|
{
|
||||||
GpuKernelType.Pow2Mod => gpuLease.Pow2ModOrderKernel,
|
GpuKernelType.Pow2Mod => kernels.Pow2ModOrder!,
|
||||||
_ => gpuLease.IncrementalOrderKernel,
|
_ => kernels.IncrementalOrder!,
|
||||||
};
|
};
|
||||||
|
|
||||||
var foundBuffer = accelerator.Allocate1D<int>(1); // TODO: Replace this allocation with the pooled device buffer from GpuOrderKernelBenchmarks so repeated scans reuse the pinned staging memory instead of allocating per run.
|
MemoryBuffer1D<int, ILGPU.Stride1D.Dense>? foundBuffer;
|
||||||
|
// lock (accelerator)
|
||||||
|
{
|
||||||
|
foundBuffer = accelerator.Allocate1D<int>(1); // TODO: Replace this allocation with the pooled device buffer from GpuOrderKernelBenchmarks so repeated scans reuse the pinned staging memory instead of allocating per run.
|
||||||
|
|
||||||
|
}
|
||||||
exponent.Mod10_8_5_3Steps(out ulong step10, out ulong step8, out ulong step5, out ulong step3);
|
exponent.Mod10_8_5_3Steps(out ulong step10, out ulong step8, out ulong step5, out ulong step3);
|
||||||
|
|
||||||
UInt128 remaining;
|
UInt128 remaining;
|
||||||
int currentSize;
|
int currentSize;
|
||||||
int found = 0;
|
int found = 0;
|
||||||
|
|
||||||
|
var smallCyclesView = GpuKernelPool.GetSmallCyclesOnDevice(kernels);
|
||||||
while (kStart <= maxK && Volatile.Read(ref isPrime))
|
while (kStart <= maxK && Volatile.Read(ref isPrime))
|
||||||
{
|
{
|
||||||
remaining = maxK - kStart + 1UL;
|
remaining = maxK - kStart + 1UL;
|
||||||
currentSize = remaining > (UInt128)batchSize ? batchSize : (int)remaining;
|
currentSize = remaining > (UInt128)batchSize ? batchSize : (int)remaining;
|
||||||
// This clear is required because the result may not always be set. Remove it after the kernels are modified to always set the result.
|
// This clear is required because the result may not always be set. Remove it after the kernels are modified to always set the result.
|
||||||
foundBuffer.MemSetToZero();
|
foundBuffer.MemSetToZero(stream);
|
||||||
// Precompute residue automaton bases for this batch
|
// Precompute residue automaton bases for this batch
|
||||||
UInt128 q0 = twoP * kStart + 1UL;
|
UInt128 q0 = twoP * kStart + 1UL;
|
||||||
// TODO: Replace these modulo recomputations with the divisor-cycle stepping deltas captured in
|
// TODO: Replace these modulo recomputations with the divisor-cycle stepping deltas captured in
|
||||||
@@ -44,12 +56,13 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
|
|||||||
var kernelArgs = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
|
var kernelArgs = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
|
||||||
|
|
||||||
// Ensure device has small cycles table for early rejections in order kernels
|
// Ensure device has small cycles table for early rejections in order kernels
|
||||||
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
|
kernel(
|
||||||
kernel(stream, currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, divMul,
|
stream, currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart,
|
||||||
kernelArgs, foundBuffer.View, smallCyclesView);
|
last, divMul, kernelArgs, foundBuffer.View, smallCyclesView
|
||||||
|
);
|
||||||
|
|
||||||
|
foundBuffer.View.CopyToCPU(stream, ref found, 1);
|
||||||
stream.Synchronize();
|
stream.Synchronize();
|
||||||
foundBuffer.View.CopyToCPU(ref found, 1);
|
|
||||||
if (found != 0)
|
if (found != 0)
|
||||||
{
|
{
|
||||||
Volatile.Write(ref isPrime, false);
|
Volatile.Write(ref isPrime, false);
|
||||||
@@ -59,7 +72,8 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
|
|||||||
kStart += (UInt128)currentSize;
|
kStart += (UInt128)currentSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stream.Dispose();
|
||||||
foundBuffer.Dispose();
|
foundBuffer.Dispose();
|
||||||
gpuLease.Dispose();
|
// GpuPrimeWorkLimiter.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,4 @@
|
|||||||
using System;
|
|
||||||
using System.Buffers;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
@@ -13,9 +8,12 @@ namespace PerfectNumbers.Core.Gpu;
|
|||||||
public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||||
{
|
{
|
||||||
private readonly bool _useGpuOrder = useGpuOrder;
|
private readonly bool _useGpuOrder = useGpuOrder;
|
||||||
private readonly ConcurrentBag<GpuContextPool.GpuContextLease> _acceleratorPool = new();
|
|
||||||
private readonly ConcurrentDictionary<Accelerator, 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>>> _pow2ModKernelCache = new(AcceleratorReferenceComparer.Instance);
|
[ThreadStatic]
|
||||||
private readonly ConcurrentDictionary<Accelerator, ConcurrentBag<ResidueResources>> _resourcePools = new(AcceleratorReferenceComparer.Instance);
|
private static Dictionary<Accelerator, 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;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Queue<ResidueResources>? _resourcePools;
|
||||||
|
|
||||||
// TODO: Integrate MersenneDivisorCycles.Shared to consult cycle lengths for small q (<= 4M) and fast-reject
|
// TODO: Integrate MersenneDivisorCycles.Shared to consult cycle lengths for small q (<= 4M) and fast-reject
|
||||||
// candidates without launching heavy kernels. Consider a device-visible constant buffer per-accelerator
|
// candidates without launching heavy kernels. Consider a device-visible constant buffer per-accelerator
|
||||||
@@ -23,26 +21,30 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
|||||||
// TODO: When cycles are missing for larger q values, compute only the single required cycle on the device
|
// TODO: When cycles are missing for larger q values, compute only the single required cycle on the device
|
||||||
// requested by the caller, skip queuing additional block generation, and keep the snapshot cache untouched.
|
// requested by the caller, skip queuing additional block generation, and keep the snapshot cache untouched.
|
||||||
|
|
||||||
|
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
|
||||||
// GPU residue variant: check 2^p % q == 1 for q = 2*p*k + 1.
|
// GPU residue variant: check 2^p % q == 1 for q = 2*p*k + 1.
|
||||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||||
{
|
{
|
||||||
var limiter = GpuPrimeWorkLimiter.Acquire();
|
// GpuPrimeWorkLimiter.Acquire();
|
||||||
var gpuLease = RentAccelerator();
|
var acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
var accelerator = gpuLease.Accelerator;
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
|
|
||||||
// Monitor.Enter(gpuLease.ExecutionLock);
|
// Monitor.Enter(gpuLease.ExecutionLock);
|
||||||
|
|
||||||
var stream = accelerator.CreateStream();
|
var stream = accelerator.CreateStream();
|
||||||
var kernel = GetPow2ModKernel(accelerator);
|
var kernel = GetPow2ModKernel(accelerator);
|
||||||
var resources = RentResources(accelerator, GpuConstants.ScanBatchSize);
|
var resources = RentResources(accelerator, GpuConstants.ScanBatchSize);
|
||||||
|
var gpuKernels = GpuKernelPool.GetOrAddKernels(acceleratorIndex, stream, KernelType.SmallCycles | KernelType.SmallPrimes);
|
||||||
|
|
||||||
var orderBuffer = resources.OrderBuffer;
|
var orderBuffer = resources.OrderBuffer;
|
||||||
ulong[] orderArray = resources.OrderArray;
|
ulong[] orderArray = resources.OrderArray;
|
||||||
int batchSize = resources.Capacity;
|
int batchSize = resources.Capacity;
|
||||||
|
|
||||||
// Ensure device has small cycles and primes tables for in-kernel lookup
|
// Ensure device has small cycles and primes tables for in-kernel lookup
|
||||||
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
|
// TODO: Pass-in the stream down the path
|
||||||
ResiduePrimeViews primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(accelerator);
|
var smallCyclesView = GpuKernelPool.GetSmallCyclesOnDevice(gpuKernels);
|
||||||
|
ResiduePrimeViews primeViews = GpuKernelPool.GetSmallPrimesOnDevice(gpuKernels);
|
||||||
|
|
||||||
UInt128 kStart = 1UL;
|
UInt128 kStart = 1UL;
|
||||||
UInt128 limit = maxK + UInt128.One;
|
UInt128 limit = maxK + UInt128.One;
|
||||||
@@ -68,8 +70,8 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
|||||||
kernel(stream, currentSize, exponent, twoPGpu, (GpuUInt128)kStart, last, 0UL,
|
kernel(stream, currentSize, exponent, twoPGpu, (GpuUInt128)kStart, last, 0UL,
|
||||||
kernelArgs, orderBuffer.View, smallCyclesView, primeViews.LastOne, primeViews.LastSeven, primeViews.LastOnePow2, primeViews.LastSevenPow2);
|
kernelArgs, orderBuffer.View, smallCyclesView, primeViews.LastOne, primeViews.LastSeven, primeViews.LastOnePow2, primeViews.LastSevenPow2);
|
||||||
|
|
||||||
|
orderBuffer.View.CopyToCPU(stream, ref ordersRef, currentSize);
|
||||||
stream.Synchronize();
|
stream.Synchronize();
|
||||||
orderBuffer.View.CopyToCPU(ref ordersRef, currentSize);
|
|
||||||
if (!Volatile.Read(ref isPrime))
|
if (!Volatile.Read(ref isPrime))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
@@ -106,80 +108,99 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnResources(accelerator, resources);
|
ReturnResources(resources);
|
||||||
stream.Dispose();
|
stream.Dispose();
|
||||||
// Monitor.Exit(gpuLease.ExecutionLock);
|
// Monitor.Exit(gpuLease.ExecutionLock);
|
||||||
ReturnAccelerator(gpuLease);
|
gpuKernels.Dispose();
|
||||||
limiter.Dispose();
|
// GpuPrimeWorkLimiter.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
private 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>> GetPow2ModKernel(Accelerator accelerator) =>
|
private static 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>> GetPow2ModKernel(Accelerator accelerator)
|
||||||
_pow2ModKernelCache.GetOrAdd(
|
|
||||||
accelerator,
|
|
||||||
static acc =>
|
|
||||||
{
|
{
|
||||||
var loaded = acc.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>>(Pow2ModKernels.Pow2ModKernelScan);
|
|
||||||
return (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>>)Delegate.CreateDelegate(
|
var pool = _pow2ModKernel ?? [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
cached = CreatePow2ModKernel(accelerator);
|
||||||
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static 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>> CreatePow2ModKernel(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
var loaded = accelerator.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>>(Pow2ModKernels.Pow2ModKernelScan);
|
||||||
|
|
||||||
|
var kernel = (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>>)Delegate.CreateDelegate(
|
||||||
typeof(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>>),
|
typeof(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>>),
|
||||||
loaded.Target,
|
loaded.Target,
|
||||||
loaded.Method);
|
loaded.Method);
|
||||||
});
|
return kernel;
|
||||||
|
|
||||||
private GpuContextPool.GpuContextLease RentAccelerator()
|
|
||||||
{
|
|
||||||
if (_acceleratorPool.TryTake(out var lease))
|
|
||||||
{
|
|
||||||
return lease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return GpuContextPool.Rent();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReturnAccelerator(GpuContextPool.GpuContextLease lease) => _acceleratorPool.Add(lease);
|
private static ResidueResources RentResources(Accelerator accelerator, int capacity)
|
||||||
|
|
||||||
private ResidueResources RentResources(Accelerator accelerator, int capacity)
|
|
||||||
{
|
{
|
||||||
var bag = _resourcePools.GetOrAdd(accelerator, static _ => new ConcurrentBag<ResidueResources>());
|
var resourcePools = _resourcePools ??= new();
|
||||||
while (bag.TryTake(out var resources))
|
if (resourcePools.TryDequeue(out var bag))
|
||||||
{
|
{
|
||||||
if (resources.Capacity >= capacity)
|
bag.EnsureCapacity(accelerator, capacity);
|
||||||
|
return bag;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ResidueResources(accelerator, capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReturnResources(ResidueResources resources)
|
||||||
{
|
{
|
||||||
// The caller always overwrites the required elements. We don't need to worry about clearing the output buffer.
|
var resourcePools = _resourcePools ??= new();
|
||||||
// resources.OrderBuffer.MemSetToZero();
|
resourcePools.Enqueue(resources);
|
||||||
return resources;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resources.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
var created = new ResidueResources(accelerator, capacity);
|
|
||||||
// The caller always overwrites the required elements. We don't need to worry about clearing the output buffer.
|
|
||||||
// created.OrderBuffer.MemSetToZero();
|
|
||||||
return created;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReturnResources(Accelerator accelerator, ResidueResources resources) =>
|
|
||||||
_resourcePools.GetOrAdd(accelerator, static _ => new ConcurrentBag<ResidueResources>()).Add(resources);
|
|
||||||
|
|
||||||
private sealed class ResidueResources
|
private sealed class ResidueResources
|
||||||
{
|
{
|
||||||
internal ResidueResources(Accelerator accelerator, int capacity)
|
internal ResidueResources(Accelerator accelerator, int capacity)
|
||||||
{
|
{
|
||||||
Capacity = Math.Max(1, capacity);
|
Capacity = Math.Max(1, capacity);
|
||||||
|
// lock (accelerator)
|
||||||
|
{
|
||||||
OrderBuffer = accelerator.Allocate1D<ulong>(Capacity);
|
OrderBuffer = accelerator.Allocate1D<ulong>(Capacity);
|
||||||
OrderArray = ArrayPool<ulong>.Shared.Rent(Capacity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal MemoryBuffer1D<ulong, Stride1D.Dense> OrderBuffer { get; }
|
OrderArray = ThreadStaticPools.UlongPool.Rent(Capacity);
|
||||||
|
}
|
||||||
|
|
||||||
internal ulong[] OrderArray { get; }
|
internal void EnsureCapacity(Accelerator accelerator, int capacity)
|
||||||
|
{
|
||||||
|
if (OrderBuffer.Length >= capacity)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
internal int Capacity { get; }
|
OrderBuffer.Dispose();
|
||||||
|
ThreadStaticPools.UlongPool.Return(OrderArray);
|
||||||
|
|
||||||
|
// lock (accelerator)
|
||||||
|
{
|
||||||
|
OrderBuffer = accelerator.Allocate1D<ulong>(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderArray = ThreadStaticPools.UlongPool.Rent(capacity);
|
||||||
|
Capacity = capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal MemoryBuffer1D<ulong, Stride1D.Dense> OrderBuffer { get; private set; }
|
||||||
|
|
||||||
|
internal ulong[] OrderArray { get; private set; }
|
||||||
|
|
||||||
|
internal int Capacity { get; private set; }
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
OrderBuffer.Dispose();
|
OrderBuffer.Dispose();
|
||||||
ArrayPool<ulong>.Shared.Return(OrderArray, clearArray: false);
|
ThreadStaticPools.UlongPool.Return(OrderArray, clearArray: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Buffers;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
@@ -29,23 +28,60 @@ public static class NttGpuMath
|
|||||||
public static NttBackend GpuTransformBackend { get; set; } = NttBackend.Reference;
|
public static NttBackend GpuTransformBackend { get; set; } = NttBackend.Reference;
|
||||||
// Controls modular reduction strategy inside staged NTT paths.
|
// Controls modular reduction strategy inside staged NTT paths.
|
||||||
public static ModReductionMode ReductionMode { get; set; } = ModReductionMode.Auto;
|
public static ModReductionMode ReductionMode { get; set; } = ModReductionMode.Auto;
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, GpuUInt128>> MulKernelCache = new(); // TODO: Replace this concurrent cache with the prewarmed accelerator-indexed tables from GpuModularArithmeticBenchmarks so kernel launches avoid dictionary lookups once the kernels are baked during startup.
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, GpuUInt128>> StageKernelCache = new(); // TODO: Same as above – materialize staged kernels during startup so we can drop ConcurrentDictionary usage entirely per the benchmark guidance.
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, GpuUInt128, GpuUInt128>> ScaleKernelCache = new(); // TODO: Promote to the static kernel table initialized alongside the GpuModularArithmeticBenchmarks fast path.
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, ulong, ulong>> StageMontKernelCache = new(); // TODO: Inline the Montgomery kernels into the startup table instead of using a concurrent cache now that we no longer mutate state at runtime.
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, ulong, ulong, ulong, ulong>> StageBarrett128KernelCache = new(); // TODO: Same plan – reuse the precomputed kernels measured fastest in MontgomeryMultiplyBenchmarks rather than looking them up dynamically.
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, GpuUInt128, ulong, ulong, ulong, ulong>> ScaleBarrett128KernelCache = new(); // TODO: Collapse into the startup kernel array once Barrett128 constants are preloaded.
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong, ulong>> SquareBarrett128KernelCache = new(); // TODO: Fold these kernels into the same startup table to avoid concurrent access overhead highlighted in GpuModularArithmeticBenchmarks.
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong>> ToMont64KernelCache = new(); // TODO: Prebind and reuse the Montgomery conversions from MontgomeryMultiplyBenchmarks instead of storing them in a concurrent cache.
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, ulong, ulong>> FromMont64KernelCache = new(); // TODO: As above – drop ConcurrentDictionary once startup prewarms every kernel.
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, ulong, ulong>> SquareMont64KernelCache = new(); // TODO: Inline into the static kernel table established during initialization.
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong>> ScaleMont64KernelCache = new(); // TODO: Move to the startup kernel table so Montgomery-scaled launches skip concurrent lookups.
|
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, int, GpuUInt128, GpuUInt128>> ForwardKernelCache = new(); // TODO: Warm these forward kernels during initialization and store them in a plain array so the LucasLehmerGpuBenchmarks launch costs disappear.
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, GpuUInt128>>? _mulKernel; // TODO: Replace this concurrent cache with the prewarmed accelerator-indexed tables from GpuModularArithmeticBenchmarks so kernel launches avoid dictionary lookups once the kernels are baked during startup.
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, int, GpuUInt128, GpuUInt128, GpuUInt128>> InverseKernelCache = new(); // TODO: Same as above – replace with startup-prepared tables per LucasLehmerGpuBenchmarks guidance.
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream,Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, GpuUInt128>>? _stageKernel; // TODO: Replace this concurrent cache with the prewarmed accelerator-indexed tables from GpuModularArithmeticBenchmarks so kernel launches avoid dictionary lookups once the kernels are baked during startup.
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<GpuUInt128>, int>> BitReverseKernelCache = new(); // TODO: Precompute bit-reversal kernels during boot so we can remove the concurrent lookup overhead flagged in the benchmarks.
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, GpuUInt128, GpuUInt128>>? _scaleKernel; // TODO: Replace this concurrent cache with the prewarmed accelerator-indexed tables from GpuModularArithmeticBenchmarks so kernel launches avoid dictionary lookups once the kernels are baked during startup.
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, ulong, ulong>>? _stageMontKernel; // TODO: Replace this concurrent cache with the prewarmed accelerator-indexed tables from GpuModularArithmeticBenchmarks so kernel launches avoid dictionary lookups once the kernels are baked during startup.
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, ulong, ulong, ulong, ulong>>? _stageBarrett128Kernel; // TODO: Replace this concurrent cache with the prewarmed accelerator-indexed tables from GpuModularArithmeticBenchmarks so kernel launches avoid dictionary lookups once the kernels are baked during startup.
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong>>? _toMont64Kernel; // TODO: Replace this concurrent cache with the prewarmed accelerator-indexed tables from GpuModularArithmeticBenchmarks so kernel launches avoid dictionary lookups once the kernels are baked during startup.
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ulong, ulong>>? _fromMont64Kernel; // TODO: Replace this concurrent cache with the prewarmed accelerator-indexed tables from GpuModularArithmeticBenchmarks so kernel launches avoid dictionary lookups once the kernels are baked during startup.
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, GpuUInt128, ulong, ulong, ulong, ulong>>? _scaleBarrett128Kernel; // TODO: Replace this concurrent cache with the prewarmed accelerator-indexed tables from GpuModularArithmeticBenchmarks so kernel launches avoid dictionary lookups once the kernels are baked during startup.
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static readonly Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, GpuUInt128>>? _squareMont64Kernel; // TODO: Replace this concurrent cache with the prewarmed accelerator-indexed tables from GpuModularArithmeticBenchmarks so kernel launches avoid dictionary lookups once the kernels are baked during startup.
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong>>? _scaleMont64Kernel; // TODO: Replace this concurrent cache with the prewarmed accelerator-indexed tables from GpuModularArithmeticBenchmarks so kernel launches avoid dictionary lookups once the kernels are baked during startup.
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, int, GpuUInt128, GpuUInt128>>? _forwardKernel; // TODO: Replace this concurrent cache with the prewarmed accelerator-indexed tables from GpuModularArithmeticBenchmarks so kernel launches avoid dictionary lookups once the kernels are baked during startup.
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, int, GpuUInt128, GpuUInt128, GpuUInt128>>? _inverseKernel; // TODO: Replace this concurrent cache with the prewarmed accelerator-indexed tables from GpuModularArithmeticBenchmarks so kernel launches avoid dictionary lookups once the kernels are baked during startup.
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Dictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, int>>? _bitReverseKernel; // TODO: Replace this concurrent cache with the prewarmed accelerator-indexed tables from GpuModularArithmeticBenchmarks so kernel launches avoid dictionary lookups once the kernels are baked during startup.
|
||||||
|
|
||||||
|
private static Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, GpuUInt128> GetStageKernel(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
var pool = _stageKernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, GpuUInt128>(NttButterflyKernels.StageKernel);
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, GpuUInt128>>();
|
||||||
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class SquareCacheEntry
|
private sealed class SquareCacheEntry
|
||||||
{
|
{
|
||||||
@@ -72,7 +108,7 @@ public static class NttGpuMath
|
|||||||
public ulong BarrettMuHigh { get; }
|
public ulong BarrettMuHigh { get; }
|
||||||
public ulong BarrettMuLow { get; }
|
public ulong BarrettMuLow { get; }
|
||||||
|
|
||||||
public SquareCacheEntry(Accelerator accelerator, int length, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
public SquareCacheEntry(Accelerator accelerator, AcceleratorStream stream, int length, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
||||||
{
|
{
|
||||||
Buffer = accelerator.Allocate1D<GpuUInt128>(length);
|
Buffer = accelerator.Allocate1D<GpuUInt128>(length);
|
||||||
Root = new GpuUInt128(primitiveRoot);
|
Root = new GpuUInt128(primitiveRoot);
|
||||||
@@ -99,7 +135,7 @@ public static class NttGpuMath
|
|||||||
// Precompute stage twiddle factors (forward and inverse) and upload to GPU.
|
// Precompute stage twiddle factors (forward and inverse) and upload to GPU.
|
||||||
Bits = (int)Math.Log2(length);
|
Bits = (int)Math.Log2(length);
|
||||||
int twiddleCount = length - 1;
|
int twiddleCount = length - 1;
|
||||||
var pool = ArrayPool<GpuUInt128>.Shared;
|
var pool = ThreadStaticPools.GpuUInt128Pool;
|
||||||
var forward = pool.Rent(twiddleCount);
|
var forward = pool.Rent(twiddleCount);
|
||||||
var inverse = pool.Rent(twiddleCount);
|
var inverse = pool.Rent(twiddleCount);
|
||||||
StageOffsets = new int[Bits];
|
StageOffsets = new int[Bits];
|
||||||
@@ -136,9 +172,9 @@ public static class NttGpuMath
|
|||||||
}
|
}
|
||||||
|
|
||||||
Twiddles = accelerator.Allocate1D<GpuUInt128>(twiddleCount);
|
Twiddles = accelerator.Allocate1D<GpuUInt128>(twiddleCount);
|
||||||
Twiddles.View.CopyFromCPU(ref forward[0], twiddleCount);
|
Twiddles.View.CopyFromCPU(stream, ref forward[0], twiddleCount);
|
||||||
TwiddlesInv = accelerator.Allocate1D<GpuUInt128>(twiddleCount);
|
TwiddlesInv = accelerator.Allocate1D<GpuUInt128>(twiddleCount);
|
||||||
TwiddlesInv.View.CopyFromCPU(ref inverse[0], twiddleCount);
|
TwiddlesInv.View.CopyFromCPU(stream, ref inverse[0], twiddleCount);
|
||||||
|
|
||||||
// Prepare Montgomery constants for 64-bit moduli and twiddles in Montgomery domain.
|
// Prepare Montgomery constants for 64-bit moduli and twiddles in Montgomery domain.
|
||||||
if (modulus.High == 0UL && (ReductionMode == ModReductionMode.Auto || ReductionMode == ModReductionMode.Mont64))
|
if (modulus.High == 0UL && (ReductionMode == ModReductionMode.Auto || ReductionMode == ModReductionMode.Mont64))
|
||||||
@@ -166,9 +202,9 @@ public static class NttGpuMath
|
|||||||
}
|
}
|
||||||
|
|
||||||
TwiddlesMont = accelerator.Allocate1D<GpuUInt128>(twiddleCount);
|
TwiddlesMont = accelerator.Allocate1D<GpuUInt128>(twiddleCount);
|
||||||
TwiddlesMont.View.CopyFromCPU(ref forwardMont[0], twiddleCount);
|
TwiddlesMont.View.CopyFromCPU(stream, ref forwardMont[0], twiddleCount);
|
||||||
TwiddlesInvMont = accelerator.Allocate1D<GpuUInt128>(twiddleCount);
|
TwiddlesInvMont = accelerator.Allocate1D<GpuUInt128>(twiddleCount);
|
||||||
TwiddlesInvMont.View.CopyFromCPU(ref inverseMont[0], twiddleCount);
|
TwiddlesInvMont.View.CopyFromCPU(stream, ref inverseMont[0], twiddleCount);
|
||||||
|
|
||||||
pool.Return(forwardMont, clearArray: true);
|
pool.Return(forwardMont, clearArray: true);
|
||||||
pool.Return(inverseMont, clearArray: true);
|
pool.Return(inverseMont, clearArray: true);
|
||||||
@@ -250,7 +286,7 @@ public static class NttGpuMath
|
|||||||
|
|
||||||
private readonly record struct SquareCacheKey(int Length, GpuUInt128 Modulus, GpuUInt128 PrimitiveRoot);
|
private readonly record struct SquareCacheKey(int Length, GpuUInt128 Modulus, GpuUInt128 PrimitiveRoot);
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, ConcurrentDictionary<SquareCacheKey, SquareCacheEntry>> SquareCache = new();
|
private static readonly ConcurrentDictionary<SquareCacheKey, SquareCacheEntry> SquareCache = [];
|
||||||
|
|
||||||
// TODO(NTT-OPT): Add a TwiddleCache for stage-wise Cooley–Tukey NTT.
|
// TODO(NTT-OPT): Add a TwiddleCache for stage-wise Cooley–Tukey NTT.
|
||||||
// Design:
|
// Design:
|
||||||
@@ -258,11 +294,10 @@ public static class NttGpuMath
|
|||||||
// - Store one GPU buffer with all stage twiddles, or per-stage slices.
|
// - Store one GPU buffer with all stage twiddles, or per-stage slices.
|
||||||
// - Expose accessors to retrieve per-stage views used by butterfly kernels.
|
// - Expose accessors to retrieve per-stage views used by butterfly kernels.
|
||||||
|
|
||||||
private static SquareCacheEntry GetSquareCache(Accelerator accelerator, int length, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
private static SquareCacheEntry GetSquareCache(Accelerator accelerator, AcceleratorStream stream, int length, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
||||||
{
|
{
|
||||||
var perAccel = SquareCache.GetOrAdd(accelerator, _ => new ConcurrentDictionary<SquareCacheKey, SquareCacheEntry>());
|
|
||||||
var key = new SquareCacheKey(length, modulus, primitiveRoot);
|
var key = new SquareCacheKey(length, modulus, primitiveRoot);
|
||||||
return perAccel.GetOrAdd(key, _ => new SquareCacheEntry(accelerator, length, modulus, primitiveRoot));
|
return SquareCache.GetOrAdd(key, _ => new SquareCacheEntry(accelerator, stream, length, modulus, primitiveRoot));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly ulong[] SmallPrimes = GenerateSmallPrimes(1000);
|
private static readonly ulong[] SmallPrimes = GenerateSmallPrimes(1000);
|
||||||
@@ -330,99 +365,191 @@ public static class NttGpuMath
|
|||||||
{
|
{
|
||||||
if (accelerator is null)
|
if (accelerator is null)
|
||||||
{
|
{
|
||||||
foreach (var perAccel in SquareCache.Values)
|
foreach (var entry in SquareCache.Values)
|
||||||
{
|
|
||||||
foreach (var entry in perAccel.Values)
|
|
||||||
{
|
{
|
||||||
entry.Dispose();
|
entry.Dispose();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
SquareCache.Clear();
|
SquareCache.Clear();
|
||||||
MulKernelCache.Clear();
|
|
||||||
StageKernelCache.Clear();
|
|
||||||
ScaleKernelCache.Clear();
|
|
||||||
StageMontKernelCache.Clear();
|
|
||||||
StageBarrett128KernelCache.Clear();
|
|
||||||
ToMont64KernelCache.Clear();
|
|
||||||
FromMont64KernelCache.Clear();
|
|
||||||
SquareMont64KernelCache.Clear();
|
|
||||||
ScaleMont64KernelCache.Clear();
|
|
||||||
ForwardKernelCache.Clear();
|
|
||||||
InverseKernelCache.Clear();
|
|
||||||
// TODO(NTT-OPT): Clear TwiddleCache once introduced.
|
// TODO(NTT-OPT): Clear TwiddleCache once introduced.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SquareCache.TryRemove(accelerator, out var cache))
|
var squareCache = SquareCache;
|
||||||
{
|
foreach (var entry in squareCache.Values)
|
||||||
foreach (var entry in cache.Values)
|
|
||||||
{
|
{
|
||||||
entry.Dispose();
|
entry.Dispose();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
MulKernelCache.TryRemove(accelerator, out _);
|
|
||||||
StageKernelCache.TryRemove(accelerator, out _);
|
|
||||||
ScaleKernelCache.TryRemove(accelerator, out _);
|
|
||||||
StageMontKernelCache.TryRemove(accelerator, out _);
|
|
||||||
StageBarrett128KernelCache.TryRemove(accelerator, out _);
|
|
||||||
ToMont64KernelCache.TryRemove(accelerator, out _);
|
|
||||||
FromMont64KernelCache.TryRemove(accelerator, out _);
|
|
||||||
SquareMont64KernelCache.TryRemove(accelerator, out _);
|
|
||||||
ScaleMont64KernelCache.TryRemove(accelerator, out _);
|
|
||||||
ForwardKernelCache.TryRemove(accelerator, out _);
|
|
||||||
InverseKernelCache.TryRemove(accelerator, out _);
|
|
||||||
// TODO(NTT-OPT): Remove per-accelerator twiddle buffers once added.
|
// TODO(NTT-OPT): Remove per-accelerator twiddle buffers once added.
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Action<Index1D, ArrayView<GpuUInt128>, int> GetBitReverseKernel(Accelerator accelerator) =>
|
private static Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, int> GetBitReverseKernel(Accelerator accelerator)
|
||||||
BitReverseKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, int>(NttTransformKernels.BitReverseKernel));
|
{
|
||||||
|
var pool = _bitReverseKernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
private static Action<Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, GpuUInt128> GetMulKernel(Accelerator accelerator) =>
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, int>(NttTransformKernels.BitReverseKernel);
|
||||||
MulKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, GpuUInt128>(NttPointwiseKernels.MulKernel));
|
|
||||||
|
|
||||||
private static Action<Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, GpuUInt128> GetStageKernel(Accelerator accelerator) =>
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
StageKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, GpuUInt128>(NttButterflyKernels.StageKernel));
|
|
||||||
|
|
||||||
private static Action<Index1D, ArrayView<GpuUInt128>, GpuUInt128, GpuUInt128> GetScaleKernel(Accelerator accelerator) =>
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, int>>();
|
||||||
ScaleKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, GpuUInt128, GpuUInt128>(NttPointwiseKernels.ScaleKernel));
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
private static Action<Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, ulong, ulong> GetStageMontKernel(Accelerator accelerator) =>
|
private static Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, int, GpuUInt128, GpuUInt128> GetForwardKernel(Accelerator accelerator)
|
||||||
StageMontKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, ulong, ulong>(NttMontgomeryKernels.StageMontKernel));
|
{
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, int, GpuUInt128, GpuUInt128>(NttTransformKernels.ForwardKernel);
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, int, GpuUInt128, GpuUInt128>>();
|
||||||
|
}
|
||||||
|
|
||||||
private static Action<Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, ulong, ulong, ulong, ulong> GetStageBarrett128Kernel(Accelerator accelerator) =>
|
private static Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ulong, ulong> GetFromMont64Kernel(Accelerator accelerator)
|
||||||
StageBarrett128KernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, ulong, ulong, ulong, ulong>(NttBarrettKernels.StageBarrett128Kernel));
|
{
|
||||||
|
var pool = _fromMont64Kernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
private static Action<Index1D, ArrayView<GpuUInt128>, GpuUInt128, ulong, ulong, ulong, ulong> GetScaleBarrett128Kernel(Accelerator accelerator) =>
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, ulong, ulong>(NttMontgomeryKernels.FromMont64Kernel);
|
||||||
ScaleBarrett128KernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, GpuUInt128, ulong, ulong, ulong, ulong>(NttBarrettKernels.ScaleBarrett128Kernel));
|
|
||||||
|
|
||||||
private static Action<Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong, ulong> GetSquareBarrett128Kernel(Accelerator accelerator) =>
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
SquareBarrett128KernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong, ulong>(NttBarrettKernels.SquareBarrett128Kernel));
|
|
||||||
|
|
||||||
private static Action<Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong> GetToMont64Kernel(Accelerator accelerator) =>
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ulong, ulong>>();
|
||||||
ToMont64KernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong>(NttMontgomeryKernels.ToMont64Kernel));
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
private static Action<Index1D, ArrayView<GpuUInt128>, ulong, ulong> GetFromMont64Kernel(Accelerator accelerator) =>
|
private static Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, int, GpuUInt128, GpuUInt128, GpuUInt128> GetInverseKernel(Accelerator accelerator)
|
||||||
FromMont64KernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, ulong, ulong>(NttMontgomeryKernels.FromMont64Kernel));
|
{
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, int, GpuUInt128, GpuUInt128, GpuUInt128>(NttTransformKernels.InverseKernel);
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, int, GpuUInt128, GpuUInt128, GpuUInt128>>();
|
||||||
|
}
|
||||||
|
|
||||||
private static Action<Index1D, ArrayView<GpuUInt128>, ulong, ulong> GetSquareMont64Kernel(Accelerator accelerator) =>
|
private static Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, GpuUInt128> GetMulKernel(Accelerator accelerator)
|
||||||
SquareMont64KernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, ulong, ulong>(NttMontgomeryKernels.SquareMont64Kernel));
|
{
|
||||||
|
var pool = _mulKernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
private static Action<Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong> GetScaleMont64Kernel(Accelerator accelerator) =>
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, GpuUInt128>(NttPointwiseKernels.MulKernel);
|
||||||
ScaleMont64KernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong>(NttMontgomeryKernels.ScaleMont64Kernel));
|
|
||||||
|
|
||||||
private static Action<Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, int, GpuUInt128, GpuUInt128> GetForwardKernel(Accelerator accelerator) =>
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
ForwardKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, int, GpuUInt128, GpuUInt128>(NttTransformKernels.ForwardKernel));
|
|
||||||
|
|
||||||
// TODO(NTT-OPT): Introduce stage-wise Cooley–Tukey kernels and a
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, GpuUInt128>>();
|
||||||
// per-(accelerator,length,modulus,root) twiddle cache. The plan:
|
pool[accelerator] = cached;
|
||||||
// - Precompute twiddle factors for all stages once and cache on GPU.
|
return cached;
|
||||||
// - Replace the current O(n^2) ForwardKernel with O(n log n) butterflies.
|
}
|
||||||
// - Provide accessors like GetStageKernel(...) and GetTwiddleBuffer(...).
|
|
||||||
|
|
||||||
private static Action<Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, int, GpuUInt128, GpuUInt128, GpuUInt128> GetInverseKernel(Accelerator accelerator) =>
|
private static Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, GpuUInt128, GpuUInt128> GetScaleKernel(Accelerator accelerator)
|
||||||
InverseKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, ArrayView<GpuUInt128>, int, GpuUInt128, GpuUInt128, GpuUInt128>(NttTransformKernels.InverseKernel));
|
{
|
||||||
|
var pool = _scaleKernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, GpuUInt128, GpuUInt128>(NttPointwiseKernels.ScaleKernel);
|
||||||
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, GpuUInt128, GpuUInt128>>();
|
||||||
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, GpuUInt128, ulong, ulong, ulong, ulong> GetScaleBarrett128Kernel(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
var pool = _scaleBarrett128Kernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, GpuUInt128, ulong, ulong, ulong, ulong>(NttBarrettKernels.ScaleBarrett128Kernel);
|
||||||
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, GpuUInt128, ulong, ulong, ulong, ulong>>();
|
||||||
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong> GetScaleMont64Kernel(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
var pool = _scaleMont64Kernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong>(NttMontgomeryKernels.ScaleMont64Kernel);
|
||||||
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong>>();
|
||||||
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, ulong, ulong, ulong, ulong> GetStageBarrett128Kernel(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
var pool = _stageBarrett128Kernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, ulong, ulong, ulong, ulong>(NttBarrettKernels.StageBarrett128Kernel);
|
||||||
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, ulong, ulong, ulong, ulong>>();
|
||||||
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, ulong, ulong> GetStageMontKernel(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
var pool = _stageMontKernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, ulong, ulong>(NttMontgomeryKernels.StageMontKernel);
|
||||||
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, int, int, int, ArrayView<GpuUInt128>, ulong, ulong>>();
|
||||||
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong> GetToMont64Kernel(Accelerator accelerator)
|
||||||
|
{
|
||||||
|
var pool = _toMont64Kernel ??= [];
|
||||||
|
if (pool.TryGetValue(accelerator, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong>(NttMontgomeryKernels.ToMont64Kernel);
|
||||||
|
|
||||||
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
|
|
||||||
|
cached = kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuUInt128>, ulong, ulong, ulong>>();
|
||||||
|
pool[accelerator] = cached;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(NTT-OPT): Mirror the forward stage-wise design for the inverse
|
// TODO(NTT-OPT): Mirror the forward stage-wise design for the inverse
|
||||||
// transform. Precompute inverse twiddles and a normalization factor
|
// transform. Precompute inverse twiddles and a normalization factor
|
||||||
@@ -446,21 +573,22 @@ public static class NttGpuMath
|
|||||||
{
|
{
|
||||||
int n = values.Length;
|
int n = values.Length;
|
||||||
int bits = (int)Math.Log2(n);
|
int bits = (int)Math.Log2(n);
|
||||||
var gpu = GpuContextPool.Rent();
|
var acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
var kernel = GetBitReverseKernel(accelerator);
|
var pool = ThreadStaticPools.GpuUInt128Pool;
|
||||||
var pool = ArrayPool<GpuUInt128>.Shared;
|
|
||||||
var array = pool.Rent(n);
|
var array = pool.Rent(n);
|
||||||
values.CopyTo(array);
|
values.CopyTo(array);
|
||||||
var buffer = accelerator.Allocate1D<GpuUInt128>(n);
|
var buffer = accelerator.Allocate1D<GpuUInt128>(n);
|
||||||
buffer.View.CopyFromCPU(ref array[0], n);
|
var stream = accelerator.CreateStream();
|
||||||
kernel(n, buffer.View, bits);
|
buffer.View.CopyFromCPU(stream, ref array[0], n);
|
||||||
accelerator.Synchronize();
|
var bitReverseKernel = GetBitReverseKernel(accelerator);
|
||||||
buffer.View.CopyToCPU(ref array[0], n);
|
bitReverseKernel(stream, n, buffer.View, bits);
|
||||||
|
buffer.View.CopyToCPU(stream, ref array[0], n);
|
||||||
|
stream.Synchronize();
|
||||||
array.AsSpan(0, n).CopyTo(values);
|
array.AsSpan(0, n).CopyTo(values);
|
||||||
pool.Return(array, clearArray: true);
|
pool.Return(array, clearArray: true);
|
||||||
buffer.Dispose();
|
buffer.Dispose();
|
||||||
gpu.Dispose();
|
stream.Dispose();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,30 +636,30 @@ public static class NttGpuMath
|
|||||||
private static void ForwardGpuReference(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
private static void ForwardGpuReference(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
||||||
{
|
{
|
||||||
int n = (int)values.Length;
|
int n = (int)values.Length;
|
||||||
var gpu = GpuContextPool.Rent();
|
var acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
var kernel = GetForwardKernel(accelerator);
|
var stream = accelerator.CreateStream();
|
||||||
var pool = ArrayPool<GpuUInt128>.Shared;
|
var pool = ThreadStaticPools.GpuUInt128Pool;
|
||||||
var inputArray = pool.Rent(n);
|
var inputArray = pool.Rent(n);
|
||||||
var outputArray = pool.Rent(n);
|
var outputArray = pool.Rent(n);
|
||||||
values.CopyTo(inputArray);
|
values.CopyTo(inputArray);
|
||||||
var inputBuffer = accelerator.Allocate1D<GpuUInt128>(n);
|
var inputBuffer = accelerator.Allocate1D<GpuUInt128>(n);
|
||||||
var outputBuffer = accelerator.Allocate1D<GpuUInt128>(n);
|
var outputBuffer = accelerator.Allocate1D<GpuUInt128>(n);
|
||||||
inputBuffer.View.CopyFromCPU(ref inputArray[0], n);
|
inputBuffer.View.CopyFromCPU(stream, ref inputArray[0], n);
|
||||||
UInt128 modValue = modulus;
|
UInt128 modValue = modulus;
|
||||||
UInt128 expBase = (modValue - 1UL) / (ulong)n;
|
UInt128 expBase = (modValue - 1UL) / (ulong)n;
|
||||||
var root = new GpuUInt128(primitiveRoot);
|
var root = new GpuUInt128(primitiveRoot);
|
||||||
root.ModPow((ulong)expBase, modulus);
|
root.ModPow((ulong)expBase, modulus);
|
||||||
kernel(n, inputBuffer.View, outputBuffer.View, n, modulus, root);
|
var forwardKernel = GetForwardKernel(accelerator);
|
||||||
accelerator.Synchronize();
|
forwardKernel(stream, n, inputBuffer.View, outputBuffer.View, n, modulus, root);
|
||||||
outputBuffer.View.CopyToCPU(ref outputArray[0], n);
|
outputBuffer.View.CopyToCPU(stream, ref outputArray[0], n);
|
||||||
|
stream.Synchronize();
|
||||||
outputArray.AsSpan(0, n).CopyTo(values);
|
outputArray.AsSpan(0, n).CopyTo(values);
|
||||||
pool.Return(inputArray, clearArray: true);
|
pool.Return(inputArray, clearArray: true);
|
||||||
pool.Return(outputArray, clearArray: true);
|
pool.Return(outputArray, clearArray: true);
|
||||||
outputBuffer.Dispose();
|
outputBuffer.Dispose();
|
||||||
inputBuffer.Dispose();
|
inputBuffer.Dispose();
|
||||||
gpu.Dispose();
|
stream.Dispose();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Inverse(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
public static void Inverse(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
||||||
@@ -605,16 +733,16 @@ public static class NttGpuMath
|
|||||||
private static void InverseGpuReference(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
private static void InverseGpuReference(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
||||||
{
|
{
|
||||||
int n = values.Length;
|
int n = values.Length;
|
||||||
var gpu = GpuContextPool.Rent();
|
var acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
var kernel = GetInverseKernel(accelerator);
|
var stream = accelerator.CreateStream();
|
||||||
var pool = ArrayPool<GpuUInt128>.Shared;
|
var pool = ThreadStaticPools.GpuUInt128Pool;
|
||||||
var inputArray = pool.Rent(n);
|
var inputArray = pool.Rent(n);
|
||||||
var outputArray = pool.Rent(n);
|
var outputArray = pool.Rent(n);
|
||||||
values.CopyTo(inputArray);
|
values.CopyTo(inputArray);
|
||||||
var inputBuffer = accelerator.Allocate1D<GpuUInt128>(n);
|
var inputBuffer = accelerator.Allocate1D<GpuUInt128>(n);
|
||||||
var outputBuffer = accelerator.Allocate1D<GpuUInt128>(n);
|
var outputBuffer = accelerator.Allocate1D<GpuUInt128>(n);
|
||||||
inputBuffer.View.CopyFromCPU(ref inputArray[0], n);
|
inputBuffer.View.CopyFromCPU(stream, ref inputArray[0], n);
|
||||||
UInt128 modValue = modulus;
|
UInt128 modValue = modulus;
|
||||||
UInt128 expBase = (modValue - 1UL) / (ulong)n;
|
UInt128 expBase = (modValue - 1UL) / (ulong)n;
|
||||||
var root = new GpuUInt128(primitiveRoot);
|
var root = new GpuUInt128(primitiveRoot);
|
||||||
@@ -638,70 +766,70 @@ public static class NttGpuMath
|
|||||||
{
|
{
|
||||||
nInv.ModInv(modulus);
|
nInv.ModInv(modulus);
|
||||||
}
|
}
|
||||||
kernel(n, inputBuffer.View, outputBuffer.View, n, modulus, rootInv, nInv);
|
|
||||||
accelerator.Synchronize();
|
var inverseKernel = GetInverseKernel(accelerator);
|
||||||
outputBuffer.View.CopyToCPU(ref outputArray[0], n);
|
inverseKernel(stream, n, inputBuffer.View, outputBuffer.View, n, modulus, rootInv, nInv);
|
||||||
|
outputBuffer.View.CopyToCPU(stream, ref outputArray[0], n);
|
||||||
|
stream.Synchronize();
|
||||||
outputArray.AsSpan(0, n).CopyTo(values);
|
outputArray.AsSpan(0, n).CopyTo(values);
|
||||||
pool.Return(inputArray, clearArray: true);
|
pool.Return(inputArray, clearArray: true);
|
||||||
pool.Return(outputArray, clearArray: true);
|
pool.Return(outputArray, clearArray: true);
|
||||||
outputBuffer.Dispose();
|
outputBuffer.Dispose();
|
||||||
inputBuffer.Dispose();
|
inputBuffer.Dispose();
|
||||||
gpu.Dispose();
|
stream.Dispose();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ForwardGpuStaged(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
private static void ForwardGpuStaged(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
||||||
{
|
{
|
||||||
int n = values.Length;
|
int n = values.Length;
|
||||||
var gpu = GpuContextPool.Rent();
|
var acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
|
var stream = accelerator.CreateStream();
|
||||||
var buffer = accelerator.Allocate1D<GpuUInt128>(n);
|
var buffer = accelerator.Allocate1D<GpuUInt128>(n);
|
||||||
buffer.View.CopyFromCPU(ref values[0], n);
|
buffer.View.CopyFromCPU(stream, ref values[0], n);
|
||||||
var cache = GetSquareCache(accelerator, n, modulus, primitiveRoot);
|
var cache = GetSquareCache(accelerator, stream, n, modulus, primitiveRoot);
|
||||||
ForwardDevice(accelerator, buffer.View, n, modulus, cache);
|
ForwardDevice(accelerator, stream, buffer.View, n, modulus, cache);
|
||||||
buffer.View.CopyToCPU(ref values[0], n);
|
buffer.View.CopyToCPU(stream, ref values[0], n);
|
||||||
|
stream.Synchronize();
|
||||||
|
stream.Dispose();
|
||||||
buffer.Dispose();
|
buffer.Dispose();
|
||||||
gpu.Dispose();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void InverseGpuStaged(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
private static void InverseGpuStaged(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
||||||
{
|
{
|
||||||
int n = values.Length;
|
int n = values.Length;
|
||||||
var gpu = GpuContextPool.Rent();
|
var acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
|
var stream = accelerator.CreateStream();
|
||||||
var buffer = accelerator.Allocate1D<GpuUInt128>(n);
|
var buffer = accelerator.Allocate1D<GpuUInt128>(n);
|
||||||
buffer.View.CopyFromCPU(ref values[0], n);
|
buffer.View.CopyFromCPU(stream, ref values[0], n);
|
||||||
var cache = GetSquareCache(accelerator, n, modulus, primitiveRoot);
|
var cache = GetSquareCache(accelerator, stream, n, modulus, primitiveRoot);
|
||||||
InverseDevice(accelerator, buffer.View, n, modulus, cache);
|
InverseDevice(accelerator, stream, buffer.View, n, modulus, cache);
|
||||||
buffer.View.CopyToCPU(ref values[0], n);
|
buffer.View.CopyToCPU(stream, ref values[0], n);
|
||||||
|
stream.Synchronize();
|
||||||
|
stream.Dispose();
|
||||||
buffer.Dispose();
|
buffer.Dispose();
|
||||||
gpu.Dispose();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ForwardDevice(Accelerator accelerator, ArrayView<GpuUInt128> data, int length, GpuUInt128 modulus, SquareCacheEntry cache)
|
private static void ForwardDevice(Accelerator accelerator, AcceleratorStream stream, ArrayView<GpuUInt128> data, int length, GpuUInt128 modulus, SquareCacheEntry cache)
|
||||||
{
|
{
|
||||||
var bitKernel = GetBitReverseKernel(accelerator);
|
var bitReverseKernel = GetBitReverseKernel(accelerator);
|
||||||
bitKernel(length, data, cache.Bits);
|
bitReverseKernel(stream, length, data, cache.Bits);
|
||||||
accelerator.Synchronize();
|
|
||||||
|
|
||||||
if (cache.UseMontgomery64)
|
if (cache.UseMontgomery64)
|
||||||
{
|
{
|
||||||
var toMont = GetToMont64Kernel(accelerator);
|
var toMont64Kernel = GetToMont64Kernel(accelerator);
|
||||||
toMont(length, data, cache.ModulusLow, cache.MontNPrime64, cache.MontR2Mod64);
|
toMont64Kernel(stream, length, data, cache.ModulusLow, cache.MontNPrime64, cache.MontR2Mod64);
|
||||||
accelerator.Synchronize();
|
|
||||||
var stageKernel = GetStageMontKernel(accelerator);
|
var stageKernel = GetStageMontKernel(accelerator);
|
||||||
int butterflies = length >> 1;
|
int butterflies = length >> 1;
|
||||||
for (int s = 0; s < cache.Bits; s++)
|
for (int s = 0; s < cache.Bits; s++)
|
||||||
{
|
{
|
||||||
int len = 1 << (s + 1);
|
int len = 1 << (s + 1);
|
||||||
int half = len >> 1;
|
int half = len >> 1;
|
||||||
stageKernel(butterflies, data, len, half, cache.StageOffsets[s], cache.TwiddlesMont!.View, cache.ModulusLow, cache.MontNPrime64);
|
stageKernel(stream, butterflies, data, len, half, cache.StageOffsets[s], cache.TwiddlesMont!.View, cache.ModulusLow, cache.MontNPrime64);
|
||||||
accelerator.Synchronize();
|
|
||||||
}
|
}
|
||||||
var fromMont = GetFromMont64Kernel(accelerator);
|
var fromMont64Kernel = GetFromMont64Kernel(accelerator);
|
||||||
fromMont(length, data, cache.ModulusLow, cache.MontNPrime64);
|
fromMont64Kernel(stream, length, data, cache.ModulusLow, cache.MontNPrime64);
|
||||||
}
|
}
|
||||||
else if (cache.UseBarrett128)
|
else if (cache.UseBarrett128)
|
||||||
{
|
{
|
||||||
@@ -711,8 +839,7 @@ public static class NttGpuMath
|
|||||||
{
|
{
|
||||||
int len = 1 << (s + 1);
|
int len = 1 << (s + 1);
|
||||||
int half = len >> 1;
|
int half = len >> 1;
|
||||||
stageKernel(butterflies, data, len, half, cache.StageOffsets[s], cache.Twiddles.View, cache.ModulusHigh, cache.ModulusLow, cache.BarrettMuHigh, cache.BarrettMuLow);
|
stageKernel(stream, butterflies, data, len, half, cache.StageOffsets[s], cache.Twiddles.View, cache.ModulusHigh, cache.ModulusLow, cache.BarrettMuHigh, cache.BarrettMuLow);
|
||||||
accelerator.Synchronize();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -723,39 +850,36 @@ public static class NttGpuMath
|
|||||||
{
|
{
|
||||||
int len = 1 << (s + 1);
|
int len = 1 << (s + 1);
|
||||||
int half = len >> 1;
|
int half = len >> 1;
|
||||||
stageKernel(butterflies, data, len, half, cache.StageOffsets[s], cache.Twiddles.View, modulus);
|
stageKernel(stream, butterflies, data, len, half, cache.StageOffsets[s], cache.Twiddles.View, modulus);
|
||||||
accelerator.Synchronize();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
accelerator.Synchronize();
|
stream.Synchronize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void InverseDevice(Accelerator accelerator, ArrayView<GpuUInt128> data, int length, GpuUInt128 modulus, SquareCacheEntry cache)
|
private static void InverseDevice(Accelerator accelerator, AcceleratorStream stream, ArrayView<GpuUInt128> data, int length, GpuUInt128 modulus, SquareCacheEntry cache)
|
||||||
{
|
{
|
||||||
var bitKernel = GetBitReverseKernel(accelerator);
|
var bitReverseKernel = GetBitReverseKernel(accelerator);
|
||||||
bitKernel(length, data, cache.Bits);
|
bitReverseKernel(stream, length, data, cache.Bits);
|
||||||
accelerator.Synchronize();
|
|
||||||
|
|
||||||
if (cache.UseMontgomery64)
|
if (cache.UseMontgomery64)
|
||||||
{
|
{
|
||||||
var toMont = GetToMont64Kernel(accelerator);
|
var toMont64Kernel = GetToMont64Kernel(accelerator);
|
||||||
toMont(length, data, cache.ModulusLow, cache.MontNPrime64, cache.MontR2Mod64);
|
toMont64Kernel(stream, length, data, cache.ModulusLow, cache.MontNPrime64, cache.MontR2Mod64);
|
||||||
accelerator.Synchronize();
|
|
||||||
var stageKernel = GetStageMontKernel(accelerator);
|
var stageKernel = GetStageMontKernel(accelerator);
|
||||||
int butterflies = length >> 1;
|
int butterflies = length >> 1;
|
||||||
for (int s = 0; s < cache.Bits; s++)
|
for (int s = 0; s < cache.Bits; s++)
|
||||||
{
|
{
|
||||||
int len = 1 << (s + 1);
|
int len = 1 << (s + 1);
|
||||||
int half = len >> 1;
|
int half = len >> 1;
|
||||||
stageKernel(butterflies, data, len, half, cache.StageOffsets[s], cache.TwiddlesInvMont!.View, cache.ModulusLow, cache.MontNPrime64);
|
stageKernel(stream, butterflies, data, len, half, cache.StageOffsets[s], cache.TwiddlesInvMont!.View, cache.ModulusLow, cache.MontNPrime64);
|
||||||
accelerator.Synchronize();
|
|
||||||
}
|
}
|
||||||
var scaleKernel = GetScaleMont64Kernel(accelerator);
|
|
||||||
scaleKernel(length, data, cache.ModulusLow, cache.MontNPrime64, cache.MontNInvR64);
|
var scaleMont64Kernel = GetScaleMont64Kernel(accelerator);
|
||||||
accelerator.Synchronize();
|
scaleMont64Kernel(stream, length, data, cache.ModulusLow, cache.MontNPrime64, cache.MontNInvR64);
|
||||||
var fromMont = GetFromMont64Kernel(accelerator);
|
|
||||||
fromMont(length, data, cache.ModulusLow, cache.MontNPrime64);
|
var fromMont64Kernel = GetFromMont64Kernel(accelerator);
|
||||||
|
fromMont64Kernel(stream, length, data, cache.ModulusLow, cache.MontNPrime64);
|
||||||
}
|
}
|
||||||
else if (cache.UseBarrett128)
|
else if (cache.UseBarrett128)
|
||||||
{
|
{
|
||||||
@@ -765,12 +889,11 @@ public static class NttGpuMath
|
|||||||
{
|
{
|
||||||
int len = 1 << (s + 1);
|
int len = 1 << (s + 1);
|
||||||
int half = len >> 1;
|
int half = len >> 1;
|
||||||
stageKernel(butterflies, data, len, half, cache.StageOffsets[s], cache.TwiddlesInv.View, cache.ModulusHigh, cache.ModulusLow, cache.BarrettMuHigh, cache.BarrettMuLow);
|
stageKernel(stream, butterflies, data, len, half, cache.StageOffsets[s], cache.TwiddlesInv.View, cache.ModulusHigh, cache.ModulusLow, cache.BarrettMuHigh, cache.BarrettMuLow);
|
||||||
accelerator.Synchronize();
|
|
||||||
}
|
}
|
||||||
var scaleKernel = GetScaleBarrett128Kernel(accelerator);
|
|
||||||
scaleKernel(length, data, cache.NInv, cache.ModulusHigh, cache.ModulusLow, cache.BarrettMuHigh, cache.BarrettMuLow);
|
var scaleBarrett128Kernel = GetScaleBarrett128Kernel(accelerator);
|
||||||
accelerator.Synchronize();
|
scaleBarrett128Kernel(stream, length, data, cache.NInv, cache.ModulusHigh, cache.ModulusLow, cache.BarrettMuHigh, cache.BarrettMuLow);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -780,15 +903,14 @@ public static class NttGpuMath
|
|||||||
{
|
{
|
||||||
int len = 1 << (s + 1);
|
int len = 1 << (s + 1);
|
||||||
int half = len >> 1;
|
int half = len >> 1;
|
||||||
stageKernel(butterflies, data, len, half, cache.StageOffsets[s], cache.TwiddlesInv.View, modulus);
|
stageKernel(stream, butterflies, data, len, half, cache.StageOffsets[s], cache.TwiddlesInv.View, modulus);
|
||||||
accelerator.Synchronize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var scaleKernel = GetScaleKernel(accelerator);
|
var scaleKernel = GetScaleKernel(accelerator);
|
||||||
scaleKernel(length, data, cache.NInv, modulus);
|
scaleKernel(stream, length, data, cache.NInv, modulus);
|
||||||
accelerator.Synchronize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
accelerator.Synchronize();
|
stream.Synchronize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Convolve(Span<GpuUInt128> left, Span<GpuUInt128> right, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
public static void Convolve(Span<GpuUInt128> left, Span<GpuUInt128> right, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
||||||
@@ -800,7 +922,7 @@ public static class NttGpuMath
|
|||||||
n <<= 1;
|
n <<= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pool = ArrayPool<GpuUInt128>.Shared;
|
var pool = ThreadStaticPools.GpuUInt128Pool;
|
||||||
var aBuffer = pool.Rent(n);
|
var aBuffer = pool.Rent(n);
|
||||||
var bBuffer = pool.Rent(n);
|
var bBuffer = pool.Rent(n);
|
||||||
var a = aBuffer.AsSpan(0, n);
|
var a = aBuffer.AsSpan(0, n);
|
||||||
@@ -837,7 +959,7 @@ public static class NttGpuMath
|
|||||||
n <<= 1;
|
n <<= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pool = ArrayPool<GpuUInt128>.Shared;
|
var pool = ThreadStaticPools.GpuUInt128Pool;
|
||||||
var aBuffer = pool.Rent(n);
|
var aBuffer = pool.Rent(n);
|
||||||
var bBuffer = pool.Rent(n);
|
var bBuffer = pool.Rent(n);
|
||||||
var a = aBuffer.AsSpan(0, n);
|
var a = aBuffer.AsSpan(0, n);
|
||||||
@@ -871,41 +993,43 @@ public static class NttGpuMath
|
|||||||
public static void SquareGpu(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot) =>
|
public static void SquareGpu(Span<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot) =>
|
||||||
ConvolveGpu(values, values, modulus, primitiveRoot);
|
ConvolveGpu(values, values, modulus, primitiveRoot);
|
||||||
|
|
||||||
public static void SquareDevice(Accelerator accelerator, ArrayView<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
public static void SquareDevice(Accelerator accelerator, AcceleratorStream stream, ArrayView<GpuUInt128> values, GpuUInt128 modulus, GpuUInt128 primitiveRoot)
|
||||||
{
|
{
|
||||||
var cache = GetSquareCache(accelerator, (int)values.Length, modulus, primitiveRoot);
|
var cache = GetSquareCache(accelerator, stream, (int)values.Length, modulus, primitiveRoot);
|
||||||
ForwardDevice(accelerator, values, (int)values.Length, modulus, cache);
|
ForwardDevice(accelerator, stream, values, (int)values.Length, modulus, cache);
|
||||||
var mul = GetMulKernel(accelerator);
|
var mulKernel = GetMulKernel(accelerator);
|
||||||
mul((int)values.Length, values, values, modulus);
|
mulKernel(stream, (int)values.Length, values, values, modulus);
|
||||||
InverseDevice(accelerator, values, (int)values.Length, modulus, cache);
|
InverseDevice(accelerator, stream, values, (int)values.Length, modulus, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
|
||||||
public static void PointwiseMultiply(Span<GpuUInt128> left, Span<GpuUInt128> right, GpuUInt128 modulus)
|
public static void PointwiseMultiply(Span<GpuUInt128> left, Span<GpuUInt128> right, GpuUInt128 modulus)
|
||||||
{
|
{
|
||||||
var gpu = GpuContextPool.Rent();
|
var acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
var kernel = GetMulKernel(accelerator);
|
var stream = accelerator.CreateStream();
|
||||||
var pool = ArrayPool<GpuUInt128>.Shared;
|
var pool = ThreadStaticPools.GpuUInt128Pool;
|
||||||
var leftArray = pool.Rent(left.Length);
|
var leftArray = pool.Rent(left.Length);
|
||||||
var rightArray = pool.Rent(right.Length);
|
var rightArray = pool.Rent(right.Length);
|
||||||
left.CopyTo(leftArray);
|
left.CopyTo(leftArray);
|
||||||
right.CopyTo(rightArray);
|
right.CopyTo(rightArray);
|
||||||
var leftBuffer = accelerator.Allocate1D<GpuUInt128>(left.Length);
|
var leftBuffer = accelerator.Allocate1D<GpuUInt128>(left.Length);
|
||||||
var rightBuffer = accelerator.Allocate1D<GpuUInt128>(right.Length);
|
var rightBuffer = accelerator.Allocate1D<GpuUInt128>(right.Length);
|
||||||
leftBuffer.View.CopyFromCPU(ref leftArray[0], left.Length);
|
leftBuffer.View.CopyFromCPU(stream, ref leftArray[0], left.Length);
|
||||||
rightBuffer.View.CopyFromCPU(ref rightArray[0], right.Length);
|
rightBuffer.View.CopyFromCPU(stream, ref rightArray[0], right.Length);
|
||||||
kernel(left.Length, leftBuffer.View, rightBuffer.View, modulus);
|
var mulKernel = GetMulKernel(accelerator);
|
||||||
accelerator.Synchronize();
|
mulKernel(stream, left.Length, leftBuffer.View, rightBuffer.View, modulus);
|
||||||
leftBuffer.View.CopyToCPU(ref leftArray[0], left.Length);
|
leftBuffer.View.CopyToCPU(stream, ref leftArray[0], left.Length);
|
||||||
rightBuffer.View.CopyToCPU(ref rightArray[0], right.Length);
|
rightBuffer.View.CopyToCPU(stream, ref rightArray[0], right.Length);
|
||||||
|
stream.Synchronize();
|
||||||
leftArray.AsSpan(0, left.Length).CopyTo(left);
|
leftArray.AsSpan(0, left.Length).CopyTo(left);
|
||||||
rightArray.AsSpan(0, right.Length).CopyTo(right);
|
rightArray.AsSpan(0, right.Length).CopyTo(right);
|
||||||
pool.Return(leftArray, clearArray: true);
|
pool.Return(leftArray, clearArray: true);
|
||||||
pool.Return(rightArray, clearArray: true);
|
pool.Return(rightArray, clearArray: true);
|
||||||
rightBuffer.Dispose();
|
rightBuffer.Dispose();
|
||||||
leftBuffer.Dispose();
|
leftBuffer.Dispose();
|
||||||
gpu.Dispose();
|
stream.Dispose();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ulong[] GenerateSmallPrimes(int limit)
|
private static ulong[] GenerateSmallPrimes(int limit)
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using ILGPU;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
|
public static class Pow2MontgomeryGpuCalculator
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ulong CalculateKeep(ulong exponent, in MontgomeryDivisorData divisor)
|
||||||
|
{
|
||||||
|
// GpuPrimeWorkLimiter.Acquire();
|
||||||
|
|
||||||
|
ulong result = 0UL;
|
||||||
|
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||||
|
int acceleratorIndex = gpu.AcceleratorIndex;
|
||||||
|
Accelerator? accelerator = gpu.Accelerator;
|
||||||
|
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input;
|
||||||
|
MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.OutputUlong;
|
||||||
|
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
|
exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1);
|
||||||
|
// We don't need to worry about any left-overs here.
|
||||||
|
// resultBuffer.MemSetToZero(stream);
|
||||||
|
|
||||||
|
var kernelLauncher = gpu.KeepMontgomeryKernelLauncher;
|
||||||
|
kernelLauncher(stream, 1, exponentBuffer.View, divisor, resultBuffer.View);
|
||||||
|
|
||||||
|
resultBuffer.View.CopyToCPU(stream, ref result, 1);
|
||||||
|
stream.Synchronize();
|
||||||
|
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||||
|
// GpuPrimeWorkLimiter.Release();
|
||||||
|
|
||||||
|
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ulong CalculateConvert(ulong exponent, in MontgomeryDivisorData divisor)
|
||||||
|
{
|
||||||
|
// GpuPrimeWorkLimiter.Acquire();
|
||||||
|
|
||||||
|
ulong result = 0UL;
|
||||||
|
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||||
|
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input;
|
||||||
|
MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.OutputUlong;
|
||||||
|
|
||||||
|
int acceleratorIndex = gpu.AcceleratorIndex;
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
|
exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1);
|
||||||
|
// We don't need to worry about any left-overs here.
|
||||||
|
// resultBuffer.MemSetToZero(stream);
|
||||||
|
|
||||||
|
var kernelLauncher = gpu.ConvertToStandardKernelLauncher!;
|
||||||
|
|
||||||
|
kernelLauncher(stream, 1, exponentBuffer.View, divisor, resultBuffer.View);
|
||||||
|
|
||||||
|
resultBuffer.View.CopyToCPU(stream, ref result, 1);
|
||||||
|
stream.Synchronize();
|
||||||
|
|
||||||
|
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||||
|
// GpuPrimeWorkLimiter.Release();
|
||||||
|
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ulong CalculateConvert(PrimeOrderCalculatorAccelerator gpu, ulong exponent, in MontgomeryDivisorData divisor)
|
||||||
|
{
|
||||||
|
ulong result = 0UL;
|
||||||
|
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input;
|
||||||
|
MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.OutputUlong;
|
||||||
|
|
||||||
|
int acceleratorIndex = gpu.AcceleratorIndex;
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
|
exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1);
|
||||||
|
// We don't need to worry about any left-overs here.
|
||||||
|
// resultBuffer.MemSetToZero(stream);
|
||||||
|
|
||||||
|
|
||||||
|
var accelerator = gpu.Accelerator;
|
||||||
|
var kernelLauncher = gpu.ConvertToStandardKernelLauncher;
|
||||||
|
kernelLauncher(stream, 1, exponentBuffer.View, divisor, resultBuffer.View);
|
||||||
|
|
||||||
|
resultBuffer.View.CopyToCPU(stream, ref result, 1);
|
||||||
|
stream.Synchronize();
|
||||||
|
|
||||||
|
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
using System.Numerics;
|
|
||||||
using PerfectNumbers.Core;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using ILGPU.Runtime.OpenCL;
|
using ILGPU.Runtime.OpenCL;
|
||||||
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
internal enum GpuPow2ModStatus
|
internal enum GpuPow2ModStatus
|
||||||
@@ -18,13 +16,22 @@ internal enum GpuPow2ModStatus
|
|||||||
|
|
||||||
internal static partial class PrimeOrderGpuHeuristics
|
internal static partial class PrimeOrderGpuHeuristics
|
||||||
{
|
{
|
||||||
|
internal static void PreloadStaticTables(AcceleratorStream stream)
|
||||||
|
{
|
||||||
|
// _ = GetSmallPrimeDeviceCache(stream);
|
||||||
|
}
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<ulong, byte> OverflowedPrimes = new();
|
private static readonly ConcurrentDictionary<ulong, byte> OverflowedPrimes = new();
|
||||||
private static readonly ConcurrentDictionary<UInt128, byte> OverflowedPrimesWide = new();
|
private static readonly ConcurrentDictionary<UInt128, byte> OverflowedPrimesWide = new();
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>> Pow2ModKernelCache = new();
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>>> Pow2ModKernelWideCache = new();
|
[ThreadStatic]
|
||||||
private static readonly ConcurrentDictionary<Accelerator, Action<AcceleratorStream, Index1D, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, int, ulong, uint, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>> PartialFactorKernelCache = new();
|
private static Kernel[]? _pow2ModKernel;
|
||||||
private static readonly ConcurrentDictionary<Accelerator, OrderKernelLauncher> OrderKernelCache = new();
|
|
||||||
private static readonly ConcurrentDictionary<Accelerator, SmallPrimeDeviceCache> SmallPrimeDeviceCaches = new();
|
[ThreadStatic]
|
||||||
|
private static Kernel[]? _partialFactorKernel;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Kernel[]? _orderKernel;
|
||||||
|
|
||||||
public readonly struct OrderKernelConfig(ulong previousOrder, byte hasPreviousOrder, uint smallFactorLimit, int maxPowChecks, int mode)
|
public readonly struct OrderKernelConfig(ulong previousOrder, byte hasPreviousOrder, uint smallFactorLimit, int maxPowChecks, int mode)
|
||||||
{
|
{
|
||||||
@@ -59,25 +66,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
public readonly ArrayView1D<byte, Stride1D.Dense> Status = status;
|
public readonly ArrayView1D<byte, Stride1D.Dense> Status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
private delegate void OrderKernelLauncher(
|
private const int WideStackThreshold = 12;
|
||||||
AcceleratorStream stream,
|
|
||||||
Index1D extent,
|
|
||||||
ulong prime,
|
|
||||||
OrderKernelConfig config,
|
|
||||||
MontgomeryDivisorData divisor,
|
|
||||||
ArrayView1D<uint, Stride1D.Dense> primes,
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> squares,
|
|
||||||
int primeCount,
|
|
||||||
OrderKernelBuffers buffers);
|
|
||||||
|
|
||||||
private sealed class SmallPrimeDeviceCache
|
|
||||||
{
|
|
||||||
public MemoryBuffer1D<uint, Stride1D.Dense>? Primes;
|
|
||||||
public MemoryBuffer1D<ulong, Stride1D.Dense>? Squares;
|
|
||||||
public int Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
private const int WideStackThreshold = 8;
|
|
||||||
private static PrimeOrderGpuCapability s_capability = PrimeOrderGpuCapability.Default;
|
private static PrimeOrderGpuCapability s_capability = PrimeOrderGpuCapability.Default;
|
||||||
|
|
||||||
private const int Pow2WindowSizeBits = 8;
|
private const int Pow2WindowSizeBits = 8;
|
||||||
@@ -101,47 +90,33 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
s_capability = PrimeOrderGpuCapability.Default;
|
s_capability = PrimeOrderGpuCapability.Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SmallPrimeDeviceCache GetSmallPrimeDeviceCache(Accelerator accelerator)
|
private static Kernel GetPartialFactorKernel(int acceleratorIndex)
|
||||||
{
|
{
|
||||||
return SmallPrimeDeviceCaches.GetOrAdd(accelerator, static acc =>
|
var pool = _partialFactorKernel ??= [];
|
||||||
|
if (pool[acceleratorIndex] is {} cached)
|
||||||
{
|
{
|
||||||
uint[] primes = PrimesGenerator.SmallPrimes;
|
return cached;
|
||||||
ulong[] squares = PrimesGenerator.SmallPrimesPow2;
|
|
||||||
var primeBuffer = acc.Allocate1D<uint>(primes.Length);
|
|
||||||
primeBuffer.View.CopyFromCPU(primes);
|
|
||||||
var squareBuffer = acc.Allocate1D<ulong>(squares.Length);
|
|
||||||
squareBuffer.View.CopyFromCPU(squares);
|
|
||||||
return new SmallPrimeDeviceCache
|
|
||||||
{
|
|
||||||
Primes = primeBuffer,
|
|
||||||
Squares = squareBuffer,
|
|
||||||
Count = primes.Length,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Action<AcceleratorStream, Index1D, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, int, ulong, uint, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>> GetPartialFactorKernel(Accelerator accelerator)
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
{
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, int, ulong, uint, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>(PartialFactorKernel);
|
||||||
return PartialFactorKernelCache.GetOrAdd(accelerator, static accel =>
|
|
||||||
{
|
cached = KernelUtil.GetKernel(loaded);
|
||||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, int, ulong, uint, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>(PartialFactorKernel);
|
pool[acceleratorIndex] = cached;
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
return cached;
|
||||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, int, ulong, uint, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>>();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryPartialFactor(
|
public static bool TryPartialFactor(
|
||||||
|
PrimeOrderCalculatorAccelerator gpu,
|
||||||
ulong value,
|
ulong value,
|
||||||
uint limit,
|
uint limit,
|
||||||
Span<ulong> primeTargets,
|
Span<ulong> primeTargets,
|
||||||
Span<int> exponentTargets,
|
Span<int> exponentTargets,
|
||||||
out int factorCount,
|
out int factorCount,
|
||||||
out ulong remaining,
|
out ulong remaining)
|
||||||
out bool fullyFactored)
|
|
||||||
{
|
{
|
||||||
factorCount = 0;
|
factorCount = 0;
|
||||||
remaining = value;
|
remaining = value;
|
||||||
fullyFactored = false;
|
|
||||||
|
|
||||||
if (primeTargets.Length == 0 || exponentTargets.Length == 0)
|
if (primeTargets.Length == 0 || exponentTargets.Length == 0)
|
||||||
{
|
{
|
||||||
@@ -152,13 +127,13 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
exponentTargets.Clear();
|
exponentTargets.Clear();
|
||||||
|
|
||||||
if (!TryLaunchPartialFactorKernel(
|
if (!TryLaunchPartialFactorKernel(
|
||||||
|
gpu,
|
||||||
value,
|
value,
|
||||||
limit,
|
limit,
|
||||||
primeTargets,
|
primeTargets,
|
||||||
exponentTargets,
|
exponentTargets,
|
||||||
out int extracted,
|
out int extracted,
|
||||||
out ulong leftover,
|
out ulong leftover))
|
||||||
out bool kernelFullyFactored))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -168,58 +143,61 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
{
|
{
|
||||||
factorCount = 0;
|
factorCount = 0;
|
||||||
remaining = value;
|
remaining = value;
|
||||||
fullyFactored = false;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
factorCount = extracted;
|
factorCount = extracted;
|
||||||
remaining = leftover;
|
remaining = leftover;
|
||||||
fullyFactored = kernelFullyFactored && leftover == 1UL;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||||
|
|
||||||
private static bool TryLaunchPartialFactorKernel(
|
private static bool TryLaunchPartialFactorKernel(
|
||||||
|
PrimeOrderCalculatorAccelerator gpu,
|
||||||
ulong value,
|
ulong value,
|
||||||
uint limit,
|
uint limit,
|
||||||
Span<ulong> primeTargets,
|
Span<ulong> primeTargets,
|
||||||
Span<int> exponentTargets,
|
Span<int> exponentTargets,
|
||||||
out int factorCount,
|
out int factorCount,
|
||||||
out ulong remaining,
|
out ulong remaining)
|
||||||
out bool fullyFactored)
|
|
||||||
{
|
{
|
||||||
factorCount = 0;
|
factorCount = 0;
|
||||||
remaining = value;
|
remaining = value;
|
||||||
fullyFactored = false;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
// GpuPrimeWorkLimiter.Acquire();
|
||||||
Accelerator accelerator = lease.Accelerator;
|
int acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
AcceleratorStream stream = lease.Stream;
|
Accelerator accelerator = _accelerators[acceleratorIndex];
|
||||||
|
AcceleratorStream stream = accelerator.CreateStream();
|
||||||
|
|
||||||
var kernel = GetPartialFactorKernel(accelerator);
|
var smallPrimesView = gpu.SmallPrimeFactorPrimes;
|
||||||
SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator);
|
var smallSquaresView = gpu.SmallPrimeFactorSquares;
|
||||||
|
|
||||||
// TODO: We should create / reallocate these buffer only once or if the new required length is bigger than capacity.
|
// TODO: We should create / reallocate these buffer only once or if the new required length is bigger than capacity.
|
||||||
var factorBuffer = accelerator.Allocate1D<ulong>(primeTargets.Length);
|
MemoryBuffer1D<ulong, Stride1D.Dense>? factorBuffer = accelerator.Allocate1D<ulong>(primeTargets.Length);
|
||||||
var exponentBuffer = accelerator.Allocate1D<int>(exponentTargets.Length);
|
MemoryBuffer1D<int, Stride1D.Dense>? exponentBuffer = accelerator.Allocate1D<int>(exponentTargets.Length);
|
||||||
var countBuffer = accelerator.Allocate1D<int>(1);
|
MemoryBuffer1D<int, Stride1D.Dense>? countBuffer = accelerator.Allocate1D<int>(1);
|
||||||
var remainingBuffer = accelerator.Allocate1D<ulong>(1);
|
MemoryBuffer1D<ulong, Stride1D.Dense>? remainingBuffer = accelerator.Allocate1D<ulong>(1);
|
||||||
var fullyFactoredBuffer = accelerator.Allocate1D<byte>(1);
|
MemoryBuffer1D<byte, Stride1D.Dense>? fullyFactoredBuffer = accelerator.Allocate1D<byte>(1);
|
||||||
|
|
||||||
// TODO: There is no need to clear these buffers because the kernel will always assign values within the required bounds.
|
// There is no need to clear these buffers because the kernel will always assign values within the required bounds. Keep it commented out.
|
||||||
// factorBuffer.MemSetToZero();
|
// factorBuffer.MemSetToZero(stream);
|
||||||
// exponentBuffer.MemSetToZero();
|
// exponentBuffer.MemSetToZero(stream);
|
||||||
// countBuffer.MemSetToZero();
|
// countBuffer.MemSetToZero(stream);
|
||||||
// remainingBuffer.MemSetToZero();
|
// remainingBuffer.MemSetToZero(stream);
|
||||||
// fullyFactoredBuffer.MemSetToZero();
|
// fullyFactoredBuffer.MemSetToZero(stream);
|
||||||
|
|
||||||
kernel(
|
var partialFactorKernel = GetPartialFactorKernel(acceleratorIndex);
|
||||||
|
var kernelLauncher = partialFactorKernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, int, ulong, uint, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>>>();
|
||||||
|
|
||||||
|
kernelLauncher(
|
||||||
stream,
|
stream,
|
||||||
1,
|
1,
|
||||||
cache.Primes!.View,
|
smallPrimesView,
|
||||||
cache.Squares!.View,
|
smallSquaresView,
|
||||||
cache.Count,
|
(int)smallPrimesView.Length,
|
||||||
primeTargets.Length,
|
primeTargets.Length,
|
||||||
value,
|
value,
|
||||||
limit,
|
limit,
|
||||||
@@ -229,17 +207,18 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
remainingBuffer.View,
|
remainingBuffer.View,
|
||||||
fullyFactoredBuffer.View);
|
fullyFactoredBuffer.View);
|
||||||
|
|
||||||
stream.Synchronize();
|
|
||||||
|
|
||||||
countBuffer.View.CopyToCPU(ref factorCount, 1);
|
countBuffer.View.CopyToCPU(stream, ref factorCount, 1);
|
||||||
factorCount = Math.Min(factorCount, primeTargets.Length);
|
factorBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(primeTargets), primeTargets.Length);
|
||||||
factorBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(primeTargets), primeTargets.Length);
|
exponentBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(exponentTargets), exponentTargets.Length);
|
||||||
exponentBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(exponentTargets), exponentTargets.Length);
|
remainingBuffer.View.CopyToCPU(stream, ref remaining, 1);
|
||||||
remainingBuffer.View.CopyToCPU(ref remaining, 1);
|
|
||||||
|
|
||||||
byte fullyFactoredFlag = 0;
|
byte fullyFactoredFlag = 0;
|
||||||
fullyFactoredBuffer.View.CopyToCPU(ref fullyFactoredFlag, 1);
|
fullyFactoredBuffer.View.CopyToCPU(stream, ref fullyFactoredFlag, 1);
|
||||||
fullyFactored = fullyFactoredFlag != 0;
|
stream.Synchronize();
|
||||||
|
stream.Dispose();
|
||||||
|
|
||||||
|
factorCount = Math.Min(factorCount, primeTargets.Length);
|
||||||
|
|
||||||
// TODO: These buffers shouldn't be disposed but rather reused accross call with the assigned accelerator.
|
// TODO: These buffers shouldn't be disposed but rather reused accross call with the assigned accelerator.
|
||||||
factorBuffer.Dispose();
|
factorBuffer.Dispose();
|
||||||
@@ -247,24 +226,18 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
countBuffer.Dispose();
|
countBuffer.Dispose();
|
||||||
remainingBuffer.Dispose();
|
remainingBuffer.Dispose();
|
||||||
fullyFactoredBuffer.Dispose();
|
fullyFactoredBuffer.Dispose();
|
||||||
lease.Dispose();
|
// GpuPrimeWorkLimiter.Release();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (CLException ex)
|
catch (CLException ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"GPU ERROR ({ex.Error}): {ex.Message}");
|
Console.WriteLine($"GPU ERROR ({ex.Error}): {ex.Message}");
|
||||||
factorCount = 0;
|
throw;
|
||||||
remaining = value;
|
|
||||||
fullyFactored = false;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (ex is AcceleratorException or InternalCompilerException or NotSupportedException or InvalidOperationException or AggregateException)
|
catch (Exception ex) when (ex is AcceleratorException or InternalCompilerException or NotSupportedException or InvalidOperationException or AggregateException)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"GPU ERROR: {ex.Message}");
|
Console.WriteLine($"GPU ERROR: {ex.Message}");
|
||||||
factorCount = 0;
|
throw;
|
||||||
remaining = value;
|
|
||||||
fullyFactored = false;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,6 +324,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal static bool TryCalculateOrder(
|
internal static bool TryCalculateOrder(
|
||||||
|
PrimeOrderCalculatorAccelerator gpu,
|
||||||
ulong prime,
|
ulong prime,
|
||||||
ulong? previousOrder,
|
ulong? previousOrder,
|
||||||
PrimeOrderCalculator.PrimeOrderSearchConfig config,
|
PrimeOrderCalculator.PrimeOrderSearchConfig config,
|
||||||
@@ -359,38 +333,52 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
{
|
{
|
||||||
order = 0UL;
|
order = 0UL;
|
||||||
|
|
||||||
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
// GpuPrimeWorkLimiter.Acquire();
|
||||||
Accelerator accelerator = lease.Accelerator;
|
int acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
AcceleratorStream stream = lease.Stream;
|
Accelerator accelerator = _accelerators[acceleratorIndex];
|
||||||
|
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
var kernel = GetOrderKernel(accelerator);
|
var smallPrimesView = gpu.SmallPrimeFactorPrimes;
|
||||||
SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator);
|
var smallSquaresView = gpu.SmallPrimeFactorSquares;
|
||||||
|
|
||||||
// TODO: These buffers should be created reallocated once and assigned to an accelerator. Callers should use their own
|
// TODO: These buffers should be created reallocated once and assigned to an accelerator. Callers should use their own
|
||||||
// thread static cache to prevent other threads from using taking accelerators with pre-allocated buffers if they don't
|
// thread static cache to prevent other threads from using taking accelerators with pre-allocated buffers if they don't
|
||||||
// use them.
|
// use them.
|
||||||
var phiFactorBuffer = accelerator.Allocate1D<ulong>(GpuSmallPrimeFactorSlots);
|
MemoryBuffer1D<ulong, Stride1D.Dense>? phiFactorBuffer;
|
||||||
var phiExponentBuffer = accelerator.Allocate1D<int>(GpuSmallPrimeFactorSlots);
|
MemoryBuffer1D<int, Stride1D.Dense>? phiExponentBuffer;
|
||||||
var workFactorBuffer = accelerator.Allocate1D<ulong>(GpuSmallPrimeFactorSlots);
|
MemoryBuffer1D<ulong, Stride1D.Dense>? workFactorBuffer;
|
||||||
var workExponentBuffer = accelerator.Allocate1D<int>(GpuSmallPrimeFactorSlots);
|
MemoryBuffer1D<int, Stride1D.Dense>? workExponentBuffer;
|
||||||
var candidateBuffer = accelerator.Allocate1D<ulong>(HeuristicCandidateLimit);
|
MemoryBuffer1D<ulong, Stride1D.Dense>? candidateBuffer;
|
||||||
var stackIndexBuffer = accelerator.Allocate1D<int>(HeuristicStackCapacity);
|
MemoryBuffer1D<int, Stride1D.Dense>? stackIndexBuffer;
|
||||||
var stackExponentBuffer = accelerator.Allocate1D<int>(HeuristicStackCapacity);
|
MemoryBuffer1D<int, Stride1D.Dense>? stackExponentBuffer;
|
||||||
var stackProductBuffer = accelerator.Allocate1D<ulong>(HeuristicStackCapacity);
|
MemoryBuffer1D<ulong, Stride1D.Dense>? stackProductBuffer;
|
||||||
var resultBuffer = accelerator.Allocate1D<ulong>(1);
|
MemoryBuffer1D<ulong, Stride1D.Dense>? resultBuffer;
|
||||||
var statusBuffer = accelerator.Allocate1D<byte>(1);
|
MemoryBuffer1D<byte, Stride1D.Dense>? statusBuffer;
|
||||||
|
|
||||||
|
lock(accelerator)
|
||||||
|
{
|
||||||
|
phiFactorBuffer = accelerator.Allocate1D<ulong>(GpuSmallPrimeFactorSlots);
|
||||||
|
phiExponentBuffer = accelerator.Allocate1D<int>(GpuSmallPrimeFactorSlots);
|
||||||
|
workFactorBuffer = accelerator.Allocate1D<ulong>(GpuSmallPrimeFactorSlots);
|
||||||
|
workExponentBuffer = accelerator.Allocate1D<int>(GpuSmallPrimeFactorSlots);
|
||||||
|
candidateBuffer = accelerator.Allocate1D<ulong>(HeuristicCandidateLimit);
|
||||||
|
stackIndexBuffer = accelerator.Allocate1D<int>(HeuristicStackCapacity);
|
||||||
|
stackExponentBuffer = accelerator.Allocate1D<int>(HeuristicStackCapacity);
|
||||||
|
stackProductBuffer = accelerator.Allocate1D<ulong>(HeuristicStackCapacity);
|
||||||
|
resultBuffer = accelerator.Allocate1D<ulong>(1);
|
||||||
|
statusBuffer = accelerator.Allocate1D<byte>(1);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Remove the cleaning after the order kernel is modified to always set the result.
|
// TODO: Remove the cleaning after the order kernel is modified to always set the result.
|
||||||
phiFactorBuffer.MemSetToZero();
|
phiFactorBuffer.MemSetToZero(stream);
|
||||||
phiExponentBuffer.MemSetToZero();
|
phiExponentBuffer.MemSetToZero(stream);
|
||||||
workFactorBuffer.MemSetToZero();
|
workFactorBuffer.MemSetToZero(stream);
|
||||||
workExponentBuffer.MemSetToZero();
|
workExponentBuffer.MemSetToZero(stream);
|
||||||
candidateBuffer.MemSetToZero();
|
candidateBuffer.MemSetToZero(stream);
|
||||||
stackIndexBuffer.MemSetToZero();
|
stackIndexBuffer.MemSetToZero(stream);
|
||||||
stackExponentBuffer.MemSetToZero();
|
stackExponentBuffer.MemSetToZero(stream);
|
||||||
stackProductBuffer.MemSetToZero();
|
stackProductBuffer.MemSetToZero(stream);
|
||||||
resultBuffer.MemSetToZero();
|
resultBuffer.MemSetToZero(stream);
|
||||||
statusBuffer.MemSetToZero();
|
statusBuffer.MemSetToZero(stream);
|
||||||
|
|
||||||
uint limit = config.SmallFactorLimit == 0 ? uint.MaxValue : config.SmallFactorLimit;
|
uint limit = config.SmallFactorLimit == 0 ? uint.MaxValue : config.SmallFactorLimit;
|
||||||
byte hasPrevious = previousOrder.HasValue ? (byte)1 : (byte)0;
|
byte hasPrevious = previousOrder.HasValue ? (byte)1 : (byte)0;
|
||||||
@@ -409,51 +397,27 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
resultBuffer.View,
|
resultBuffer.View,
|
||||||
statusBuffer.View);
|
statusBuffer.View);
|
||||||
|
|
||||||
kernel(
|
var orderKernel = GetOrderKernel(acceleratorIndex);
|
||||||
|
var kernelLauncher = orderKernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, OrderKernelConfig, MontgomeryDivisorData, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers>>();
|
||||||
|
|
||||||
|
kernelLauncher(
|
||||||
stream,
|
stream,
|
||||||
1,
|
1,
|
||||||
prime,
|
prime,
|
||||||
kernelConfig,
|
kernelConfig,
|
||||||
divisorData,
|
divisorData,
|
||||||
cache.Primes!.View,
|
smallPrimesView,
|
||||||
cache.Squares!.View,
|
smallSquaresView,
|
||||||
cache.Count,
|
(int)smallPrimesView.Length,
|
||||||
buffers);
|
buffers);
|
||||||
|
|
||||||
stream.Synchronize();
|
|
||||||
|
|
||||||
byte status = 0;
|
byte status = 0;
|
||||||
statusBuffer.View.CopyToCPU(ref status, 1);
|
statusBuffer.View.CopyToCPU(stream, ref status, 1);
|
||||||
|
resultBuffer.View.CopyToCPU(stream, ref order, 1);
|
||||||
|
stream.Synchronize();
|
||||||
|
|
||||||
PrimeOrderKernelStatus kernelStatus = (PrimeOrderKernelStatus)status;
|
stream.Dispose();
|
||||||
if (kernelStatus == PrimeOrderKernelStatus.Fallback)
|
|
||||||
{
|
|
||||||
DisposeResources();
|
|
||||||
lease.Dispose();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kernelStatus == PrimeOrderKernelStatus.PollardOverflow)
|
|
||||||
{
|
|
||||||
DisposeResources();
|
|
||||||
lease.Dispose();
|
|
||||||
throw new InvalidOperationException("GPU Pollard Rho stack overflow; increase HeuristicStackCapacity.");
|
|
||||||
}
|
|
||||||
|
|
||||||
resultBuffer.View.CopyToCPU(ref order, 1);
|
|
||||||
|
|
||||||
if (kernelStatus == PrimeOrderKernelStatus.FactoringFailure)
|
|
||||||
{
|
|
||||||
order = 0UL;
|
|
||||||
}
|
|
||||||
|
|
||||||
DisposeResources();
|
|
||||||
lease.Dispose();
|
|
||||||
|
|
||||||
return order != 0UL;
|
|
||||||
|
|
||||||
void DisposeResources()
|
|
||||||
{
|
|
||||||
phiFactorBuffer.Dispose();
|
phiFactorBuffer.Dispose();
|
||||||
phiExponentBuffer.Dispose();
|
phiExponentBuffer.Dispose();
|
||||||
workFactorBuffer.Dispose();
|
workFactorBuffer.Dispose();
|
||||||
@@ -464,17 +428,41 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
stackProductBuffer.Dispose();
|
stackProductBuffer.Dispose();
|
||||||
resultBuffer.Dispose();
|
resultBuffer.Dispose();
|
||||||
statusBuffer.Dispose();
|
statusBuffer.Dispose();
|
||||||
}
|
// GpuPrimeWorkLimiter.Release();
|
||||||
|
|
||||||
|
PrimeOrderKernelStatus kernelStatus = (PrimeOrderKernelStatus)status;
|
||||||
|
if (kernelStatus == PrimeOrderKernelStatus.Fallback)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OrderKernelLauncher GetOrderKernel(Accelerator accelerator)
|
if (kernelStatus == PrimeOrderKernelStatus.PollardOverflow)
|
||||||
{
|
{
|
||||||
return OrderKernelCache.GetOrAdd(accelerator, static accel =>
|
throw new InvalidOperationException("GPU Pollard Rho stack overflow; increase HeuristicStackCapacity.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kernelStatus == PrimeOrderKernelStatus.FactoringFailure)
|
||||||
{
|
{
|
||||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ulong, OrderKernelConfig, MontgomeryDivisorData, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers>(CalculateOrderKernel);
|
order = 0UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return order != 0UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Kernel GetOrderKernel(int acceleratorIndex)
|
||||||
|
{
|
||||||
|
var pool = _orderKernel ??= [];
|
||||||
|
if (pool[acceleratorIndex] is {} cached)
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
|
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, OrderKernelConfig, MontgomeryDivisorData, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers>(CalculateOrderKernel);
|
||||||
|
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
var kernel = KernelUtil.GetKernel(loaded);
|
||||||
return kernel.CreateLauncherDelegate<OrderKernelLauncher>();
|
pool[acceleratorIndex] = kernel;
|
||||||
});
|
return kernel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GpuPow2ModStatus TryPow2ModBatchInternal(ReadOnlySpan<UInt128> exponents, UInt128 prime, Span<UInt128> remainders)
|
private static GpuPow2ModStatus TryPow2ModBatchInternal(ReadOnlySpan<UInt128> exponents, UInt128 prime, Span<UInt128> remainders)
|
||||||
@@ -489,7 +477,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
throw new ArgumentException("Remainder span is shorter than the exponent span.", nameof(remainders));
|
throw new ArgumentException("Remainder span is shorter than the exponent span.", nameof(remainders));
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<UInt128> target = remainders.Slice(0, exponents.Length);
|
Span<UInt128> target = remainders[..exponents.Length];
|
||||||
target.Clear();
|
target.Clear();
|
||||||
|
|
||||||
ConcurrentDictionary<UInt128, byte> overflowRegistryWide = OverflowedPrimesWide;
|
ConcurrentDictionary<UInt128, byte> overflowRegistryWide = OverflowedPrimesWide;
|
||||||
@@ -515,45 +503,57 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool computed = TryComputeOnGpuWide(exponents, prime, target);
|
ComputeOnGpuWide(exponents, prime, target);
|
||||||
return computed ? GpuPow2ModStatus.Success : GpuPow2ModStatus.Unavailable;
|
return GpuPow2ModStatus.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryComputeOnGpu(ReadOnlySpan<ulong> exponents, ulong prime, in MontgomeryDivisorData divisorData, Span<ulong> results)
|
private static bool TryComputeOnGpu(ReadOnlySpan<ulong> exponents, ulong prime, in MontgomeryDivisorData divisorData, Span<ulong> results)
|
||||||
{
|
{
|
||||||
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
// GpuPrimeWorkLimiter.Acquire();
|
||||||
Accelerator accelerator = lease.Accelerator;
|
int acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||||
AcceleratorStream stream = lease.Stream;
|
Accelerator accelerator = _accelerators[acceleratorIndex];
|
||||||
var kernel = GetPow2ModKernel(accelerator);
|
AcceleratorStream? stream = accelerator.CreateStream();
|
||||||
// TODO: These buffers should be allocated once per accelerator and only reallocated when the new length exceeds capacity.
|
// TODO: These buffers should be allocated once per accelerator and only reallocated when the new length exceeds capacity.
|
||||||
// Modify the callers to use their own pool of buffers per accelerator, so that other threads don't use the accelerators
|
// Modify the callers to use their own pool of buffers per accelerator, so that other threads don't use the accelerators
|
||||||
// with out preallocated buffers. Share the pool with Pow2ModWide kernel.
|
// with out preallocated buffers. Share the pool with Pow2ModWide kernel.
|
||||||
var exponentBuffer = accelerator.Allocate1D<ulong>(exponents.Length);
|
MemoryBuffer1D<ulong, Stride1D.Dense>? exponentBuffer;
|
||||||
var remainderBuffer = accelerator.Allocate1D<ulong>(exponents.Length);
|
MemoryBuffer1D<ulong, Stride1D.Dense>? remainderBuffer;
|
||||||
|
lock(accelerator)
|
||||||
|
{
|
||||||
|
exponentBuffer = accelerator.Allocate1D<ulong>(exponents.Length);
|
||||||
|
remainderBuffer = accelerator.Allocate1D<ulong>(exponents.Length);
|
||||||
|
}
|
||||||
|
|
||||||
exponentBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(exponents), exponents.Length);
|
exponentBuffer.View.CopyFromCPU(stream, ref MemoryMarshal.GetReference(exponents), exponents.Length);
|
||||||
|
|
||||||
// Pow2Mod kernel always assigns the required output elements so we don't need to worry about clearing these.
|
// Pow2Mod kernel always assigns the required output elements so we don't need to worry about clearing these.
|
||||||
// remainderBuffer.MemSetToZero();
|
// remainderBuffer.MemSetToZero(stream);
|
||||||
|
|
||||||
|
var pow2ModKernel = GetPow2ModKernel(acceleratorIndex);
|
||||||
|
var kernelLauncher = pow2ModKernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
kernel(stream, exponents.Length, exponentBuffer.View, divisorData, remainderBuffer.View);
|
kernelLauncher(stream, exponents.Length, exponentBuffer.View, divisorData, remainderBuffer.View);
|
||||||
|
remainderBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(results), exponents.Length);
|
||||||
stream.Synchronize();
|
stream.Synchronize();
|
||||||
|
|
||||||
|
stream.Dispose();
|
||||||
|
stream = null;
|
||||||
|
exponentBuffer.Dispose();
|
||||||
|
remainderBuffer.Dispose();
|
||||||
|
// GpuPrimeWorkLimiter.Release();
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Exception for {prime} and exponents: {string.Join(",", exponents.ToArray())}.");
|
Console.WriteLine($"Exception for {prime} and exponents: {string.Join(",", exponents.ToArray())}.");
|
||||||
|
stream?.Dispose();
|
||||||
exponentBuffer.Dispose();
|
exponentBuffer.Dispose();
|
||||||
remainderBuffer.Dispose();
|
remainderBuffer.Dispose();
|
||||||
lease.Dispose();
|
// GpuPrimeWorkLimiter.Release();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
remainderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(results), exponents.Length);
|
|
||||||
exponentBuffer.Dispose();
|
|
||||||
remainderBuffer.Dispose();
|
|
||||||
lease.Dispose();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,80 +566,64 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryComputeOnGpuWide(ReadOnlySpan<UInt128> exponents, UInt128 prime, Span<UInt128> results)
|
private static void ComputeOnGpuWide(ReadOnlySpan<UInt128> exponents, UInt128 prime, Span<UInt128> results)
|
||||||
{
|
{
|
||||||
int length = exponents.Length;
|
int length = exponents.Length;
|
||||||
if (length == 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
GpuUInt128[]? rentedExponents = null;
|
GpuUInt128[]? rentedExponents = null;
|
||||||
GpuUInt128[]? rentedResults = null;
|
GpuUInt128[]? rentedResults = null;
|
||||||
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
ArrayPool<GpuUInt128> gpuUInt128Pool = ThreadStaticPools.GpuUInt128Pool;
|
||||||
|
PrimeOrderCalculatorAccelerator? gpu = null;
|
||||||
try
|
bool poolingRequired = length > WideStackThreshold;
|
||||||
{
|
Span<GpuUInt128> exponentSpan = poolingRequired
|
||||||
Accelerator accelerator = lease.Accelerator;
|
|
||||||
AcceleratorStream stream = lease.Stream;
|
|
||||||
var kernel = GetPow2ModWideKernel(accelerator);
|
|
||||||
// TODO: These buffers should be allocated once per accelerator and only reallocated when the new length exceeds capacity.
|
|
||||||
// Modify the callers to use their own pool of buffers per accelerator, so that other threads don't use the accelerators
|
|
||||||
// with out preallocated buffers. Share the pool with Pow2Mod kernel.
|
|
||||||
var exponentBuffer = accelerator.Allocate1D<GpuUInt128>(length);
|
|
||||||
var remainderBuffer = accelerator.Allocate1D<GpuUInt128>(length);
|
|
||||||
|
|
||||||
Span<GpuUInt128> exponentSpan = length <= WideStackThreshold
|
|
||||||
? stackalloc GpuUInt128[length]
|
? stackalloc GpuUInt128[length]
|
||||||
: new Span<GpuUInt128>(rentedExponents = ArrayPool<GpuUInt128>.Shared.Rent(length), 0, length);
|
: new Span<GpuUInt128>(rentedExponents = gpuUInt128Pool.Rent(length), 0, length);
|
||||||
|
Span<GpuUInt128> resultSpan = length <= WideStackThreshold
|
||||||
|
? stackalloc GpuUInt128[length]
|
||||||
|
: new Span<GpuUInt128>(rentedResults = gpuUInt128Pool.Rent(length), 0, length);
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
exponentSpan[i] = (GpuUInt128)exponents[i];
|
exponentSpan[i] = (GpuUInt128)exponents[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
exponentBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(exponentSpan), length);
|
|
||||||
// Pow2ModWide kernel always assigns the required output elements so we don't need to worry about clearing these.
|
|
||||||
// remainderBuffer.MemSetToZero();
|
|
||||||
|
|
||||||
GpuUInt128 modulus = (GpuUInt128)prime;
|
GpuUInt128 modulus = (GpuUInt128)prime;
|
||||||
kernel(stream, length, exponentBuffer.View, modulus, remainderBuffer.View);
|
gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||||
|
int acceleratorIndex = gpu.AcceleratorIndex;
|
||||||
|
Accelerator accelerator = gpu.Accelerator;
|
||||||
|
|
||||||
|
AcceleratorStream? stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||||
|
MemoryBuffer1D<GpuUInt128, Stride1D.Dense> exponentBuffer;
|
||||||
|
MemoryBuffer1D<GpuUInt128, Stride1D.Dense> remainderBuffer;
|
||||||
|
|
||||||
|
lock(accelerator)
|
||||||
|
{
|
||||||
|
exponentBuffer = accelerator.Allocate1D<GpuUInt128>(length);
|
||||||
|
remainderBuffer = accelerator.Allocate1D<GpuUInt128>(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
exponentBuffer.View.CopyFromCPU(stream, exponentSpan);
|
||||||
|
var kernelLauncher = gpu.Pow2ModWideKernelLauncher;
|
||||||
|
|
||||||
|
kernelLauncher(stream, length, exponentBuffer.View, modulus, remainderBuffer.View);
|
||||||
|
remainderBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(resultSpan), length);
|
||||||
stream.Synchronize();
|
stream.Synchronize();
|
||||||
|
|
||||||
Span<GpuUInt128> resultSpan = length <= WideStackThreshold
|
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||||
? stackalloc GpuUInt128[length]
|
|
||||||
: new Span<GpuUInt128>(rentedResults = ArrayPool<GpuUInt128>.Shared.Rent(length), 0, length);
|
|
||||||
|
|
||||||
remainderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(resultSpan), length);
|
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
results[i] = (UInt128)resultSpan[i];
|
results[i] = (UInt128)resultSpan[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
exponentBuffer.Dispose();
|
|
||||||
remainderBuffer.Dispose();
|
remainderBuffer.Dispose();
|
||||||
return true;
|
exponentBuffer.Dispose();
|
||||||
}
|
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Exception when computing {prime} with exponents: {string.Join(", ", exponents.ToArray())}");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (rentedExponents is not null)
|
if (rentedExponents is not null)
|
||||||
{
|
{
|
||||||
ArrayPool<GpuUInt128>.Shared.Return(rentedExponents, clearArray: false);
|
gpuUInt128Pool.Return(rentedExponents, clearArray: false);
|
||||||
}
|
gpuUInt128Pool.Return(rentedResults!, clearArray: false);
|
||||||
|
|
||||||
if (rentedResults is not null)
|
|
||||||
{
|
|
||||||
ArrayPool<GpuUInt128>.Shared.Return(rentedResults, clearArray: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
lease.Dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -662,18 +646,20 @@ internal static partial class PrimeOrderGpuHeuristics
|
|||||||
return ULongExtensions.Pow2MontgomeryModWindowedGpuConvertToStandard(divisorData, exponent);
|
return ULongExtensions.Pow2MontgomeryModWindowedGpuConvertToStandard(divisorData, exponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>> GetPow2ModKernel(Accelerator accelerator)
|
private static Kernel GetPow2ModKernel(int acceleratorIndex)
|
||||||
{
|
{
|
||||||
return Pow2ModKernelCache.GetOrAdd(accelerator, static accel =>
|
var pool = _pow2ModKernel ??= [];
|
||||||
|
if (pool[acceleratorIndex] is {} cached)
|
||||||
{
|
{
|
||||||
var loaded = accel.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernel);
|
return cached;
|
||||||
var kernel = KernelUtil.GetKernel(loaded);
|
|
||||||
return kernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var accelerator = _accelerators[acceleratorIndex];
|
||||||
|
var loaded = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernel));
|
||||||
|
|
||||||
|
pool[acceleratorIndex] = loaded;
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
internal readonly record struct PrimeOrderGpuCapability(int ModulusBits, int ExponentBits)
|
internal readonly record struct PrimeOrderGpuCapability(int ModulusBits, int ExponentBits)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
|
public readonly struct ResidueAutomatonArgs(
|
||||||
|
ulong q0m10, ulong step10, ulong q0m8, ulong step8, ulong q0m3, ulong step3, ulong q0m5, ulong step5
|
||||||
|
)
|
||||||
|
{
|
||||||
|
public readonly ulong Q0M10 = q0m10;
|
||||||
|
public readonly ulong Step10 = step10;
|
||||||
|
public readonly ulong Q0M8 = q0m8;
|
||||||
|
public readonly ulong Step8 = step8;
|
||||||
|
public readonly ulong Q0M3 = q0m3;
|
||||||
|
public readonly ulong Step3 = step3;
|
||||||
|
public readonly ulong Q0M5 = q0m5;
|
||||||
|
public readonly ulong Step5 = step5;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using ILGPU;
|
||||||
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
|
public readonly struct ResiduePrimeViews(
|
||||||
|
ArrayView1D<uint, Stride1D.Dense> lastOne,
|
||||||
|
ArrayView1D<uint, Stride1D.Dense> lastSeven,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> lastOnePow2,
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> lastSevenPow2)
|
||||||
|
{
|
||||||
|
public readonly ArrayView1D<uint, Stride1D.Dense> LastOne = lastOne;
|
||||||
|
public readonly ArrayView1D<uint, Stride1D.Dense> LastSeven = lastSeven;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> LastOnePow2 = lastOnePow2;
|
||||||
|
public readonly ArrayView1D<ulong, Stride1D.Dense> LastSevenPow2 = lastSevenPow2;
|
||||||
|
}
|
||||||
Loaded 100 of 125 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user