From 0de1faa11263592073a2d8e7970e4bcacc03c89b Mon Sep 17 00:00:00 2001 From: Asriel Camora Date: Wed, 21 Jun 2023 20:44:13 -0700 Subject: [PATCH] Add benchmarks --- Benchmark/Bench.cs | 59 ++++++++++++++++++++++++++ Benchmark/Craftimizer.Benchmark.csproj | 1 + Benchmark/Program.cs | 6 ++- Solver/Crafty/Solver.cs | 43 ++++++++++++++----- 4 files changed, 97 insertions(+), 12 deletions(-) create mode 100644 Benchmark/Bench.cs diff --git a/Benchmark/Bench.cs b/Benchmark/Bench.cs new file mode 100644 index 0000000..1ee12a8 --- /dev/null +++ b/Benchmark/Bench.cs @@ -0,0 +1,59 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace Craftimizer.Benchmark; + +[SimpleJob(RuntimeMoniker.Net70)] +[SimpleJob(RuntimeMoniker.NativeAot70)] +public class Bench +{ + private float[] data; + private int[] dataLengths; + + [Params(1000, 10000)] + public int N; + + [GlobalSetup] + public void Setup() + { + var rand = new Random(); + data = new float[N * 8]; + dataLengths = new int[N]; + for (var i = 0; i < data.Length; i += 8) + { + var len = rand.NextSingle() > .5 ? 8 : rand.Next(1, 9); + dataLengths[i / 8] = len; + for (var j = 0; j < len; ++j) + data[i + j] = rand.NextSingle(); + for (var j = len; j < 8; ++j) + data[i + j] = float.NaN; + } + } + + [Benchmark] + public int[] Scalar() + { + var d = new int[N]; + var dataSpan = data.AsSpan(); + for (var i = 0; i < N; ++i) + d[i] = Solver.Crafty.Solver.HMaxIndexScalar(new Vector(dataSpan.Slice(i * 8, 8)), dataLengths[i]); + return d; + } + + [Benchmark] + public int[] AVX2() + { + var d = new int[128]; + var dataSpan = data.AsSpan(); + for (var i = 0; i < 128; ++i) + d[i] = Solver.Crafty.Solver.HMaxIndexAVX2(new Vector(dataSpan.Slice(i * 8, 8)), dataLengths[i]); + return d; + } +} diff --git a/Benchmark/Craftimizer.Benchmark.csproj b/Benchmark/Craftimizer.Benchmark.csproj index e7b4a1c..1fc9825 100644 --- a/Benchmark/Craftimizer.Benchmark.csproj +++ b/Benchmark/Craftimizer.Benchmark.csproj @@ -8,6 +8,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Benchmark/Program.cs b/Benchmark/Program.cs index bb9201a..ad81a72 100644 --- a/Benchmark/Program.cs +++ b/Benchmark/Program.cs @@ -1,3 +1,4 @@ +using BenchmarkDotNet.Running; using Craftimizer.Simulator; using Craftimizer.Simulator.Actions; using Craftimizer.Solver.Crafty; @@ -10,6 +11,9 @@ internal static class Program { private static void Main() { + //var summary = BenchmarkRunner.Run(); + //return; + //TypeLayout.PrintLayout>(true); //return; @@ -33,7 +37,7 @@ internal static class Program var config = new SolverConfig() { - Iterations = 1_000_000 + Iterations = 30_000,//1_000_000 }; var s = Stopwatch.StartNew(); diff --git a/Solver/Crafty/Solver.cs b/Solver/Crafty/Solver.cs index 1893b8b..61f5a5b 100644 --- a/Solver/Crafty/Solver.cs +++ b/Solver/Crafty/Solver.cs @@ -99,23 +99,45 @@ public class Solver [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - // https://stackoverflow.com/a/23592221 - private static (int, uint) HMaxIndex(Vector256 v, int len) + public static int HMaxIndexScalar(Vector v, int len) { - var vfilt = Avx.Blend(v, Vector256.Zero, (byte)~((1 << len) - 1)); + var m = 0; + for (var i = 1; i < len; ++i) + { + if (v[i] >= v[m]) + m = i; + } + return m; + } + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + // https://stackoverflow.com/a/23592221 + public static int HMaxIndexAVX2(Vector v, int len) + { + // Remove NaNs + var vfilt = Avx.Blend(v.AsVector256(), Vector256.Zero, (byte)~((1 << len) - 1)); + + // Find max value and broadcast to all lanes var vmax128 = HMax(vfilt); var vmax = Vector256.Create(vmax128, vmax128); - var vcmp = Avx.CompareEqual(v, vmax); + // Find the highest index with that value, respecting len + var vcmp = Avx.CompareEqual(vfilt, vmax); var mask = unchecked((uint)Avx2.MoveMask(vcmp.AsByte())); - mask <<= (8 - len) << 2; - var inverseIdx = BitOperations.LeadingZeroCount(mask) >> 2; + var inverseIdx = BitOperations.LeadingZeroCount(mask << ((8 - len) << 2)) >> 2; - return (len - 1 - inverseIdx, mask); + return len - 1 - inverseIdx; } + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int HMaxIndex(Vector v, int len) => + Avx2.IsSupported ? + HMaxIndexAVX2(v, len) : + HMaxIndexScalar(v, len); + [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] private Node EvalBestChild(float parentVisits, ReadOnlySpan children) @@ -134,7 +156,6 @@ public class Solver var max = 0; var maxScore = 0f; - for (var i = 0; i < length; i += vecLength) { var iterCount = i + vecLength > length ? @@ -153,7 +174,7 @@ public class Solver var exploration = Vector.SquareRoot(CVector / new Vector(visits)); var evalScores = exploitation + exploration; - var (idx, mask) = HMaxIndex(evalScores.AsVector256(), iterCount); + var idx = HMaxIndex(evalScores, iterCount); if (evalScores[idx] >= maxScore) { @@ -186,7 +207,7 @@ public class Solver if (initialState.IsComplete) return (initialNode, initialState.CompletionState, initialState.CalculateScore(Config.MaxStepCount) ?? 0); - var randomAction = initialState.AvailableActions.SelectRandom(Random); + var randomAction = initialState.AvailableActions.First();//.SelectRandom(Random); initialState.AvailableActions.RemoveAction(randomAction); var expandedNode = initialNode.Add(Execute(initialState.State, randomAction, true)); @@ -201,7 +222,7 @@ public class Solver { if (SimulationNode.GetCompletionState(currentCompletionState, currentActions) != CompletionState.Incomplete) break; - randomAction = currentActions.SelectRandom(Random); + randomAction = currentActions.First();//.SelectRandom(Random); actions[actionCount++] = randomAction; (_, currentState) = Simulator.Execute(currentState, randomAction); currentCompletionState = Simulator.CompletionState;