Unnecessary memory zeroing commented out. TODOs added for the remainders.
This commit is contained in:
1 parent
8e3e990499
commit
0d7df2e45b
7 files changed
+246
-208
No files matched your search
@@ -68,7 +68,6 @@ internal static class IncrementalKernels
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Is this expected that the value of kStart is modified?
|
||||
GpuUInt128 k = kStart + (GpuUInt128)idx;
|
||||
GpuUInt128 q = twoP;
|
||||
GpuUInt128.Mul64(ref q, k.High, k.Low);
|
||||
@@ -125,13 +124,16 @@ internal static class IncrementalKernels
|
||||
ulong exponent,
|
||||
GpuUInt128 twoP,
|
||||
GpuUInt128 kStart,
|
||||
byte lastIsSeven,
|
||||
byte _,
|
||||
ulong divMul,
|
||||
ResidueAutomatonArgs ra,
|
||||
ArrayView<int> found,
|
||||
ArrayView1D<ulong, Stride1D.Dense> smallCycles)
|
||||
{
|
||||
{
|
||||
// TODO: Modify this kernel to always assign the required found element to the result of the calculations and
|
||||
// remove any clearing of the output buffer in the caller methods.
|
||||
ulong idx = (ulong)index.X;
|
||||
// TODO: Replace % operator in GPU and their outgoing paths with logical operations, where possible.
|
||||
ulong idxMod3 = idx % 3UL;
|
||||
ulong idxMod5 = idx % 5UL;
|
||||
ulong r10 = ra.Q0M10 + (ra.Step10 * idx).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL;
|
||||
|
||||
@@ -6,173 +6,177 @@ namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
internal static class Pow2ModKernels
|
||||
{
|
||||
public static void Pow2ModKernelScan(
|
||||
Index1D index,
|
||||
ulong exponent,
|
||||
GpuUInt128 twoP,
|
||||
GpuUInt128 kStart,
|
||||
byte lastIsSeven,
|
||||
ulong _,
|
||||
ResidueAutomatonArgs ra,
|
||||
ArrayView<ulong> orders,
|
||||
ArrayView1D<ulong, Stride1D.Dense> smallCycles,
|
||||
ArrayView1D<uint, Stride1D.Dense> smallPrimesLastOne,
|
||||
ArrayView1D<uint, Stride1D.Dense> smallPrimesLastSeven,
|
||||
ArrayView1D<ulong, Stride1D.Dense> smallPrimesPow2LastOne,
|
||||
ArrayView1D<ulong, Stride1D.Dense> smallPrimesPow2LastSeven)
|
||||
{
|
||||
ulong idx = (ulong)index.X;
|
||||
ulong idxMod3 = idx % 3UL;
|
||||
ulong idxMod5 = idx % 5UL;
|
||||
ulong r10 = ra.Q0M10 + (ra.Step10 * idx).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL;
|
||||
bool shouldCheck = r10 != 5UL;
|
||||
if (shouldCheck)
|
||||
{
|
||||
ulong r8 = (ra.Q0M8 + ((ra.Step8 * idx) & 7UL)) & 7UL;
|
||||
if (r8 != 1UL && r8 != 7UL)
|
||||
{
|
||||
shouldCheck = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong r3 = ra.Q0M3 + (ra.Step3 * idxMod3);
|
||||
if (r3 >= 6UL) r3 -= 6UL;
|
||||
if (r3 >= 3UL) r3 -= 3UL;
|
||||
if (r3 == 0UL)
|
||||
{
|
||||
shouldCheck = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong r5 = ra.Q0M5 + (ra.Step5 * idxMod5);
|
||||
if (r5 >= 15UL) r5 -= 15UL;
|
||||
if (r5 >= 10UL) r5 -= 10UL;
|
||||
if (r5 >= 5UL) r5 -= 5UL;
|
||||
if (r5 == 0UL)
|
||||
{
|
||||
shouldCheck = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!shouldCheck)
|
||||
{
|
||||
orders[index] = 0UL;
|
||||
return;
|
||||
}
|
||||
/// This kernel always sets the result of the corresponding element. Callers don't need to clear the output buffers.
|
||||
public static void Pow2ModKernelScan(
|
||||
Index1D index,
|
||||
ulong exponent,
|
||||
GpuUInt128 twoP,
|
||||
GpuUInt128 kStart,
|
||||
byte lastIsSeven,
|
||||
ulong _,
|
||||
ResidueAutomatonArgs ra,
|
||||
ArrayView<ulong> orders,
|
||||
ArrayView1D<ulong, Stride1D.Dense> smallCycles,
|
||||
ArrayView1D<uint, Stride1D.Dense> smallPrimesLastOne,
|
||||
ArrayView1D<uint, Stride1D.Dense> smallPrimesLastSeven,
|
||||
ArrayView1D<ulong, Stride1D.Dense> smallPrimesPow2LastOne,
|
||||
ArrayView1D<ulong, Stride1D.Dense> smallPrimesPow2LastSeven)
|
||||
{
|
||||
ulong idx = (ulong)index.X;
|
||||
// TODO: Replace % operator in GPU and their outgoing paths with logical operations, where possible.
|
||||
ulong idxMod3 = idx % 3UL;
|
||||
ulong idxMod5 = idx % 5UL;
|
||||
ulong r10 = ra.Q0M10 + (ra.Step10 * idx).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL;
|
||||
bool shouldCheck = r10 != 5UL;
|
||||
// TODO: Replace these "if" statements with ?:
|
||||
if (shouldCheck)
|
||||
{
|
||||
ulong r8 = (ra.Q0M8 + ((ra.Step8 * idx) & 7UL)) & 7UL;
|
||||
if (r8 != 1UL && r8 != 7UL)
|
||||
{
|
||||
shouldCheck = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong r3 = ra.Q0M3 + (ra.Step3 * idxMod3);
|
||||
if (r3 >= 6UL) r3 -= 6UL;
|
||||
if (r3 >= 3UL) r3 -= 3UL;
|
||||
if (r3 == 0UL)
|
||||
{
|
||||
shouldCheck = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong r5 = ra.Q0M5 + (ra.Step5 * idxMod5);
|
||||
if (r5 >= 15UL) r5 -= 15UL;
|
||||
if (r5 >= 10UL) r5 -= 10UL;
|
||||
if (r5 >= 5UL) r5 -= 5UL;
|
||||
if (r5 == 0UL)
|
||||
{
|
||||
shouldCheck = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!shouldCheck)
|
||||
{
|
||||
orders[index] = 0UL;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: kStart is modified after this. Is this expected?
|
||||
kStart.Add(idx);
|
||||
GpuUInt128 q = twoP;
|
||||
GpuUInt128.Mul64(ref q, kStart.High, kStart.Low);
|
||||
q.Add(GpuUInt128.One);
|
||||
ReadOnlyGpuUInt128 readOnlyQ = q.AsReadOnly();
|
||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||
{
|
||||
ulong cycle = smallCycles[(int)q.Low];
|
||||
// cycle should always be initialized if we're within array limit in production code
|
||||
if (cycle != 0UL && cycle <= exponent && (exponent % cycle) != 0UL)
|
||||
{
|
||||
orders[index] = 0UL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (GpuUInt128.Pow2Minus1Mod(exponent, in readOnlyQ) != GpuUInt128.Zero)
|
||||
{
|
||||
orders[index] = 0UL;
|
||||
return;
|
||||
}
|
||||
kStart.Add(idx);
|
||||
GpuUInt128 q = twoP;
|
||||
GpuUInt128.Mul64(ref q, kStart.High, kStart.Low);
|
||||
q.Add(GpuUInt128.One);
|
||||
ReadOnlyGpuUInt128 readOnlyQ = q.AsReadOnly();
|
||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||
{
|
||||
ulong cycle = smallCycles[(int)q.Low];
|
||||
// cycle should always be initialized if we're within array limit in production code
|
||||
if (cycle != 0UL && cycle <= exponent && (exponent % cycle) != 0UL)
|
||||
{
|
||||
orders[index] = 0UL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (GpuUInt128.Pow2Minus1Mod(exponent, in readOnlyQ) != GpuUInt128.Zero)
|
||||
{
|
||||
orders[index] = 0UL;
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayView1D<uint, Stride1D.Dense> primes = lastIsSeven != 0 ? smallPrimesLastSeven : smallPrimesLastOne;
|
||||
ArrayView1D<ulong, Stride1D.Dense> primesPow2 = lastIsSeven != 0 ? smallPrimesPow2LastSeven : smallPrimesPow2LastOne;
|
||||
int primesLen = (int)primes.Length;
|
||||
for (int i = 0; i < primesLen; i++)
|
||||
{
|
||||
ulong square = primesPow2[i];
|
||||
if (new GpuUInt128(0UL, square) > q)
|
||||
{
|
||||
break;
|
||||
}
|
||||
ulong prime = primes[i];
|
||||
if (KernelMathHelpers.Mod128By64(q, prime) == 0UL)
|
||||
{
|
||||
orders[index] = 0UL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
ArrayView1D<uint, Stride1D.Dense> primes = lastIsSeven != 0 ? smallPrimesLastSeven : smallPrimesLastOne;
|
||||
ArrayView1D<ulong, Stride1D.Dense> primesPow2 = lastIsSeven != 0 ? smallPrimesPow2LastSeven : smallPrimesPow2LastOne;
|
||||
int primesLen = (int)primes.Length;
|
||||
for (int i = 0; i < primesLen; i++)
|
||||
{
|
||||
ulong square = primesPow2[i];
|
||||
if (new GpuUInt128(0UL, square) > q)
|
||||
{
|
||||
break;
|
||||
}
|
||||
ulong prime = primes[i];
|
||||
if (KernelMathHelpers.Mod128By64(q, prime) == 0UL)
|
||||
{
|
||||
orders[index] = 0UL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
orders[index] = exponent;
|
||||
}
|
||||
orders[index] = exponent;
|
||||
}
|
||||
|
||||
public static void Pow2ModOrderKernelScan(
|
||||
Index1D index,
|
||||
ulong exponent,
|
||||
GpuUInt128 twoP,
|
||||
GpuUInt128 kStart,
|
||||
byte lastIsSeven,
|
||||
ulong _,
|
||||
ResidueAutomatonArgs ra,
|
||||
ArrayView<int> found,
|
||||
ArrayView1D<ulong, Stride1D.Dense> smallCycles)
|
||||
{
|
||||
ulong idx = (ulong)index.X;
|
||||
ulong idxMod3 = idx % 3UL;
|
||||
ulong idxMod5 = idx % 5UL;
|
||||
ulong r10 = ra.Q0M10 + (ra.Step10 * idx).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL;
|
||||
bool shouldCheck = r10 != 5UL;
|
||||
if (shouldCheck)
|
||||
{
|
||||
ulong r8 = (ra.Q0M8 + ((ra.Step8 * idx) & 7UL)) & 7UL;
|
||||
if (r8 != 1UL && r8 != 7UL)
|
||||
{
|
||||
shouldCheck = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong r3 = ra.Q0M3 + (ra.Step3 * idxMod3);
|
||||
if (r3 >= 6UL) r3 -= 6UL;
|
||||
if (r3 >= 3UL) r3 -= 3UL;
|
||||
if (r3 == 0UL)
|
||||
{
|
||||
shouldCheck = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong r5 = ra.Q0M5 + (ra.Step5 * idxMod5);
|
||||
if (r5 >= 15UL) r5 -= 15UL;
|
||||
if (r5 >= 10UL) r5 -= 10UL;
|
||||
if (r5 >= 5UL) r5 -= 5UL;
|
||||
if (r5 == 0UL)
|
||||
{
|
||||
shouldCheck = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!shouldCheck)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/// This kernel doesn't always set the result of the found array. It sets the value only when it needs to. The callers
|
||||
/// must clean the output buffer before the call to get a deterministic result.
|
||||
public static void Pow2ModOrderKernelScan(
|
||||
Index1D index,
|
||||
ulong exponent,
|
||||
GpuUInt128 twoP,
|
||||
GpuUInt128 kStart,
|
||||
byte _,
|
||||
ulong __,
|
||||
ResidueAutomatonArgs ra,
|
||||
ArrayView<int> found,
|
||||
ArrayView1D<ulong, Stride1D.Dense> smallCycles)
|
||||
{
|
||||
ulong idx = (ulong)index.X;
|
||||
ulong idxMod3 = idx % 3UL;
|
||||
ulong idxMod5 = idx % 5UL;
|
||||
ulong r10 = ra.Q0M10 + (ra.Step10 * idx).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL;
|
||||
bool shouldCheck = r10 != 5UL;
|
||||
if (shouldCheck)
|
||||
{
|
||||
ulong r8 = (ra.Q0M8 + ((ra.Step8 * idx) & 7UL)) & 7UL;
|
||||
if (r8 != 1UL && r8 != 7UL)
|
||||
{
|
||||
shouldCheck = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong r3 = ra.Q0M3 + (ra.Step3 * idxMod3);
|
||||
if (r3 >= 6UL) r3 -= 6UL;
|
||||
if (r3 >= 3UL) r3 -= 3UL;
|
||||
if (r3 == 0UL)
|
||||
{
|
||||
shouldCheck = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong r5 = ra.Q0M5 + (ra.Step5 * idxMod5);
|
||||
if (r5 >= 15UL) r5 -= 15UL;
|
||||
if (r5 >= 10UL) r5 -= 10UL;
|
||||
if (r5 >= 5UL) r5 -= 5UL;
|
||||
if (r5 == 0UL)
|
||||
{
|
||||
shouldCheck = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!shouldCheck)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GpuUInt128 k = kStart + (GpuUInt128)idx;
|
||||
GpuUInt128 q = twoP;
|
||||
GpuUInt128.Mul64(ref q, k.High, k.Low);
|
||||
q.Add(GpuUInt128.One);
|
||||
ReadOnlyGpuUInt128 readOnlyQ = q.AsReadOnly();
|
||||
// Small-cycles in-kernel early rejection from device table
|
||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||
{
|
||||
ulong cycle = smallCycles[(int)q.Low];
|
||||
if (cycle != 0UL && cycle <= exponent && (exponent % cycle) != 0UL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (GpuUInt128.Pow2Mod(exponent, in readOnlyQ) != GpuUInt128.One)
|
||||
{
|
||||
return;
|
||||
}
|
||||
GpuUInt128 k = kStart + (GpuUInt128)idx;
|
||||
GpuUInt128 q = twoP;
|
||||
GpuUInt128.Mul64(ref q, k.High, k.Low);
|
||||
q.Add(GpuUInt128.One);
|
||||
ReadOnlyGpuUInt128 readOnlyQ = q.AsReadOnly();
|
||||
// Small-cycles in-kernel early rejection from device table
|
||||
if (q.High == 0UL && q.Low < (ulong)smallCycles.Length)
|
||||
{
|
||||
ulong cycle = smallCycles[(int)q.Low];
|
||||
if (cycle != 0UL && cycle <= exponent && (exponent % cycle) != 0UL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (GpuUInt128.Pow2Mod(exponent, in readOnlyQ) != GpuUInt128.One)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Atomic.Or(ref found[0], 1);
|
||||
}
|
||||
Atomic.Or(ref found[0], 1);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
internal static partial class PrimeOrderGpuHeuristics
|
||||
{
|
||||
/// 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.
|
||||
internal static void PartialFactorKernel(
|
||||
Index1D index,
|
||||
ArrayView1D<uint, Stride1D.Dense> primes,
|
||||
@@ -111,6 +113,8 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
}
|
||||
}
|
||||
|
||||
/// This kernel doesn't always set the result of the first element. It sets the value only when it needs to. The callers
|
||||
/// must clean the output buffer before the call to get a deterministic result.
|
||||
internal static void CalculateOrderKernel(
|
||||
Index1D index,
|
||||
ulong prime,
|
||||
@@ -126,7 +130,9 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayView1D<ulong, Stride1D.Dense> phiFactors = buffers.PhiFactors;
|
||||
// TODO: Modify this kernel to always set the output result so that callers never need to clear any buffers and remove
|
||||
// any existing cleaning of the buffers after that.
|
||||
ArrayView1D<ulong, Stride1D.Dense> phiFactors = buffers.PhiFactors;
|
||||
ArrayView1D<int, Stride1D.Dense> phiExponents = buffers.PhiExponents;
|
||||
ArrayView1D<ulong, Stride1D.Dense> workFactors = buffers.WorkFactors;
|
||||
ArrayView1D<int, Stride1D.Dense> workExponents = buffers.WorkExponents;
|
||||
@@ -143,15 +149,18 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
int maxPowChecks = config.MaxPowChecks;
|
||||
int mode = config.Mode;
|
||||
|
||||
statusOut[0] = (byte)PrimeOrderKernelStatus.Fallback;
|
||||
statusOut[0] = (byte)PrimeOrderKernelStatus.Fallback;
|
||||
|
||||
if (prime <= 3UL)
|
||||
{
|
||||
ulong orderValue = prime == 3UL ? 2UL : 1UL;
|
||||
resultOut[0] = orderValue;
|
||||
statusOut[0] = (byte)PrimeOrderKernelStatus.Found;
|
||||
return;
|
||||
}
|
||||
// TODO: Is this condition ever satisfied on EventPerfectBitScanner's execution paths? Comment it out with explanatory
|
||||
// comment before it, if not. Otherwise add a comment explaining how it's used. If tests or benchmarks require this,
|
||||
// modify such tests and benchmark to avoid that.
|
||||
if (prime <= 3UL)
|
||||
{
|
||||
ulong orderValue = prime == 3UL ? 2UL : 1UL;
|
||||
resultOut[0] = orderValue;
|
||||
statusOut[0] = (byte)PrimeOrderKernelStatus.Found;
|
||||
return;
|
||||
}
|
||||
|
||||
ulong phi = prime - 1UL;
|
||||
|
||||
@@ -1153,11 +1162,13 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
return order;
|
||||
}
|
||||
|
||||
internal static void Pow2ModKernel(Index1D index, ArrayView1D<ulong, Stride1D.Dense> exponents, MontgomeryDivisorData divisor, ArrayView1D<ulong, Stride1D.Dense> remainders)
|
||||
{
|
||||
ulong exponent = exponents[index];
|
||||
remainders[index] = ULongExtensions.Pow2MontgomeryModWindowedGpuConvertToStandard(divisor, exponent);
|
||||
}
|
||||
/// This kernel always sets the result of the corresponding element. Callers don't need to clear the output buffers.
|
||||
|
||||
internal static void Pow2ModKernel(Index1D index, ArrayView1D<ulong, Stride1D.Dense> exponents, MontgomeryDivisorData divisor, ArrayView1D<ulong, Stride1D.Dense> remainders)
|
||||
{
|
||||
ulong exponent = exponents[index];
|
||||
remainders[index] = ULongExtensions.Pow2MontgomeryModWindowedGpuConvertToStandard(divisor, exponent);
|
||||
}
|
||||
|
||||
private static Action<AcceleratorStream, Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>> GetPow2ModWideKernel(Accelerator accelerator)
|
||||
{
|
||||
@@ -1169,6 +1180,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
});
|
||||
}
|
||||
|
||||
/// 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)
|
||||
{
|
||||
GpuUInt128 exponent = exponents[index];
|
||||
|
||||
@@ -41,7 +41,8 @@ public sealed class MersenneNumberDivisorGpuTester
|
||||
var accelerator = gpu.Accelerator;
|
||||
var kernel = GetKernel(accelerator);
|
||||
var resultBuffer = _resultBuffers.GetOrAdd(accelerator, acc => acc.Allocate1D<byte>(1));
|
||||
resultBuffer.MemSetToZero();
|
||||
// There is no point in clearing this buffer. We always override item [0] and never use it beyond item [0]
|
||||
// resultBuffer.MemSetToZero();
|
||||
kernel(1, exponent, divisor, resultBuffer.View);
|
||||
accelerator.Synchronize();
|
||||
Span<byte> result = stackalloc byte[1];
|
||||
|
||||
@@ -35,6 +35,7 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
|
||||
{
|
||||
remaining = maxK - kStart + 1UL;
|
||||
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.
|
||||
foundBuffer.MemSetToZero();
|
||||
// Precompute residue automaton bases for this batch
|
||||
UInt128 q0 = twoP * kStart + 1UL;
|
||||
|
||||
@@ -143,8 +143,9 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
while (bag.TryTake(out var resources))
|
||||
{
|
||||
if (resources.Capacity >= capacity)
|
||||
{
|
||||
resources.OrderBuffer.MemSetToZero();
|
||||
{
|
||||
// The caller always overwrites the required elements. We don't need to worry about clearing the output buffer.
|
||||
// resources.OrderBuffer.MemSetToZero();
|
||||
return resources;
|
||||
}
|
||||
|
||||
@@ -152,7 +153,8 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
}
|
||||
|
||||
var created = new ResidueResources(accelerator, capacity);
|
||||
created.OrderBuffer.MemSetToZero();
|
||||
// The caller always overwrites the required elements. We don't need to worry about clearing the output buffer.
|
||||
// created.OrderBuffer.MemSetToZero();
|
||||
return created;
|
||||
}
|
||||
|
||||
|
||||
@@ -199,19 +199,21 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
AcceleratorStream stream = lease.Stream;
|
||||
|
||||
var kernel = GetPartialFactorKernel(accelerator);
|
||||
SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator);
|
||||
SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator);
|
||||
|
||||
// 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);
|
||||
var exponentBuffer = accelerator.Allocate1D<int>(exponentTargets.Length);
|
||||
var countBuffer = accelerator.Allocate1D<int>(1);
|
||||
var remainingBuffer = accelerator.Allocate1D<ulong>(1);
|
||||
var fullyFactoredBuffer = accelerator.Allocate1D<byte>(1);
|
||||
|
||||
factorBuffer.MemSetToZero();
|
||||
exponentBuffer.MemSetToZero();
|
||||
countBuffer.MemSetToZero();
|
||||
remainingBuffer.MemSetToZero();
|
||||
fullyFactoredBuffer.MemSetToZero();
|
||||
// TODO: There is no need to clear these buffers because the kernel will always assign values within the required bounds.
|
||||
// factorBuffer.MemSetToZero();
|
||||
// exponentBuffer.MemSetToZero();
|
||||
// countBuffer.MemSetToZero();
|
||||
// remainingBuffer.MemSetToZero();
|
||||
// fullyFactoredBuffer.MemSetToZero();
|
||||
|
||||
kernel(
|
||||
stream,
|
||||
@@ -240,6 +242,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
fullyFactoredBuffer.View.CopyToCPU(ref fullyFactoredFlag, 1);
|
||||
fullyFactored = fullyFactoredFlag != 0;
|
||||
|
||||
// TODO: These buffers shouldn't be disposed but rather reused accross call with the assigned accelerator.
|
||||
factorBuffer.Dispose();
|
||||
exponentBuffer.Dispose();
|
||||
countBuffer.Dispose();
|
||||
@@ -364,8 +367,11 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
AcceleratorStream stream = lease.Stream;
|
||||
|
||||
var kernel = GetOrderKernel(accelerator);
|
||||
SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator);
|
||||
SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator);
|
||||
|
||||
// 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
|
||||
// use them.
|
||||
var phiFactorBuffer = accelerator.Allocate1D<ulong>(GpuSmallPrimeFactorSlots);
|
||||
var phiExponentBuffer = accelerator.Allocate1D<int>(GpuSmallPrimeFactorSlots);
|
||||
var workFactorBuffer = accelerator.Allocate1D<ulong>(GpuSmallPrimeFactorSlots);
|
||||
@@ -377,7 +383,8 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
var resultBuffer = accelerator.Allocate1D<ulong>(1);
|
||||
var statusBuffer = accelerator.Allocate1D<byte>(1);
|
||||
|
||||
phiFactorBuffer.MemSetToZero();
|
||||
// TODO: Remove the cleaning after the order kernel is modified to always set the result.
|
||||
phiFactorBuffer.MemSetToZero();
|
||||
phiExponentBuffer.MemSetToZero();
|
||||
workFactorBuffer.MemSetToZero();
|
||||
workExponentBuffer.MemSetToZero();
|
||||
@@ -523,26 +530,31 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
Accelerator accelerator = lease.Accelerator;
|
||||
AcceleratorStream stream = lease.Stream;
|
||||
var kernel = GetPow2ModKernel(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 Pow2ModWide kernel.
|
||||
var exponentBuffer = accelerator.Allocate1D<ulong>(exponents.Length);
|
||||
var remainderBuffer = accelerator.Allocate1D<ulong>(exponents.Length);
|
||||
|
||||
exponentBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(exponents), exponents.Length);
|
||||
remainderBuffer.MemSetToZero();
|
||||
|
||||
try
|
||||
{
|
||||
kernel(stream, exponents.Length, exponentBuffer.View, divisorData, remainderBuffer.View);
|
||||
stream.Synchronize();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Console.WriteLine($"Exception for {prime} and exponents: {string.Join(",", exponents.ToArray())}.");
|
||||
exponentBuffer.Dispose();
|
||||
remainderBuffer.Dispose();
|
||||
execution.Dispose();
|
||||
lease.Dispose();
|
||||
throw;
|
||||
}
|
||||
// Pow2Mod kernel always assigns the required output elements so we don't need to worry about clearing these.
|
||||
// remainderBuffer.MemSetToZero();
|
||||
|
||||
try
|
||||
{
|
||||
kernel(stream, exponents.Length, exponentBuffer.View, divisorData, remainderBuffer.View);
|
||||
stream.Synchronize();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Console.WriteLine($"Exception for {prime} and exponents: {string.Join(",", exponents.ToArray())}.");
|
||||
exponentBuffer.Dispose();
|
||||
remainderBuffer.Dispose();
|
||||
execution.Dispose();
|
||||
lease.Dispose();
|
||||
throw;
|
||||
}
|
||||
|
||||
remainderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(results), exponents.Length);
|
||||
exponentBuffer.Dispose();
|
||||
@@ -579,6 +591,9 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
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);
|
||||
|
||||
@@ -592,7 +607,8 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
}
|
||||
|
||||
exponentBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(exponentSpan), length);
|
||||
remainderBuffer.MemSetToZero();
|
||||
// Pow2ModWide kernel always assigns the required output elements so we don't need to worry about clearing these.
|
||||
// remainderBuffer.MemSetToZero();
|
||||
|
||||
GpuUInt128 modulus = (GpuUInt128)prime;
|
||||
kernel(stream, length, exponentBuffer.View, modulus, remainderBuffer.View);
|
||||
|
||||
Reference in new issue
Block a user