Benchmarks to compare Interlocked with Atomic added. Atomic replaced with Interlocked on CPU path as better performer.
This commit is contained in:
1 parent
0ec48f1f0b
commit
43376745f5
3 files changed
+247
-6
No files matched your search
@@ -0,0 +1,238 @@
|
|||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using BenchmarkDotNet.Jobs;
|
||||||
|
using ILGPU;
|
||||||
|
using Open.Numeric.Primes;
|
||||||
|
|
||||||
|
namespace EvenPerfectBitScanner.Benchmarks;
|
||||||
|
|
||||||
|
[ShortRunJob(RuntimeMoniker.Net80)]
|
||||||
|
[MemoryDiagnoser]
|
||||||
|
public class AtomicBenchmarks
|
||||||
|
{
|
||||||
|
private ulong[] _candidates = [];
|
||||||
|
private int[] _candidatesInt = [];
|
||||||
|
|
||||||
|
[ParamsSource(nameof(GetBenchmarkCases))]
|
||||||
|
public PrimeBenchmarkCase BenchmarkCase { get; set; }
|
||||||
|
|
||||||
|
[GlobalSetup]
|
||||||
|
public void GlobalSetup()
|
||||||
|
{
|
||||||
|
_candidates = BenchmarkCase.Candidates;
|
||||||
|
_candidatesInt = [.. _candidates.Select(x => (int)x)];
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public int AtomicAddInt()
|
||||||
|
{
|
||||||
|
int[] values = _candidatesInt;
|
||||||
|
int primeCount;
|
||||||
|
int oldValue = 0;
|
||||||
|
for (int i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
primeCount = values[i];
|
||||||
|
oldValue ^= Atomic.Add(ref primeCount, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark(Baseline = true)]
|
||||||
|
public int InterlockedIncInt()
|
||||||
|
{
|
||||||
|
int[] values = _candidatesInt;
|
||||||
|
int primeCount;
|
||||||
|
int oldValue = 0;
|
||||||
|
for (int i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
primeCount = values[i];
|
||||||
|
oldValue ^= Interlocked.Increment(ref primeCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public ulong AtomicAddUlong()
|
||||||
|
{
|
||||||
|
ulong[] values = _candidates;
|
||||||
|
ulong primeCount;
|
||||||
|
ulong oldValue = 0UL;
|
||||||
|
for (int i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
primeCount = values[i];
|
||||||
|
oldValue ^= Atomic.Add(ref primeCount, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public ulong InterlockedIncUlong()
|
||||||
|
{
|
||||||
|
ulong[] values = _candidates;
|
||||||
|
ulong primeCount;
|
||||||
|
ulong oldValue = 0UL;
|
||||||
|
for (int i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
primeCount = values[i];
|
||||||
|
oldValue ^= Interlocked.Increment(ref primeCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<PrimeBenchmarkCase> GetBenchmarkCases()
|
||||||
|
{
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≤100",
|
||||||
|
"Admissible odd values ≤ 100 (k ≤ 5, not divisible by 5)",
|
||||||
|
3UL,
|
||||||
|
100UL,
|
||||||
|
int.MaxValue);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≤4_000_000",
|
||||||
|
"Sampled admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
3UL,
|
||||||
|
4_000_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥16_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 16,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
16_000_001UL,
|
||||||
|
16_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥32_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 32,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
32_000_001UL,
|
||||||
|
32_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥64_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 64,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
64_000_001UL,
|
||||||
|
64_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥100_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 100,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
100_000_001UL,
|
||||||
|
100_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥138_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 138,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
138_000_001UL,
|
||||||
|
138_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥276_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 276,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
276_000_001UL,
|
||||||
|
276_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥552_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 552,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
552_000_001UL,
|
||||||
|
552_500_000UL,
|
||||||
|
4_096);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct PrimeBenchmarkCase(string Name, string Description, ulong[] Candidates)
|
||||||
|
{
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimeBenchmarkCase FromRange(
|
||||||
|
string name,
|
||||||
|
string description,
|
||||||
|
ulong startInclusive,
|
||||||
|
ulong endInclusive,
|
||||||
|
int maxCount)
|
||||||
|
{
|
||||||
|
ulong[] values = CollectRange(startInclusive, endInclusive, maxCount);
|
||||||
|
return new PrimeBenchmarkCase(name, description, values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ulong[] CollectRange(ulong startInclusive, ulong endInclusive, int maxCount)
|
||||||
|
{
|
||||||
|
maxCount = maxCount < 0 ? 0 : maxCount;
|
||||||
|
if (maxCount == 0)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
int estimatedLength = (int)Math.Min((ulong)maxCount, endInclusive - startInclusive + 1UL);
|
||||||
|
ulong[] buffer = new ulong[estimatedLength];
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (ulong value = startInclusive; value <= endInclusive && count < maxCount; value++)
|
||||||
|
{
|
||||||
|
if (!IsAdmissibleMersenneDivisorCandidate(value))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == buffer.Length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref buffer, buffer.Length * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[count++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == buffer.Length)
|
||||||
|
{
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong[] result = new ulong[count];
|
||||||
|
Array.Copy(buffer, result, count);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsAdmissibleMersenneDivisorCandidate(ulong value)
|
||||||
|
{
|
||||||
|
if ((value & 1UL) == 0UL || value % 5UL == 0UL || value <= 1UL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong qMinusOne = value - 1UL;
|
||||||
|
|
||||||
|
for (ulong k = 1; k <= 5; k++)
|
||||||
|
{
|
||||||
|
ulong denominator = k << 1;
|
||||||
|
if (qMinusOne % denominator != 0UL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong p = qMinusOne / denominator;
|
||||||
|
if (p < 2UL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Prime.Numbers.IsPrime(p))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
|
|
||||||
@@ -11,10 +12,11 @@ namespace PerfectNumbers.Core.Gpu
|
|||||||
|
|
||||||
private int _index;
|
private int _index;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public int Rent()
|
public int Rent()
|
||||||
{
|
{
|
||||||
Atomic.CompareExchange(ref _index, Capacity, 0);
|
_ = Interlocked.CompareExchange(ref _index, Capacity, 0);
|
||||||
var index = Atomic.Add(ref _index, 1);
|
var index = Interlocked.Increment(ref _index);
|
||||||
if (index >= Capacity)
|
if (index >= Capacity)
|
||||||
{
|
{
|
||||||
index -= Capacity;
|
index -= Capacity;
|
||||||
@@ -23,6 +25,7 @@ namespace PerfectNumbers.Core.Gpu
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public void Return(int acceleratorIndex)
|
public void Return(int acceleratorIndex)
|
||||||
{
|
{
|
||||||
// Intentionally left empty - there's nothing to do here. We're not really renting anything.
|
// Intentionally left empty - there's nothing to do here. We're not really renting anything.
|
||||||
|
|||||||
@@ -377,12 +377,12 @@ internal static partial class PrimeOrderCalculator
|
|||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (Atomic.CompareExchange(ref _cpuCount, 0, 1) == 0)
|
if (Interlocked.CompareExchange(ref _cpuCount, 0, 1) == 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Atomic.CompareExchange(ref _cpuCount, 1, 0) == 1)
|
if (Interlocked.CompareExchange(ref _cpuCount, 1, 0) == 1)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -391,10 +391,10 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int cpuCount = Atomic.Add(ref _cpuCount, 1);
|
int cpuCount = Interlocked.Increment(ref _cpuCount);
|
||||||
if (cpuCount == PerfectNumberConstants.GpuRatio)
|
if (cpuCount == PerfectNumberConstants.GpuRatio)
|
||||||
{
|
{
|
||||||
Atomic.Add(ref _cpuCount, -PerfectNumberConstants.GpuRatio);
|
Interlocked.Add(ref _cpuCount, -PerfectNumberConstants.GpuRatio);
|
||||||
}
|
}
|
||||||
else if (cpuCount > PerfectNumberConstants.GpuRatio)
|
else if (cpuCount > PerfectNumberConstants.GpuRatio)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in new issue
Block a user