Some multithreaded fixes, deadlocks due to List reads during adds

This commit is contained in:
Asriel Camora
2023-07-04 17:19:55 +02:00
parent 75306ca020
commit 4d96fd173f
8 changed files with 90 additions and 29 deletions
+1 -1
View File
@@ -9,7 +9,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.5" /> <PackageReference Include="BenchmarkDotNet" Version="0.13.5" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.61"> <PackageReference Include="Meziantou.Analyzer" Version="2.0.62">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
+3 -2
View File
@@ -45,10 +45,11 @@ internal static class Program
0 0
); );
var threads = 1;
var config = new SolverConfig() var config = new SolverConfig()
{ {
Iterations = 30_000 / 1, Iterations = 30_000 / threads,
ThreadCount = 1, ThreadCount = threads,
}; };
Debugger.Break(); Debugger.Break();
+1 -1
View File
@@ -28,7 +28,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="DalamudPackager" Version="2.1.11" /> <PackageReference Include="DalamudPackager" Version="2.1.11" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.61"> <PackageReference Include="Meziantou.Analyzer" Version="2.0.62">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
+3 -3
View File
@@ -10,9 +10,9 @@
}, },
"Meziantou.Analyzer": { "Meziantou.Analyzer": {
"type": "Direct", "type": "Direct",
"requested": "[2.0.61, )", "requested": "[2.0.62, )",
"resolved": "2.0.61", "resolved": "2.0.62",
"contentHash": "DhiEScqTxQb8R7s9EWMjs5F5EA7AD+JO5upb88QqPwPQUsAOm2gN5AjeQon3XLrquw1G5r+C9Yxct+rFxwuMZg==" "contentHash": "uG2CiDIm97q8KrUt8B34WdElpEDDLOe4YzrLWpwlQmesXrSX2WuJZ+HwIGWrJgDBBMi2a3tVjeF8oKjV+AhUdA=="
}, },
"craftimizer.simulator": { "craftimizer.simulator": {
"type": "Project" "type": "Project"
+1 -1
View File
@@ -7,7 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Meziantou.Analyzer" Version="2.0.61"> <PackageReference Include="Meziantou.Analyzer" Version="2.0.62">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
+1 -1
View File
@@ -8,7 +8,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Meziantou.Analyzer" Version="2.0.61"> <PackageReference Include="Meziantou.Analyzer" Version="2.0.62">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
+49
View File
@@ -1,4 +1,5 @@
using Craftimizer.Simulator.Actions; using Craftimizer.Simulator.Actions;
using System;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -54,6 +55,54 @@ public struct ActionSet
//public readonly ActionType SelectRandom(Random random) => ElementAt(random.Next(Count)); //public readonly ActionType SelectRandom(Random random) => ElementAt(random.Next(Count));
public readonly ActionType SelectRandom(Random random) => First(); public readonly ActionType SelectRandom(Random random) => First();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ActionType? PopRandom(Random random) => PopFirst();
/*public ActionType? PopRandom(Random random)
{
uint snapshot;
uint newValue;
ActionType action;
do
{
snapshot = bits;
if (snapshot == 0)
return null;
var count = BitOperations.PopCount(snapshot);
var index = random.Next(count);
action = ToAction(Intrinsics.NthBitSet(snapshot, index) - 1);
newValue = snapshot & ~ToMask(action);
}
while (Interlocked.CompareExchange(ref bits, newValue, snapshot) != snapshot);
return action;
}*/
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ActionType? PopFirst()
{
uint snapshot;
uint newValue;
ActionType action;
int i = 0;
do
{
++i;
snapshot = bits;
if (snapshot == 0)
return null;
var index = 0;
action = ToAction(Intrinsics.NthBitSet(snapshot, index) - 1);
newValue = snapshot & ~ToMask(action);
}
while (Interlocked.CompareExchange(ref bits, newValue, snapshot) != snapshot);
//if (i != 1)
//Console.WriteLine($"Retried {i-1} times");
return action;
}
[Pure] [Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly ActionType First() => ElementAt(0); public readonly ActionType First() => ElementAt(0);
+31 -20
View File
@@ -19,7 +19,7 @@ public class Solver
public Solver(SolverConfig config, SimulationState state, bool strict) public Solver(SolverConfig config, SimulationState state, bool strict)
{ {
Config = config; Config = config;
Simulator sim = new(state, config.MaxStepCount); var sim = new Simulator(state, config.MaxStepCount);
RootNode = new(new( RootNode = new(new(
state, state,
null, null,
@@ -98,7 +98,12 @@ public class Solver
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private Node EvalBestChild(float parentVisits, ReadOnlySpan<Node> children) private Node EvalBestChild(float parentVisits, ReadOnlySpan<Node> children)
{ {
var length = children.Length; if (parentVisits == 0)
{
Console.WriteLine("no visits");
return null;
}
var vecLength = Vector<float>.Count; var vecLength = Vector<float>.Count;
var C = MathF.Sqrt(Config.ExplorationConstant * MathF.Log(parentVisits)); var C = MathF.Sqrt(Config.ExplorationConstant * MathF.Log(parentVisits));
@@ -152,30 +157,28 @@ public class Solver
var node = RootNode; var node = RootNode;
while (true) while (true)
{ {
if (!Monitor.TryEnter(node, 5)) var expandable = !node.State.AvailableActions.IsEmpty;
return Select();
var expandable = node.State.AvailableActions.Count != 0;
var likelyTerminal = node.Children.Count == 0; var likelyTerminal = node.Children.Count == 0;
if (expandable || likelyTerminal) if (expandable || likelyTerminal)
return node; return node;
// select the node with the highest score // select the node with the highest score
var n = EvalBestChild(node.State.Scores.Visits, CollectionsMarshal.AsSpan(node.Children)); // if null (current node is invalid & not backpropagated just yet), try again from root
Monitor.Exit(node); node = EvalBestChild(node.State.Scores.Visits, node.Children) ?? RootNode;
node = n;
} }
} }
public (Node ExpandedNode, CompletionState State, float Score) ExpandAndRollout(Simulator simulator, Node initialNode) public (Node ExpandedNode, float Score)? ExpandAndRollout(Simulator simulator, Node initialNode)
{ {
ref var initialState = ref initialNode.State; ref var initialState = ref initialNode.State;
// expand once // expand once
if (initialState.IsComplete) if (initialState.IsComplete)
return (initialNode, initialState.CompletionState, initialState.CalculateScore(Config.MaxStepCount) ?? 0); return (initialNode, initialState.CalculateScore(Config.MaxStepCount) ?? 0);
var randomAction = initialState.AvailableActions.SelectRandom(Random); var poppedAction = initialState.AvailableActions.PopRandom(Random);
initialState.AvailableActions.RemoveAction(randomAction); if (!poppedAction.HasValue)
var expandedNode = initialNode.Add(Execute(simulator, initialState.State, randomAction, true)); return null;
var expandedNode = initialNode.Add(Execute(simulator, initialState.State, poppedAction.Value, true));
// playout to a terminal state // playout to a terminal state
var currentState = expandedNode.State.State; var currentState = expandedNode.State.State;
@@ -188,9 +191,9 @@ public class Solver
{ {
if (SimulationNode.GetCompletionState(currentCompletionState, currentActions) != CompletionState.Incomplete) if (SimulationNode.GetCompletionState(currentCompletionState, currentActions) != CompletionState.Incomplete)
break; break;
randomAction = currentActions.SelectRandom(Random); var nextAction = currentActions.SelectRandom(Random);
actions[actionCount++] = randomAction; actions[actionCount++] = nextAction;
(_, currentState) = simulator.Execute(currentState, randomAction); (_, currentState) = simulator.Execute(currentState, nextAction);
currentCompletionState = simulator.CompletionState; currentCompletionState = simulator.CompletionState;
currentActions = simulator.AvailableActionsHeuristic(true); currentActions = simulator.AvailableActionsHeuristic(true);
} }
@@ -202,10 +205,10 @@ public class Solver
if (score >= Config.ScoreStorageThreshold && score >= RootNode.State.Scores.MaxScore) if (score >= Config.ScoreStorageThreshold && score >= RootNode.State.Scores.MaxScore)
{ {
(var terminalNode, _) = ExecuteActions(simulator, expandedNode, actions[..actionCount], true); (var terminalNode, _) = ExecuteActions(simulator, expandedNode, actions[..actionCount], true);
return (terminalNode, currentCompletionState, score); return (terminalNode, score);
} }
} }
return (expandedNode, currentCompletionState, score); return (expandedNode, score);
} }
public void Backpropagate(Node startNode, float score) public void Backpropagate(Node startNode, float score)
@@ -230,9 +233,17 @@ public class Solver
break; break;
var selectedNode = Select(); var selectedNode = Select();
var (endNode, _, score) = ExpandAndRollout(simulator, selectedNode); var rolledOut = ExpandAndRollout(simulator, selectedNode);
Monitor.Exit(selectedNode); //Monitor.Exit(selectedNode);
if (!rolledOut.HasValue)
{
Console.WriteLine("Retry");
// Retry, count this iteration as moot
i--;
continue;
}
var (endNode, score) = rolledOut.Value;
Backpropagate(endNode, score); Backpropagate(endNode, score);
} }
} }