Skip to content

Commit

Permalink
Merge branch 'micro-optimizations' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
WorkingRobot committed Nov 12, 2023
2 parents 334810c + c8b963d commit 4bf7397
Show file tree
Hide file tree
Showing 19 changed files with 66 additions and 50 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down
26 changes: 19 additions & 7 deletions Benchmark/Bench.cs
Original file line number Diff line number Diff line change
@@ -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>(T Data) where T : notnull
{
public static implicit operator T(SHAWrapper<T> 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
Expand Down Expand Up @@ -68,22 +80,22 @@ public class Bench
})
};

public static IEnumerable<SimulationState> States => Inputs.Select(i => new SimulationState(i));
public static IEnumerable<SHAWrapper<SimulationState>> States => Inputs.Select(i => new SHAWrapper<SimulationState>(new(i)));

public static IEnumerable<SolverConfig> Configs => new SolverConfig[]
public static IEnumerable<SHAWrapper<SolverConfig>> Configs => new SHAWrapper<SolverConfig>[]
{
new()
new(new()
{
Algorithm = SolverAlgorithm.Stepwise,
Iterations = 30_000,
}
})
};

[ParamsSource(nameof(States))]
public SimulationState State { get; set; }
public SHAWrapper<SimulationState> State { get; set; }

[ParamsSource(nameof(Configs))]
public SolverConfig Config { get; set; }
public SHAWrapper<SolverConfig> Config { get; set; }

[Benchmark]
public async Task<float> Solve()
Expand Down
8 changes: 6 additions & 2 deletions Benchmark/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Craftimizer.Simulator;
using Craftimizer.Simulator.Actions;
using Craftimizer.Solver;
using ObjectLayoutInspector;

namespace Craftimizer.Benchmark;

Expand Down Expand Up @@ -63,8 +64,11 @@ private static async Task RunTrace()

private static async Task RunOther()
{
//TypeLayout.PrintLayout<ArenaNode<SimulationNode>>(true);
//return;
TypeLayout.PrintLayout<SimulationState>(true);
TypeLayout.PrintLayout<Simulator.Simulator>(true);
TypeLayout.PrintLayout<BaseAction>(true);
TypeLayout.PrintLayout<SimulationNode>(true);
return;

var input = new SimulationInput(

Check warning on line 73 in Benchmark/Program.cs

View workflow job for this annotation

GitHub Actions / build

Unreachable code detected

Check warning on line 73 in Benchmark/Program.cs

View workflow job for this annotation

GitHub Actions / build

Unreachable code detected

Check warning on line 73 in Benchmark/Program.cs

View workflow job for this annotation

GitHub Actions / bench

Unreachable code detected

Check warning on line 73 in Benchmark/Program.cs

View workflow job for this annotation

GitHub Actions / build

Unreachable code detected

Check warning on line 73 in Benchmark/Program.cs

View workflow job for this annotation

GitHub Actions / build

Unreachable code detected

Check warning on line 73 in Benchmark/Program.cs

View workflow job for this annotation

GitHub Actions / bench

Unreachable code detected

Check warning on line 73 in Benchmark/Program.cs

View workflow job for this annotation

GitHub Actions / bench

Unreachable code detected

Check warning on line 73 in Benchmark/Program.cs

View workflow job for this annotation

GitHub Actions / build

Unreachable code detected

Check warning on line 73 in Benchmark/Program.cs

View workflow job for this annotation

GitHub Actions / build

Unreachable code detected
new CharacterStats
Expand Down
10 changes: 2 additions & 8 deletions Simulator/Actions/BaseComboAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -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);
}
2 changes: 1 addition & 1 deletion Simulator/SimulationState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<int> 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,
Expand Down
8 changes: 4 additions & 4 deletions Simulator/Simulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,17 @@ public virtual CompletionState CompletionState {

public IEnumerable<ActionType> 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);
Expand Down Expand Up @@ -75,7 +75,7 @@ private ActionResponse Execute(ActionType action)
return ActionResponse.UsedAction;
}

