From bbbdd097a2063c4c8bec5d913e26e9fcdc8408bf Mon Sep 17 00:00:00 2001 From: Xian55 <367101+Xian55@users.noreply.github.com> Date: Sun, 8 Sep 2024 12:16:11 +0200 Subject: [PATCH] .gitignore BenchmarkDotNet artifacts Added new project Benchmarks Benchmarks: Added two bench --- .gitignore | 5 +- Benchmarks/Benchmarks.csproj | 18 +++ Benchmarks/PPather/PPatherHasHoles.cs | 74 +++++++++++ Benchmarks/Program.cs | 5 + ...CoreRequirementCreateTargetCastingSpell.cs | 120 ++++++++++++++++++ MasterOfPuppets.sln | 14 ++ 6 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 Benchmarks/Benchmarks.csproj create mode 100644 Benchmarks/PPather/PPatherHasHoles.cs create mode 100644 Benchmarks/Program.cs create mode 100644 Benchmarks/Requirement/CoreRequirementCreateTargetCastingSpell.cs diff --git a/.gitignore b/.gitignore index 00a5e8d6f..5373377e8 100644 --- a/.gitignore +++ b/.gitignore @@ -293,4 +293,7 @@ HeadlessServer/data_config.json PathingAPI/frame_config.json PathingAPI/addon_config.json -PathingAPI/data_config.json \ No newline at end of file +PathingAPI/data_config.json + +# BenchmarkDotNet artifacts +**/BenchmarkDotNet.Artifacts/ \ No newline at end of file diff --git a/Benchmarks/Benchmarks.csproj b/Benchmarks/Benchmarks.csproj new file mode 100644 index 000000000..b36f521ad --- /dev/null +++ b/Benchmarks/Benchmarks.csproj @@ -0,0 +1,18 @@ + + + + Exe + enable + enable + true + + + + + + + + + + + diff --git a/Benchmarks/PPather/PPatherHasHoles.cs b/Benchmarks/PPather/PPatherHasHoles.cs new file mode 100644 index 000000000..89e8b8df1 --- /dev/null +++ b/Benchmarks/PPather/PPatherHasHoles.cs @@ -0,0 +1,74 @@ +using BenchmarkDotNet.Attributes; + +namespace Benchmarks.PPather; + +[MemoryDiagnoser] +public class PPatherHasHoles +{ + + public bool hasholes = true; + + public readonly uint holes; + + // 0 ..3, 0 ..3 + private static readonly int[] old_holetab_h = [0x1111, 0x2222, 0x4444, 0x8888]; + private static readonly int[] old_holetab_v = [0x000F, 0x00F0, 0x0F00, 0xF000]; + + private static readonly int[] new_holetab = [ + 0x1111 & 0x000F, 0x1111 & 0x00F0, 0x1111 & 0x0F00, 0x1111 & 0xF000, + 0x2222 & 0x000F, 0x2222 & 0x00F0, 0x2222 & 0x0F00, 0x2222 & 0xF000, + 0x4444 & 0x000F, 0x4444 & 0x00F0, 0x4444 & 0x0F00, 0x4444 & 0xF000, + 0x8888 & 0x000F, 0x8888 & 0x00F0, 0x8888 & 0x0F00, 0x8888 & 0xF000 + ]; + + [Benchmark(Baseline = true)] + [ArgumentsSource(nameof(Inputs_Fast))] + public bool IsHole_Old(int i, int j) + { + if (!hasholes) + return false; + + i /= 2; + j /= 2; + + return i <= 3 && j <= 3 && (holes & old_holetab_h[i] & old_holetab_v[j]) != 0; + } + + [Benchmark] + [ArgumentsSource(nameof(Inputs_Fast))] + public bool IsHole_New(int i, int j) + { + if (!hasholes) + return false; + + i >>= 1; + j >>= 1; + + if (i > 3 || j > 3) + return false; + + int index = (i << 2) | j; + + return (holes & new_holetab[index]) != 0; + } + + public static IEnumerable Inputs_All() + { + const int min = -1; // -1 + const int max = 5; // 5 + + // Generate a range of inputs from -1,-1 to 5,5 + for (int i = min; i < max; i++) + { + for (int j = min; j < max; j++) + { + yield return [i, j]; + } + } + } + + public static IEnumerable Inputs_Fast() + { + yield return [0, 0]; + } +} diff --git a/Benchmarks/Program.cs b/Benchmarks/Program.cs new file mode 100644 index 000000000..9e5a32a99 --- /dev/null +++ b/Benchmarks/Program.cs @@ -0,0 +1,5 @@ +using BenchmarkDotNet.Running; + +using Requirement; + +BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); \ No newline at end of file diff --git a/Benchmarks/Requirement/CoreRequirementCreateTargetCastingSpell.cs b/Benchmarks/Requirement/CoreRequirementCreateTargetCastingSpell.cs new file mode 100644 index 000000000..06f95e657 --- /dev/null +++ b/Benchmarks/Requirement/CoreRequirementCreateTargetCastingSpell.cs @@ -0,0 +1,120 @@ +using System.Runtime.CompilerServices; + +using BenchmarkDotNet.Attributes; + +namespace Requirement; + +internal static class PlayerReader +{ + public static bool IsTargetCasting() => true; + + public static int SpellBeingCastByTarget { get; set; } +} + +[MemoryDiagnoser] +public class CoreRequirementCreateTargetCastingSpell +{ + private const char SEP1 = ':'; + private const char SEP2 = ','; + + public CoreRequirementCreateTargetCastingSpell() + { + /* + foreach(var input in CreateTargetCastingSpell_Inputs()) + { + CreateTargetCastingSpell_Old(input); + CreateTargetCastingSpell_New(input); + } + */ + } + + [Benchmark(Baseline = true)] + [ArgumentsSource(nameof(CreateTargetCastingSpell_Inputs))] + public void Run_Old(string text) => CreateTargetCastingSpell_Old(text); + + [Benchmark] + [ArgumentsSource(nameof(CreateTargetCastingSpell_Inputs))] + public void Run_New(string text) => CreateTargetCastingSpell_New(text); + + public Core.Requirement CreateTargetCastingSpell_Old(string requirement) + { + return create(requirement); + static Core.Requirement create(string requirement) + { + ReadOnlySpan span = requirement; + int sep1 = span.IndexOf(SEP1); + // 'TargetCastingSpell' + if (sep1 == -1) + { + return new Core.Requirement + { + HasRequirement = PlayerReader.IsTargetCasting, + LogMessage = () => "Target casting" + }; + } + + // 'TargetCastingSpell:_1_?,_n_' + string[] spellsPart = span[(sep1 + 1)..].ToString().Split(SEP2); + HashSet spellIds = spellsPart.Select(int.Parse).ToHashSet(); + + bool f() => spellIds.Contains(PlayerReader.SpellBeingCastByTarget); + string s() => $"Target casts {PlayerReader.SpellBeingCastByTarget} ∈ [{string.Join(SEP2, spellIds)}]"; + return new Core.Requirement + { + HasRequirement = f, + LogMessage = s + }; + } + } + + [SkipLocalsInit] + public Core.Requirement CreateTargetCastingSpell_New(string requirement) + { + return create(requirement); + static Core.Requirement create(string requirement) + { + ReadOnlySpan span = requirement; + int sep1 = span.IndexOf(SEP1); + // 'TargetCastingSpell' + if (sep1 == -1) + { + return new Core.Requirement + { + HasRequirement = PlayerReader.IsTargetCasting, + LogMessage = () => "Target casting" + }; + } + + // 'TargetCastingSpell:_1_?,_n_' + Span ranges = stackalloc Range[span.Length]; + ReadOnlySpan values = span[(sep1 + 1)..]; + int count = values.Split(ranges, SEP2); + + HashSet spellIds = new(count); + foreach (var range in ranges[..count]) + { + spellIds.Add(int.Parse(values[range])); + } + + bool f() => spellIds.Contains(PlayerReader.SpellBeingCastByTarget); + string s() => $"Target casts {PlayerReader.SpellBeingCastByTarget} ∈ [{string.Join(SEP2, spellIds)}]"; + return new Core.Requirement + { + HasRequirement = f, + LogMessage = s + }; + } + } + + public static IEnumerable CreateTargetCastingSpell_Inputs() + { + yield return "TargetCastingSpell"; + yield return "TargetCastingSpell:1"; + yield return "TargetCastingSpell:1,12"; + yield return "TargetCastingSpell:1,12,123"; + yield return "TargetCastingSpell:1,12,123,1234"; + yield return "TargetCastingSpell:4321,321,21,1"; + yield return "TargetCastingSpell:4321648,3721,24841,148484"; + } + +} diff --git a/MasterOfPuppets.sln b/MasterOfPuppets.sln index df1db7198..ae608b4d5 100644 --- a/MasterOfPuppets.sln +++ b/MasterOfPuppets.sln @@ -31,6 +31,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Frontend", "Frontend\Fronte EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HeadlessServer", "HeadlessServer\HeadlessServer.csproj", "{8364AC12-DBF5-4DFF-B3A2-87472413DE01}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Benchmarks\Benchmarks.csproj", "{488984A0-F158-4EDA-A2CB-57C10D8596E1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -185,6 +187,18 @@ Global {8364AC12-DBF5-4DFF-B3A2-87472413DE01}.Release|x64.Build.0 = Release|x64 {8364AC12-DBF5-4DFF-B3A2-87472413DE01}.Release|x86.ActiveCfg = Release|x86 {8364AC12-DBF5-4DFF-B3A2-87472413DE01}.Release|x86.Build.0 = Release|x86 + {488984A0-F158-4EDA-A2CB-57C10D8596E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {488984A0-F158-4EDA-A2CB-57C10D8596E1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {488984A0-F158-4EDA-A2CB-57C10D8596E1}.Debug|x64.ActiveCfg = Debug|Any CPU + {488984A0-F158-4EDA-A2CB-57C10D8596E1}.Debug|x64.Build.0 = Debug|Any CPU + {488984A0-F158-4EDA-A2CB-57C10D8596E1}.Debug|x86.ActiveCfg = Debug|Any CPU + {488984A0-F158-4EDA-A2CB-57C10D8596E1}.Debug|x86.Build.0 = Debug|Any CPU + {488984A0-F158-4EDA-A2CB-57C10D8596E1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {488984A0-F158-4EDA-A2CB-57C10D8596E1}.Release|Any CPU.Build.0 = Release|Any CPU + {488984A0-F158-4EDA-A2CB-57C10D8596E1}.Release|x64.ActiveCfg = Release|Any CPU + {488984A0-F158-4EDA-A2CB-57C10D8596E1}.Release|x64.Build.0 = Release|Any CPU + {488984A0-F158-4EDA-A2CB-57C10D8596E1}.Release|x86.ActiveCfg = Release|Any CPU + {488984A0-F158-4EDA-A2CB-57C10D8596E1}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE