Skip to content

Commit

Permalink
Memory-related improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
WorkingRobot committed Nov 11, 2023
1 parent 6965ec4 commit adf3a80
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 49 deletions.
14 changes: 11 additions & 3 deletions Benchmark/Bench.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using BenchmarkDotNet.Jobs;
using Craftimizer.Simulator;
using Craftimizer.Solver;
using System.Security.Cryptography;
using System.Text;

namespace Craftimizer.Benchmark;

Expand All @@ -12,6 +14,12 @@ namespace Craftimizer.Benchmark;
[DotTraceDiagnoser]
public class Bench
{
public record struct SimStateWrapper(SimulationState State)
{
public override readonly string ToString() =>
Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(State.ToString())));
}

private static SimulationInput[] Inputs { get; } = new SimulationInput[] {
// https://craftingway.app/rotation/loud-namazu-jVe9Y
// Chondrite Saw
Expand Down Expand Up @@ -70,7 +78,7 @@ public class Bench
})
};

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

public static IEnumerable<SolverConfig> Configs => new SolverConfig[]
{
Expand All @@ -82,15 +90,15 @@ public class Bench
};

[ParamsSource(nameof(States))]
public SimulationState State { get; set; }
public SimStateWrapper State { get; set; }

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

[Benchmark]
public async Task<float> Solve()
{
var solver = new Solver.Solver(Config, State);
var solver = new Solver.Solver(Config, State.State);
solver.Start();
var (_, s) = await solver.GetTask().ConfigureAwait(false);
return (float)s.Quality / s.Input.Recipe.MaxQuality;
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
new CharacterStats
Expand Down
11 changes: 6 additions & 5 deletions Craftimizer/Utils/Gearsets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,21 @@ public record struct GearsetItem(uint itemId, bool isHq, GearsetMateria[] materi
public const int ParamCraftsmanship = 70;
public const int ParamControl = 71;

private static readonly int[] LevelToCLvlLUT;
private static ReadOnlyMemory<int> LevelToCLvlLUT { get; }

static Gearsets()
{
LevelToCLvlLUT = new int[90];
var lut = new int[90];
for (uint i = 0; i < 80; ++i) {
var level = i + 1;
LevelToCLvlLUT[i] = LuminaSheets.ParamGrowSheet.GetRow(level)!.CraftingLevel;
lut[i] = LuminaSheets.ParamGrowSheet.GetRow(level)!.CraftingLevel;
}
for (var i = 80; i < 90; ++i)
{
var level = i + 1;
LevelToCLvlLUT[i] = (int)LuminaSheets.RecipeLevelTableSheet.First(r => r.ClassJobLevel == level).RowId;
lut[i] = (int)LuminaSheets.RecipeLevelTableSheet.First(r => r.ClassJobLevel == level).RowId;
}
LevelToCLvlLUT = lut;
}

public static void Initialize() { }
Expand Down Expand Up @@ -148,7 +149,7 @@ public static bool IsSplendorousTool(GearsetItem item) =>

public static int CalculateCLvl(int level) =>
(level > 0 && level <= 90) ?
LevelToCLvlLUT[level - 1] :
LevelToCLvlLUT.Span[level - 1] :
throw new ArgumentOutOfRangeException(nameof(level), level, "Level is out of range.");

// https://github.com/ffxiv-teamcraft/ffxiv-teamcraft/blob/24d0db2d9676f264edf53651b21005305267c84c/apps/client/src/app/modules/gearsets/materia.service.ts#L265
Expand Down
4 changes: 2 additions & 2 deletions Simulator/Actions/ActionType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public enum ActionType : byte

public static class ActionUtils
{
private static readonly BaseAction[] Actions;
private static ReadOnlyMemory<BaseAction> Actions { get; }

static ActionUtils()
{
Expand All @@ -61,7 +61,7 @@ static ActionUtils()

[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BaseAction Base(this ActionType me) => Actions[(int)me];
public static BaseAction Base(this ActionType me) => Actions.Span[(int)me];

public static IEnumerable<ActionType> AvailableActions(Simulator simulation) =>
simulation.IsComplete
Expand Down
2 changes: 1 addition & 1 deletion Simulator/Effects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Craftimizer.Simulator;

[StructLayout(LayoutKind.Auto)]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public record struct Effects
{
public byte InnerQuiet;
Expand Down
13 changes: 2 additions & 11 deletions Simulator/SimulationState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Craftimizer.Simulator;

[StructLayout(LayoutKind.Auto)]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public record struct SimulationState
{
public readonly SimulationInput Input;
Expand All @@ -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 All @@ -44,13 +44,4 @@ public SimulationState(SimulationInput input)
ActionCount = 0;
ActionStates = new();
}

#if IS_DETERMINISTIC
public override readonly string ToString()
{
var b = new System.Text.StringBuilder();
PrintMembers(b);
return Convert.ToHexString(System.Security.Cryptography.SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(b.ToString())));
}
#endif
}
12 changes: 6 additions & 6 deletions Solver/MCTS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ private static (int arrayIdx, int subIdx) ChildMaxScore(in NodeScoresBuffer scor
{
var iterCount = Math.Min(vecLength, length);

ref var chunk = ref scores.Data[i];
var m = new Vector<float>(chunk.MaxScore.Span);
ref var chunk = ref scores.Data!.Value.Span[i];
var m = new Vector<float>(chunk.MaxScore);

var idx = Intrinsics.HMaxIndex(m, iterCount);

Expand Down Expand Up @@ -127,10 +127,10 @@ private static (int arrayIdx, int subIdx) EvalBestChild(
{
var iterCount = Math.Min(vecLength, length);

ref var chunk = ref scores.Data[i];
var s = new Vector<float>(chunk.ScoreSum.Span);
var vInt = new Vector<int>(chunk.Visits.Span);
var m = new Vector<float>(chunk.MaxScore.Span);
ref var chunk = ref scores.Data!.Value.Span[i];
var s = new Vector<float>(chunk.ScoreSum);
var vInt = new Vector<int>(chunk.Visits);
var m = new Vector<float>(chunk.MaxScore);

vInt = Vector.Max(vInt, Vector<int>.One);
var v = Vector.ConvertToSingle(vInt);
Expand Down
40 changes: 22 additions & 18 deletions Solver/NodeScoresBuffer.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
using System.Diagnostics.Contracts;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

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 ScoresBatch()
{
ScoreSum = new float[BatchSize];
MaxScore = new float[BatchSize];
Visits = new int[BatchSize];
}
private readonly Memory<byte> data;

public Span<float> ScoreSum =>
MemoryMarshal.Cast<byte, float>(data.Span.Slice(0, BatchSize * sizeof(float)));
public Span<float> MaxScore =>
MemoryMarshal.Cast<byte, float>(data.Span.Slice(BatchSize * sizeof(float), BatchSize * sizeof(float)));
public Span<int> Visits =>
MemoryMarshal.Cast<byte, int>(data.Span.Slice(BatchSize * sizeof(float) * 2, BatchSize * sizeof(int)));

public ScoresBatch() =>
data = new byte[BatchSize * sizeof(float) * 3];
}

// Technically 25, but it's very unlikely to actually get to there.
Expand All @@ -31,7 +33,7 @@ public ScoresBatch()

private static readonly int BatchCount = MaxSize / BatchSize;

public ScoresBatch[] Data;
public Memory<ScoresBatch>? Data;
public int Count { get; private set; }

public void Add()
Expand All @@ -40,20 +42,22 @@ public void Add()

var idx = Count++;

var (arrayIdx, _) = GetArrayIndex(idx);
var (arrayIdx, subIdx) = GetArrayIndex(idx);

Data[arrayIdx] ??= new();
if (subIdx == 0)
Data.Value.Span[arrayIdx] = new();
}

public readonly void Visit((int arrayIdx, int subIdx) at, float score)
{
Data[at.arrayIdx].ScoreSum.Span[at.subIdx] += score;
Data[at.arrayIdx].MaxScore.Span[at.subIdx] = Math.Max(Data[at.arrayIdx].MaxScore.Span[at.subIdx], score);
Data[at.arrayIdx].Visits.Span[at.subIdx]++;
ref var batch = ref Data!.Value.Span[at.arrayIdx];
batch.ScoreSum[at.subIdx] += score;
batch.MaxScore[at.subIdx] = Math.Max(batch.MaxScore[at.subIdx], score);
batch.Visits[at.subIdx]++;
}

public readonly int GetVisits((int arrayIdx, int subIdx) at) =>
Data[at.arrayIdx].Visits.Span[at.subIdx];
Data!.Value.Span[at.arrayIdx].Visits[at.subIdx];

[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
2 changes: 1 addition & 1 deletion Solver/SimulationNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace Craftimizer.Solver;

[StructLayout(LayoutKind.Auto)]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SimulationNode
{
public readonly SimulationState State;
Expand Down

1 comment on commit adf3a80

@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: adf3a80 Previous: a8c3f34 Ratio
Craftimizer.Benchmark.Bench.Solve(State: 5372D31C98FA4C357F54029912394B0F5ECBE94AEC9D12C1C2B7F453C62ACD2F, Config: SolverConfig { Iterations = 30000, ScoreStorageThreshold = 1, MaxScoreWeightingConstant = 0.1, ExplorationConstant = 4, MaxStepCount = 30, MaxRolloutStepCount = 99, MaxThreadCount = 1, ForkCount = 32, FurcatedActionCount = 16, StrictActions = True, ScoreProgress = 0.2, ScoreQuality = 0.65, ScoreDurability = 0.05, ScoreCP = 0.05, ScoreSteps = 0.05, Algorithm = Stepwise }) 2352608346.6666665 ns (± 41517326.550040126)
Craftimizer.Benchmark.Bench.Solve(State: 5372D31C98FA4C357F54029912394B0F5ECBE94AEC9D12C1C2B7F453C62ACD2F, Config: SolverConfig { Iterations = 30000, ScoreStorageThreshold = 1, MaxScoreWeightingConstant = 0.1, ExplorationConstant = 4, MaxStepCount = 30, MaxRolloutStepCount = 99, MaxThreadCount = 1, ForkCount = 32, FurcatedActionCount = 16, StrictActions = True, ScoreProgress = 0.2, ScoreQuality = 0.65, ScoreDurability = 0.05, ScoreCP = 0.05, ScoreSteps = 0.05, Algorithm = Stepwise }) 1583858640 ns (± 20855157.07988931)
Craftimizer.Benchmark.Bench.Solve(State: 99B0F1AD46A18B4D8262F9BA75ABE23507217C2F20FBF895A49282DDFEF50190, Config: SolverConfig { Iterations = 30000, ScoreStorageThreshold = 1, MaxScoreWeightingConstant = 0.1, ExplorationConstant = 4, MaxStepCount = 30, MaxRolloutStepCount = 99, MaxThreadCount = 1, ForkCount = 32, FurcatedActionCount = 16, StrictActions = True, ScoreProgress = 0.2, ScoreQuality = 0.65, ScoreDurability = 0.05, ScoreCP = 0.05, ScoreSteps = 0.05, Algorithm = Stepwise }) 2527160726.6666665 ns (± 41474619.06978199)
Craftimizer.Benchmark.Bench.Solve(State: 99B0F1AD46A18B4D8262F9BA75ABE23507217C2F20FBF895A49282DDFEF50190, Config: SolverConfig { Iterations = 30000, ScoreStorageThreshold = 1, MaxScoreWeightingConstant = 0.1, ExplorationConstant = 4, MaxStepCount = 30, MaxRolloutStepCount = 99, MaxThreadCount = 1, ForkCount = 32, FurcatedActionCount = 16, StrictActions = True, ScoreProgress = 0.2, ScoreQuality = 0.65, ScoreDurability = 0.05, ScoreCP = 0.05, ScoreSteps = 0.05, Algorithm = Stepwise }) 1751483944.4444444 ns (± 35846049.30165572)

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

Please sign in to comment.