public (ActionResponse Response, SimulationState NewState, int FailedActionIdx) ExecuteMultiple(SimulationState state, IEnumerable<ActionType> actions)
public (ActionResponse Response, SimulationState NewState, int FailedActionIdx) ExecuteMultiple(in SimulationState state, IEnumerable<ActionType> actions)
{
State = state;
var i = 0;
Expand Down
2 changes: 1 addition & 1 deletion Simulator/SimulatorNoRandom.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Craftimizer.Simulator;

public class SimulatorNoRandom : Simulator
{
public SimulatorNoRandom(SimulationState state) : base(state)
public SimulatorNoRandom(in SimulationState state) : base(state)
{
}

Expand Down
2 changes: 1 addition & 1 deletion Solver/ActionSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public struct ActionSet
{
private uint bits;

public static readonly ActionType[] AcceptedActions = new[]
internal static ReadOnlySpan<ActionType> AcceptedActions => new[]
{
ActionType.StandardTouchCombo,
ActionType.AdvancedTouchCombo,
Expand Down
6 changes: 6 additions & 0 deletions Solver/Craftimizer.Solver.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
<ProjectReference Include="..\Simulator\Craftimizer.Simulator.csproj" />
</ItemGroup>

<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>Craftimizer.Test</_Parameter1>
</AssemblyAttribute>
</ItemGroup>

<PropertyGroup Condition="'$(IS_BENCH)'=='1'">
<DefineConstants>$(DefineConstants);IS_DETERMINISTIC</DefineConstants>
</PropertyGroup>
Expand Down
12 changes: 6 additions & 6 deletions Solver/MCTS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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(
Expand Down Expand Up @@ -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<float>.Count;
Expand Down Expand Up @@ -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<float>.Count;
Expand Down Expand Up @@ -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)!;
}
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions Solver/MCTSConfig.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Runtime.InteropServices;
using System.Runtime.InteropServices;

namespace Craftimizer.Solver;

Expand All @@ -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;
Expand Down
13 changes: 7 additions & 6 deletions Solver/NodeScoresBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<float> ScoreSum;
public Memory<float> MaxScore;
public Memory<int> Visits;
public readonly Memory<float> ScoreSum;
public readonly Memory<float> MaxScore;
public readonly Memory<int> Visits;

public ScoresBatch()
{
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions Solver/SimulationNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion Solver/Simulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion Solver/Solver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 1 addition & 3 deletions Solver/SolverConfig.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using Craftimizer.Simulator.Actions;
using Craftimizer.Simulator;
using System.Runtime.InteropServices;

namespace Craftimizer.Solver;
Expand Down Expand Up @@ -71,4 +69,4 @@ public SolverConfig()
FurcatedActionCount = Environment.ProcessorCount / 2,
Algorithm = SolverAlgorithm.StepwiseForked
};
}
}
2 changes: 1 addition & 1 deletion Solver/SolverSolution.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public readonly record struct SolverSolution {
public readonly IEnumerable<ActionType> ActionEnumerable { init => actions = SanitizeCombos(value).ToList(); }
public readonly SimulationState State { get; init; }

public SolverSolution(IEnumerable<ActionType> actions, SimulationState state)
public SolverSolution(IEnumerable<ActionType> actions, in SimulationState state)
{
ActionEnumerable = actions;
State = state;
Expand Down
2 changes: 0 additions & 2 deletions Test/Solver/ActionSet.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using Craftimizer.Simulator.Actions;

namespace Craftimizer.Test.Solver;

[TestClass]
Expand Down
1 change: 1 addition & 0 deletions Test/Usings.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
global using Microsoft.VisualStudio.TestTools.UnitTesting;
global using Craftimizer.Solver;
global using Craftimizer.Simulator;
global using Craftimizer.Simulator.Actions;

2 comments on commit 4bf7397

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: 4bf7397 Previous: a8c3f34 Ratio
Craftimizer.Benchmark.Bench.Solve(State: 5372D31C98FA4C357F54029912394B0F5ECBE94AEC9D12C1C2B7F453C62ACD2F, Config: E1C895829E5BB1533C41D3C71AD65B6B3E1A60B5EE2D35A845744C251E71EFA5) 1985541453.3333333 ns (± 29812667.045422792)
Craftimizer.Benchmark.Bench.Solve(State: 5372D31C98FA4C357F54029912394B0F5ECBE94AEC9D12C1C2B7F453C62ACD2F, Config: E1C895829E5BB1533C41D3C71AD65B6B3E1A60B5EE2D35A845744C251E71EFA5) 1597875393.75 ns (± 29828760.020366684)
Craftimizer.Benchmark.Bench.Solve(State: 99B0F1AD46A18B4D8262F9BA75ABE23507217C2F20FBF895A49282DDFEF50190, Config: E1C895829E5BB1533C41D3C71AD65B6B3E1A60B5EE2D35A845744C251E71EFA5) 2222113428.571429 ns (± 31525711.855110083)
Craftimizer.Benchmark.Bench.Solve(State: 99B0F1AD46A18B4D8262F9BA75ABE23507217C2F20FBF895A49282DDFEF50190, Config: E1C895829E5BB1533C41D3C71AD65B6B3E1A60B5EE2D35A845744C251E71EFA5) 1755958162.5 ns (± 33919357.618482396)

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: 4bf7397 Previous: a8c3f34 Ratio
Craftimizer.Benchmark.Bench.Solve(State: 5372D31C98FA4C357F54029912394B0F5ECBE94AEC9D12C1C2B7F453C62ACD2F, Config: E1C895829E5BB1533C41D3C71AD65B6B3E1A60B5EE2D35A845744C251E71EFA5) 2109083533.3333333 ns (± 19528009.750599876)
Craftimizer.Benchmark.Bench.Solve(State: 5372D31C98FA4C357F54029912394B0F5ECBE94AEC9D12C1C2B7F453C62ACD2F, Config: E1C895829E5BB1533C41D3C71AD65B6B3E1A60B5EE2D35A845744C251E71EFA5) 1578882184.6153846 ns (± 14972031.469479248)
Craftimizer.Benchmark.Bench.Solve(State: 99B0F1AD46A18B4D8262F9BA75ABE23507217C2F20FBF895A49282DDFEF50190, Config: E1C895829E5BB1533C41D3C71AD65B6B3E1A60B5EE2D35A845744C251E71EFA5) 2197847773.3333335 ns (± 27619337.230836418)
Craftimizer.Benchmark.Bench.Solve(State: 99B0F1AD46A18B4D8262F9BA75ABE23507217C2F20FBF895A49282DDFEF50190, Config: E1C895829E5BB1533C41D3C71AD65B6B3E1A60B5EE2D35A845744C251E71EFA5) 1709379806.6666667 ns (± 17448712.709270753)

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.