Fix concurrency deadlock

I'm stupid and forgot it divided the number of iterations by 8, no wonder it was so fast lmao
This commit is contained in:
Asriel Camora
2023-07-07 13:01:17 +02:00
parent d5a8288439
commit 1386f9150c
7 changed files with 71 additions and 71 deletions
+3 -5
View File
@@ -41,15 +41,13 @@ internal static class Program
QualityDivider = 115, QualityDivider = 115,
ProgressModifier = 80, ProgressModifier = 80,
ProgressDivider = 130, ProgressDivider = 130,
}, }
0
); );
var threads = 8;
var config = new SolverConfig() var config = new SolverConfig()
{ {
Iterations = 30_000 / threads, Iterations = 1_000_000,
ThreadCount = threads, ThreadCount = 8,
}; };
Debugger.Break(); Debugger.Break();
+1 -2
View File
@@ -71,7 +71,7 @@ public struct ActionSet
public readonly bool IsEmpty => bits == 0; public readonly bool IsEmpty => bits == 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly ActionType SelectRandom(Random random) => ElementAt(0);// random.Next(Count)); public readonly ActionType SelectRandom(Random random) => ElementAt(random.Next(Count));
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ActionType? PopRandomConcurrent(Random random) public ActionType? PopRandomConcurrent(Random random)
@@ -117,7 +117,6 @@ public struct ActionSet
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ActionType PopRandom(Random random) public ActionType PopRandom(Random random)
{ {
return PopFirst();
var action = ElementAt(random.Next(Count)); var action = ElementAt(random.Next(Count));
RemoveAction(action); RemoveAction(action);
return action; return action;
+60
View File
@@ -0,0 +1,60 @@
using System.Diagnostics.Contracts;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Craftimizer.Solver.Crafty;
// Adapted from https://github.com/dtao/ConcurrentList/blob/4fcf1c76e93021a41af5abb2d61a63caeba2adad/ConcurrentList/ConcurrentList.cs
public struct ArenaBuffer<T>
{
// Technically 25, but it's very unlikely to actually get to there.
// The benchmark reaches 20 at most, but here we have a little leeway just in case.
private const int MaxSize = 24;
private static int BatchSize = Vector<float>.Count;
private static int BatchSizeBits = int.Log2(BatchSize);
private static int BatchSizeMask = BatchSize - 1;
private static int BatchCount = MaxSize / BatchSize;
public T[][] Data;
private int index; // Unused in single threaded workload
private int count;
public readonly int Count => count;
public void AddConcurrent(T node)
{
if (Data == null)
Interlocked.CompareExchange(ref Data, new T[BatchCount][], null);
var idx = Interlocked.Increment(ref index) - 1;
var (arrayIdx, subIdx) = GetArrayIndex(idx);
if (Data[arrayIdx] == null)
Interlocked.CompareExchange(ref Data[arrayIdx], new T[BatchSize], null);
Data[arrayIdx][subIdx] = node;
Interlocked.Increment(ref count);
}
public void Add(T node)
{
Data ??= new T[BatchCount][];
var idx = count++;
var (arrayIdx, subIdx) = GetArrayIndex(idx);
Data[arrayIdx] ??= new T[BatchSize];
Data[arrayIdx][subIdx] = node;
}
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static (int arrayIdx, int subIdx) GetArrayIndex(int idx) =>
(idx >> BatchSizeBits, idx & BatchSizeMask);
}
+1 -58
View File
@@ -1,68 +1,11 @@
using System.Diagnostics.Contracts;
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Craftimizer.Solver.Crafty; namespace Craftimizer.Solver.Crafty;
public sealed class ArenaNode<T> where T : struct public sealed class ArenaNode<T> where T : struct
{ {
// Adapted from https://github.com/dtao/ConcurrentList/blob/4fcf1c76e93021a41af5abb2d61a63caeba2adad/ConcurrentList/ConcurrentList.cs
public struct ChildBuffer
{
// Technically 25, but it's very unlikely to actually get to there.
// The benchmark reaches 20 at most, but here we have a little leeway just in case.
private const int MaxSize = 24;
private static int BatchSize = Vector<float>.Count;
private static int BatchSizeBits = int.Log2(BatchSize);
private static int BatchSizeMask = BatchSize - 1;
private static int BatchCount = MaxSize / BatchSize;
public ArenaNode<T>[][] Data;
private int index; // Unused in single threaded workload
private int count;
public readonly int Count => count;
public void AddConcurrent(ArenaNode<T> node)
{
if (Data == null)
Interlocked.CompareExchange(ref Data, new ArenaNode<T>[BatchCount][], null);
var idx = Interlocked.Increment(ref index) - 1;
var (arrayIdx, subIdx) = GetArrayIndex(idx);
if (Data[arrayIdx] == null)
Interlocked.CompareExchange(ref Data[arrayIdx], new ArenaNode<T>[BatchSize], null);
Data[arrayIdx][subIdx] = node;
Interlocked.Increment(ref count);
}
public void Add(ArenaNode<T> node)
{
Data ??= new ArenaNode<T>[BatchCount][];
var idx = count++;
var (arrayIdx, subIdx) = GetArrayIndex(idx);
Data[arrayIdx] ??= new ArenaNode<T>[BatchSize];
Data[arrayIdx][subIdx] = node;
}
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static (int arrayIdx, int subIdx) GetArrayIndex(int idx) =>
(idx >> BatchSizeBits, idx & BatchSizeMask);
}
public T State; public T State;
public ChildBuffer Children; public ArenaBuffer<ArenaNode<T>> Children;
public readonly ArenaNode<T>? Parent; public readonly ArenaNode<T>? Parent;
public ArenaNode(T state, ArenaNode<T>? parent = null) public ArenaNode(T state, ArenaNode<T>? parent = null)
+2 -2
View File
@@ -53,7 +53,7 @@ public sealed class SolverConcurrent : ISolver
var poppedAction = initialState.AvailableActions.PopRandomConcurrent(random); var poppedAction = initialState.AvailableActions.PopRandomConcurrent(random);
if (!poppedAction.HasValue) if (!poppedAction.HasValue)
return null; return null;
var expandedNode = initialNode.Add(SolverUtils.Execute(simulator, initialState.State, poppedAction.Value, true)); var expandedNode = initialNode.AddConcurrent(SolverUtils.Execute(simulator, initialState.State, poppedAction.Value, true));
return SolverUtils.Rollout(ref config, rootNode, expandedNode, random, simulator); return SolverUtils.Rollout(ref config, rootNode, expandedNode, random, simulator);
} }
@@ -84,7 +84,7 @@ public sealed class SolverConcurrent : ISolver
} }
public static void SearchThread(SolverConfig config, Node rootNode, CancellationToken token) => public static void SearchThread(SolverConfig config, Node rootNode, CancellationToken token) =>
SolverUtils.Search<SolverConcurrent>(ref config, rootNode, token); SolverUtils.Search<SolverConcurrent>(ref config, config.Iterations / config.ThreadCount, rootNode, token);
public static void Search(ref SolverConfig config, Node rootNode, CancellationToken token) public static void Search(ref SolverConfig config, Node rootNode, CancellationToken token)
{ {
+1 -1
View File
@@ -75,5 +75,5 @@ public sealed class SolverSingle : ISolver
} }
public static void Search(ref SolverConfig config, Node rootNode, CancellationToken token) => public static void Search(ref SolverConfig config, Node rootNode, CancellationToken token) =>
SolverUtils.Search<SolverSingle>(ref config, rootNode, token); SolverUtils.Search<SolverSingle>(ref config, config.Iterations, rootNode, token);
} }
+3 -3
View File
@@ -29,7 +29,7 @@ public static class SolverUtils
if (!state.AvailableActions.HasAction(action)) if (!state.AvailableActions.HasAction(action))
return (startNode, CompletionState.InvalidAction); return (startNode, CompletionState.InvalidAction);
state.AvailableActions.RemoveActionConcurrent(action); state.AvailableActions.RemoveAction(action);
startNode = startNode.Add(Execute(simulator, state.State, action, strict)); startNode = startNode.Add(Execute(simulator, state.State, action, strict));
} }
@@ -167,11 +167,11 @@ public static class SolverUtils
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Search<S>(ref SolverConfig config, Node rootNode, CancellationToken token) where S : ISolver public static void Search<S>(ref SolverConfig config, int iterations, Node rootNode, CancellationToken token) where S : ISolver
{ {
Simulator simulator = new(rootNode.State.State, config.MaxStepCount); Simulator simulator = new(rootNode.State.State, config.MaxStepCount);
var random = rootNode.State.State.Input.Random; var random = rootNode.State.State.Input.Random;
for (var i = 0; i < config.Iterations; i++) for (var i = 0; i < iterations; i++)
{ {
if (token.IsCancellationRequested) if (token.IsCancellationRequested)
break; break;