diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a8dc56..e85119a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -90,7 +90,9 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: '7.0' + dotnet-version: | + 7.0 + 8.0 - name: Download Dalamud run: | diff --git a/Benchmark/Bench.cs b/Benchmark/Bench.cs index 2518195..8485503 100644 --- a/Benchmark/Bench.cs +++ b/Benchmark/Bench.cs @@ -1,15 +1,27 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Diagnostics.dotTrace; +using BenchmarkDotNet.Jobs; using Craftimizer.Simulator; using Craftimizer.Solver; +using System.Security.Cryptography; +using System.Text; namespace Craftimizer.Benchmark; -[SimpleJob(iterationCount: 10)] +[SimpleJob(RuntimeMoniker.Net70, baseline: true)] +[SimpleJob(RuntimeMoniker.Net80)] [MinColumn, Q1Column, Q3Column, MaxColumn] [DotTraceDiagnoser] public class Bench { + public record struct SHAWrapper(T Data) where T : notnull + { + public static implicit operator T(SHAWrapper wrapper) => wrapper.Data; + + public override readonly string ToString() => + Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(Data.ToString()!))); + } + private static SimulationInput[] Inputs { get; } = new SimulationInput[] { // https://craftingway.app/rotation/loud-namazu-jVe9Y // Chondrite Saw @@ -68,22 +80,22 @@ public class Bench }) }; - public static IEnumerable States => Inputs.Select(i => new SimulationState(i)); + public static IEnumerable> States => Inputs.Select(i => new SHAWrapper(new(i))); - public static IEnumerable Configs => new SolverConfig[] + public static IEnumerable> Configs => new SHAWrapper[] { - new() + new(new() { Algorithm = SolverAlgorithm.Stepwise, Iterations = 30_000, - } + }) }; [ParamsSource(nameof(States))] - public SimulationState State { get; set; } + public SHAWrapper State { get; set; } [ParamsSource(nameof(Configs))] - public SolverConfig Config { get; set; } + public SHAWrapper Config { get; set; } [Benchmark] public async Task Solve() diff --git a/Benchmark/Program.cs b/Benchmark/Program.cs index 70c6299..4ba614e 100644 --- a/Benchmark/Program.cs +++ b/Benchmark/Program.cs @@ -1,6 +1,7 @@ using Craftimizer.Simulator; using Craftimizer.Simulator.Actions; using Craftimizer.Solver; +using ObjectLayoutInspector; namespace Craftimizer.Benchmark; @@ -63,8 +64,11 @@ private static async Task RunTrace() private static async Task RunOther() { - //TypeLayout.PrintLayout>(true); - //return; + TypeLayout.PrintLayout(true); + TypeLayout.PrintLayout(true); + TypeLayout.PrintLayout(true); + TypeLayout.PrintLayout(true); + return; var input = new SimulationInput( new CharacterStats diff --git a/Simulator/Actions/BaseComboAction.cs b/Simulator/Actions/BaseComboAction.cs index 80d11f7..16876c5 100644 --- a/Simulator/Actions/BaseComboAction.cs +++ b/Simulator/Actions/BaseComboAction.cs @@ -10,7 +10,7 @@ public abstract class BaseComboAction : BaseAction protected bool BaseCanUse(Simulator s) => base.CanUse(s); - private static bool VerifyDurability2(int durabilityA, int durability, Effects effects) + private static bool VerifyDurability2(int durabilityA, int durability, in Effects effects) { var wasteNots = effects.HasEffect(EffectType.WasteNot) || effects.HasEffect(EffectType.WasteNot2); // -A @@ -23,13 +23,10 @@ private static bool VerifyDurability2(int durabilityA, int durability, Effects e return true; } - public static bool VerifyDurability2(SimulationState s, int durabilityA) => - VerifyDurability2(durabilityA, s.Durability, s.ActiveEffects); - public static bool VerifyDurability2(Simulator s, int durabilityA) => VerifyDurability2(durabilityA, s.Durability, s.ActiveEffects); - public static bool VerifyDurability3(int durabilityA, int durabilityB, int durability, Effects effects) + public static bool VerifyDurability3(int durabilityA, int durabilityB, int durability, in Effects effects) { var wasteNots = Math.Max(effects.GetDuration(EffectType.WasteNot), effects.GetDuration(EffectType.WasteNot2)); var manips = effects.HasEffect(EffectType.Manipulation); @@ -56,7 +53,4 @@ public static bool VerifyDurability3(int durabilityA, int durabilityB, int durab public static bool VerifyDurability3(Simulator s, int durabilityA, int durabilityB) => VerifyDurability3(durabilityA, durabilityB, s.Durability, s.ActiveEffects); - - public static bool VerifyDurability3(SimulationState s, int durabilityA, int durabilityB) => - VerifyDurability3(durabilityA, durabilityB, s.Durability, s.ActiveEffects); } diff --git a/Simulator/SimulationState.cs b/Simulator/SimulationState.cs index e743ade..8147cdb 100644 --- a/Simulator/SimulationState.cs +++ b/Simulator/SimulationState.cs @@ -18,7 +18,7 @@ public record struct SimulationState public ActionStates ActionStates; // https://github.com/ffxiv-teamcraft/simulator/blob/0682dfa76043ff4ccb38832c184d046ceaff0733/src/model/tables.ts#L2 - private static readonly int[] HQPercentTable = { + private static ReadOnlySpan HQPercentTable => new[] { 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 20, 20, 21, 22, 23, 24, 26, 28, 31, 34, 38, 42, 47, 52, 58, 64, 68, 71, diff --git a/Simulator/Simulator.cs b/Simulator/Simulator.cs index 492b4db..03dbb12 100644 --- a/Simulator/Simulator.cs +++ b/Simulator/Simulator.cs @@ -35,17 +35,17 @@ public virtual CompletionState CompletionState { public IEnumerable AvailableActions => ActionUtils.AvailableActions(this); - public Simulator(SimulationState state) + public Simulator(in SimulationState state) { State = state; } - public void SetState(SimulationState state) + public void SetState(in SimulationState state) { State = state; } - public (ActionResponse Response, SimulationState NewState) Execute(SimulationState state, ActionType action) + public (ActionResponse Response, SimulationState NewState) Execute(in SimulationState state, ActionType action) { State = state; return (Execute(action), State); @@ -75,7 +75,7 @@ private ActionResponse Execute(ActionType action) return ActionResponse.UsedAction; } - public (ActionResponse Response, SimulationState NewState, int FailedActionIdx) ExecuteMultiple(SimulationState state, IEnumerable actions) + public (ActionResponse Response, SimulationState NewState, int FailedActionIdx) ExecuteMultiple(in SimulationState state, IEnumerable actions) { State = state; var i = 0; diff --git a/Simulator/SimulatorNoRandom.cs b/Simulator/SimulatorNoRandom.cs index 1f4c827..e6c4738 100644 --- a/Simulator/SimulatorNoRandom.cs +++ b/Simulator/SimulatorNoRandom.cs @@ -2,7 +2,7 @@ namespace Craftimizer.Simulator; public class SimulatorNoRandom : Simulator { - public SimulatorNoRandom(SimulationState state) : base(state) + public SimulatorNoRandom(in SimulationState state) : base(state) { } diff --git a/Solver/ActionSet.cs b/Solver/ActionSet.cs index 24b135d..b7cc97d 100644 --- a/Solver/ActionSet.cs +++ b/Solver/ActionSet.cs @@ -9,7 +9,7 @@ public struct ActionSet { private uint bits; - public static readonly ActionType[] AcceptedActions = new[] + internal static ReadOnlySpan AcceptedActions => new[] { ActionType.StandardTouchCombo, ActionType.AdvancedTouchCombo, diff --git a/Solver/Craftimizer.Solver.csproj b/Solver/Craftimizer.Solver.csproj index bf16279..eaea55b 100644 --- a/Solver/Craftimizer.Solver.csproj +++ b/Solver/Craftimizer.Solver.csproj @@ -20,6 +20,12 @@ + + + <_Parameter1>Craftimizer.Test + + + $(DefineConstants);IS_DETERMINISTIC diff --git a/Solver/MCTS.cs b/Solver/MCTS.cs index 8e40a6b..76251db 100644 --- a/Solver/MCTS.cs +++ b/Solver/MCTS.cs @@ -17,7 +17,7 @@ public sealed class MCTS public float MaxScore => rootScores.MaxScore; - public MCTS(MCTSConfig config, SimulationState state) + public MCTS(in MCTSConfig config, in SimulationState state) { this.config = config; var sim = new Simulator(state, config.MaxStepCount); @@ -30,7 +30,7 @@ public MCTS(MCTSConfig config, SimulationState state) rootScores = new(); } - private static SimulationNode Execute(Simulator simulator, SimulationState state, ActionType action, bool strict) + private static SimulationNode Execute(Simulator simulator, in SimulationState state, ActionType action, bool strict) { (_, var newState) = simulator.Execute(state, action); return new( @@ -61,7 +61,7 @@ private static Node ExecuteActions(Simulator simulator, Node startNode, ReadOnly [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static (int arrayIdx, int subIdx) ChildMaxScore(ref NodeScoresBuffer scores) + private static (int arrayIdx, int subIdx) ChildMaxScore(in NodeScoresBuffer scores) { var length = scores.Count; var vecLength = Vector.Count; @@ -111,7 +111,7 @@ private static (int arrayIdx, int subIdx) EvalBestChild( float explorationConstant, float maxScoreWeightingConstant, int parentVisits, - ref NodeScoresBuffer scores) + in NodeScoresBuffer scores) { var length = scores.Count; var vecLength = Vector.Count; @@ -168,7 +168,7 @@ private Node Select() return node; // select the node with the highest score - var at = EvalBestChild(explorationConstant, maxScoreWeightingConstant, nodeVisits, ref node.ChildScores); + var at = EvalBestChild(explorationConstant, maxScoreWeightingConstant, nodeVisits, in node.ChildScores); nodeVisits = node.ChildScores.GetVisits(at); node = node.ChildAt(at)!; } @@ -320,7 +320,7 @@ public SolverSolution Solution() while (node.Children.Count != 0) { - node = node.ChildAt(ChildMaxScore(ref node.ChildScores))!; + node = node.ChildAt(ChildMaxScore(in node.ChildScores))!; if (node.State.Action != null) actions.Add(node.State.Action.Value); diff --git a/Solver/MCTSConfig.cs b/Solver/MCTSConfig.cs index a0e554d..1bf4e3d 100644 --- a/Solver/MCTSConfig.cs +++ b/Solver/MCTSConfig.cs @@ -1,4 +1,4 @@ -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace Craftimizer.Solver; @@ -21,7 +21,7 @@ public readonly record struct MCTSConfig public float ScoreCP { get; init; } public float ScoreSteps { get; init; } - public MCTSConfig(SolverConfig config) + public MCTSConfig(in SolverConfig config) { MaxStepCount = config.MaxStepCount; MaxRolloutStepCount = config.MaxRolloutStepCount; diff --git a/Solver/NodeScoresBuffer.cs b/Solver/NodeScoresBuffer.cs index fecfb40..5d45e59 100644 --- a/Solver/NodeScoresBuffer.cs +++ b/Solver/NodeScoresBuffer.cs @@ -7,11 +7,11 @@ namespace Craftimizer.Solver; // Adapted from https://github.com/dtao/ConcurrentList/blob/4fcf1c76e93021a41af5abb2d61a63caeba2adad/ConcurrentList/ConcurrentList.cs public struct NodeScoresBuffer { - public sealed class ScoresBatch + public readonly struct ScoresBatch { - public Memory ScoreSum; - public Memory MaxScore; - public Memory Visits; + public readonly Memory ScoreSum; + public readonly Memory MaxScore; + public readonly Memory Visits; public ScoresBatch() { @@ -40,9 +40,10 @@ public void Add() var idx = Count++; - var (arrayIdx, _) = GetArrayIndex(idx); + var (arrayIdx, subIdx) = GetArrayIndex(idx); - Data[arrayIdx] ??= new(); + if (subIdx == 0) + Data[arrayIdx] = new(); } public readonly void Visit((int arrayIdx, int subIdx) at, float score) diff --git a/Solver/SimulationNode.cs b/Solver/SimulationNode.cs index a0c722b..a2b8f34 100644 --- a/Solver/SimulationNode.cs +++ b/Solver/SimulationNode.cs @@ -19,7 +19,7 @@ public struct SimulationNode public readonly bool IsComplete => CompletionState != CompletionState.Incomplete; - public SimulationNode(SimulationState state, ActionType? action, CompletionState completionState, ActionSet actions) + public SimulationNode(in SimulationState state, ActionType? action, CompletionState completionState, ActionSet actions) { State = state; Action = action; @@ -32,10 +32,10 @@ public static CompletionState GetCompletionState(CompletionState simCompletionSt CompletionState.NoMoreActions : simCompletionState; - public readonly float? CalculateScore(MCTSConfig config) => + public readonly float? CalculateScore(in MCTSConfig config) => CalculateScoreForState(State, SimulationCompletionState, config); - public static float? CalculateScoreForState(SimulationState state, CompletionState completionState, MCTSConfig config) + public static float? CalculateScoreForState(in SimulationState state, CompletionState completionState, MCTSConfig config) { if (completionState != CompletionState.ProgressComplete) return null; diff --git a/Solver/Simulator.cs b/Solver/Simulator.cs index 8edf3e4..a3a29fa 100644 --- a/Solver/Simulator.cs +++ b/Solver/Simulator.cs @@ -20,7 +20,7 @@ public override CompletionState CompletionState } } - public Simulator(SimulationState state, int maxStepCount) : base(state) + public Simulator(in SimulationState state, int maxStepCount) : base(state) { this.maxStepCount = maxStepCount; } diff --git a/Solver/Solver.cs b/Solver/Solver.cs index ca6343e..7c9efcf 100644 --- a/Solver/Solver.cs +++ b/Solver/Solver.cs @@ -35,7 +35,7 @@ public sealed class Solver : IDisposable // Always called when a new step is generated. public event NewActionDelegate? OnNewAction; - public Solver(SolverConfig config, SimulationState state) + public Solver(in SolverConfig config, in SimulationState state) { Config = config; State = state; diff --git a/Solver/SolverConfig.cs b/Solver/SolverConfig.cs index d261949..65274a9 100644 --- a/Solver/SolverConfig.cs +++ b/Solver/SolverConfig.cs @@ -1,5 +1,3 @@ -using Craftimizer.Simulator.Actions; -using Craftimizer.Simulator; using System.Runtime.InteropServices; namespace Craftimizer.Solver; @@ -71,4 +69,4 @@ public SolverConfig() FurcatedActionCount = Environment.ProcessorCount / 2, Algorithm = SolverAlgorithm.StepwiseForked }; -} \ No newline at end of file +} diff --git a/Solver/SolverSolution.cs b/Solver/SolverSolution.cs index f512479..ce38fce 100644 --- a/Solver/SolverSolution.cs +++ b/Solver/SolverSolution.cs @@ -9,7 +9,7 @@ public readonly record struct SolverSolution { public readonly IEnumerable ActionEnumerable { init => actions = SanitizeCombos(value).ToList(); } public readonly SimulationState State { get; init; } - public SolverSolution(IEnumerable actions, SimulationState state) + public SolverSolution(IEnumerable actions, in SimulationState state) { ActionEnumerable = actions; State = state; diff --git a/Test/Solver/ActionSet.cs b/Test/Solver/ActionSet.cs index 0f4c9df..fd8c8a1 100644 --- a/Test/Solver/ActionSet.cs +++ b/Test/Solver/ActionSet.cs @@ -1,5 +1,3 @@ -using Craftimizer.Simulator.Actions; - namespace Craftimizer.Test.Solver; [TestClass] diff --git a/Test/Usings.cs b/Test/Usings.cs index da624f8..c58173e 100644 --- a/Test/Usings.cs +++ b/Test/Usings.cs @@ -1,3 +1,4 @@ global using Microsoft.VisualStudio.TestTools.UnitTesting; global using Craftimizer.Solver; global using Craftimizer.Simulator; +global using Craftimizer.Simulator.Actions;