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:
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,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)
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user