working on dynamic wrapping
This commit is contained in:
@@ -49,8 +49,8 @@ namespace StgSharp.Mathematics.Numeric
|
||||
long count = (long)left.KernelColumnLength * left.KernelRowLength;
|
||||
if (count <= 1024)
|
||||
{
|
||||
CapacityFixedStack<MatrixParallelThread> pool = MatrixParallel.LeadParallel();
|
||||
if (!pool.TryPop(out MatrixParallelThread thread))
|
||||
ReadOnlySpan<MatrixParallelThread> pool = MatrixParallel.LeadParallel(1);
|
||||
if (pool.Length == 0)
|
||||
{
|
||||
/*
|
||||
* wait
|
||||
@@ -84,8 +84,8 @@ namespace StgSharp.Mathematics.Numeric
|
||||
if (count <= 1024)
|
||||
{
|
||||
// TODO: this is very awkward, blocking thread renting, need a better way to handle small tasks
|
||||
CapacityFixedStack<MatrixParallelThread> pool = MatrixParallel.LeadParallel();
|
||||
if (!pool.TryPop(out MatrixParallelThread thread))
|
||||
ReadOnlySpan<MatrixParallelThread> pool = MatrixParallel.LeadParallel(1);
|
||||
if (pool.Length == 0)
|
||||
{
|
||||
/*
|
||||
* wait
|
||||
|
||||
@@ -820,9 +820,15 @@ namespace StgSharp.Mathematics.Numeric
|
||||
int tileWidth = (1024 / secCount) + 1;
|
||||
int tileCount = (primCount + tileWidth - 1) / tileWidth;
|
||||
|
||||
CapacityFixedStack<MatrixParallelThread> pool = LeadParallel();
|
||||
ReadOnlySpan<MatrixParallelThread> pool = LeadParallel(tileCount);
|
||||
|
||||
int threadCount = pool.Count;
|
||||
int threadCount = pool.Length;
|
||||
if (pool.Length == 0)
|
||||
{
|
||||
/*
|
||||
* wait until has enough threads
|
||||
*/
|
||||
}
|
||||
int tilePerThreadLess = tileCount / threadCount; // tiles per thread without extra slice
|
||||
int columnAfterBaseTile = tileCount % threadCount * tileWidth; // tile sliced into columns
|
||||
int extraColumnPerThread = columnAfterBaseTile / threadCount;
|
||||
@@ -831,17 +837,8 @@ namespace StgSharp.Mathematics.Numeric
|
||||
int total = primCount;
|
||||
Stack<MatrixParallelWrap> taskBunch = new Stack<MatrixParallelWrap>(threadCount);
|
||||
MatrixParallelTaskPackage<T>* current = package;
|
||||
while (true)
|
||||
foreach (MatrixParallelThread thread in pool)
|
||||
{
|
||||
if (!pool.TryPop(out MatrixParallelThread? wrap) || wrap is null)
|
||||
{
|
||||
#if DEBUG
|
||||
throw new InvalidOperationException("Someone steal parallel wraps from the pool.");
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool hasSlice = columnAfterBaseTile > 0;
|
||||
bool isLargeSlice = extraColumnCount > 0;
|
||||
|
||||
@@ -872,7 +869,7 @@ namespace StgSharp.Mathematics.Numeric
|
||||
total -= columnInWrap;
|
||||
|
||||
// set to wrap and push
|
||||
wrap.Tasks.Enqueue((IntPtr)current);
|
||||
// wrap.Tasks.Enqueue((IntPtr)current);
|
||||
|
||||
// taskBunch.Push(wrap);
|
||||
|
||||
@@ -884,7 +881,6 @@ namespace StgSharp.Mathematics.Numeric
|
||||
current = MatrixParallelFactory.FromExistPackage(current);
|
||||
current->PrimTileOffset += columnInWrap;
|
||||
}
|
||||
ReleaseParallelLeadership(ref pool);
|
||||
|
||||
return taskBunch;
|
||||
}
|
||||
|
||||
@@ -34,45 +34,12 @@ using System.Threading;
|
||||
|
||||
namespace StgSharp.Mathematics.Numeric
|
||||
{
|
||||
/*
|
||||
* Thread wrapping to accelerate matrix operations by dividing threads into groups (pool).
|
||||
* For M pool, each containing an average of N threads, we satisfy the condition M * N = T - 4,
|
||||
* where T is the total thread count of the CPU. Typically, for modern consumer CPUs, T is around 16.
|
||||
* For simplicity, let M * N = 16.
|
||||
*
|
||||
* The total time complexity for publishing tasks is O(M + N):
|
||||
* - O(M) accounts for distributing tasks across M pool.
|
||||
* - O(N) accounts for threads within each wrap fetching tasks from the wrap queue.
|
||||
*
|
||||
* Using the inequality Sqrt(M * N) <= (M + N) / 2 (derived from the arithmetic-geometric mean inequality),
|
||||
* the optimal configuration occurs when M = N. For M * N = 16, this gives M = N = 4.
|
||||
*
|
||||
* To ensure compatibility across a wide range of CPU architectures,
|
||||
* we allow each wrap to contain either 3 or 4 threads, providing flexibility while maintaining efficiency.
|
||||
*/
|
||||
|
||||
public static partial class MatrixParallel
|
||||
{
|
||||
|
||||
private static readonly (int a, int b)[] LookupTable = [
|
||||
(1, 1), // 7
|
||||
(0, 2), // 8 (no non-zero solution)
|
||||
(3, 0), // 9 (no non-zero solution)
|
||||
(2, 1), // 10
|
||||
(1, 2), // 11
|
||||
(0, 3), // 12 (no non-zero solution)
|
||||
(3, 1), // 13
|
||||
(2, 2), // 14
|
||||
(1, 3), // 15
|
||||
(4, 1), // 16
|
||||
(3, 2), // 17
|
||||
(2, 3), // 18
|
||||
(1, 4), // 19
|
||||
(4, 2), // 20
|
||||
];
|
||||
private static bool _meaningful;
|
||||
|
||||
private static CapacityFixedStack<MatrixParallelThread> _threads;
|
||||
private static ConcurrentCapacityFixedStack<MatrixParallelThread> _threads;
|
||||
|
||||
private static volatile int RemainThreadCount = 0;
|
||||
private static readonly object _lock = new();
|
||||
@@ -91,22 +58,6 @@ namespace StgSharp.Mathematics.Numeric
|
||||
Monitor.Exit(_lock);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static (int count_3, int count_4) FastDecompose(int c)
|
||||
{
|
||||
if (c < 7) {
|
||||
return (0, 0);
|
||||
}
|
||||
|
||||
int t = c - 7;
|
||||
int k = t / 14;
|
||||
int d = t % 14;
|
||||
|
||||
(int a0, int b0) = LookupTable[d];
|
||||
|
||||
return (a0 + (2 * k), b0 + (2 * k));
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
if (IsInitialized) {
|
||||
@@ -144,17 +95,17 @@ namespace StgSharp.Mathematics.Numeric
|
||||
|
||||
#region wrap field
|
||||
|
||||
public static CapacityFixedStack<MatrixParallelThread> LeadParallel()
|
||||
public static MatrixParallelThread[] LeadParallel(int recommendCount)
|
||||
{
|
||||
#if NET9_0_OR_GREATER
|
||||
#else
|
||||
Monitor.Enter(_lock);
|
||||
#endif
|
||||
|
||||
return _threads;
|
||||
return _threads.PopRange(recommendCount);
|
||||
}
|
||||
|
||||
public static void ReleaseParallelLeadership(ref CapacityFixedStack<MatrixParallelThread> pool)
|
||||
public static void ReleaseParallelLeadership(ref MatrixParallelThread[] pool)
|
||||
{
|
||||
#if NET9_0_OR_GREATER
|
||||
#else
|
||||
|
||||
@@ -36,79 +36,115 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Mathematics.Numeric
|
||||
{
|
||||
public class MatrixParallelPool
|
||||
public struct MatrixParallelHandle : IDisposable
|
||||
{
|
||||
|
||||
private MatrixParallelPool()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
/*
|
||||
internal MatrixParallelPool(int threadCount, ReadOnlySpan<MatrixParallelWrap> wrapSpan)
|
||||
{
|
||||
SpareWrap = CapacityFixedStackBuilder.Create(wrapSpan);
|
||||
SingleThreadAllocation = new CapacityFixedBag<MatrixParallelWrap>(wrapSpan.Length);
|
||||
SpareThreadCount = threadCount;
|
||||
}
|
||||
/**/
|
||||
private static readonly (int a, int b)[] LookupTable = [
|
||||
(1, 1), // 7
|
||||
(0, 2), // 8 (no non-zero solution)
|
||||
(3, 0), // 9 (no non-zero solution)
|
||||
(2, 1), // 10
|
||||
(1, 2), // 11
|
||||
(0, 3), // 12 (no non-zero solution)
|
||||
(3, 1), // 13
|
||||
(2, 2), // 14
|
||||
(1, 3), // 15
|
||||
(4, 1), // 16
|
||||
(3, 2), // 17
|
||||
(2, 3), // 18
|
||||
(1, 4), // 19
|
||||
(4, 2), // 20
|
||||
];
|
||||
|
||||
internal MatrixParallelPool(int threadCount, CapacityFixedStack<MatrixParallelWrap> wrapSpan)
|
||||
private MatrixParallelThread[] _threads;
|
||||
private bool _disposed = false;
|
||||
private CapacityFixedStack<MatrixParallelWrap> _wraps;
|
||||
|
||||
internal MatrixParallelHandle(MatrixParallelThread[] threadArray)
|
||||
{
|
||||
SpareWrap = wrapSpan;
|
||||
SingleThreadAllocation = new CapacityFixedBag<MatrixParallelWrap>(wrapSpan.Count);
|
||||
SpareThreadCount = threadCount;
|
||||
_threads = threadArray;
|
||||
IsMultithread = threadArray.Length > 1;
|
||||
SliceToWraps();
|
||||
}
|
||||
|
||||
public int SpareThreadCount { get; set; }
|
||||
|
||||
internal CapacityFixedBag<MatrixParallelWrap> SingleThreadAllocation { get; set; }
|
||||
|
||||
internal CapacityFixedStack<MatrixParallelWrap> SpareWrap { get; set; }
|
||||
|
||||
/*
|
||||
public void ReturnIndividualHandle(ref MatrixParallelWrap.Handle handle)
|
||||
public ReadOnlySpan<MatrixParallelThread> Threads
|
||||
{
|
||||
MatrixParallelWrap wrap = handle.Wrap;
|
||||
handle.Role = 0;
|
||||
if (wrap.IsAllAvailable)
|
||||
get
|
||||
{
|
||||
SingleThreadAllocation.RemoveAt(wrap.Magic);
|
||||
SpareWrap.Push(wrap);
|
||||
SpareThreadCount += wrap.WrapTaskCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryRentIndividualHandle(out MatrixParallelWrap.Handle handle)
|
||||
{
|
||||
foreach (MatrixParallelWrap item in SingleThreadAllocation)
|
||||
{
|
||||
if (item.TryGetLeaderHandle(out handle!)) {
|
||||
return true;
|
||||
if (_disposed) {
|
||||
throw new ObjectDisposedException(typeof(MatrixParallelHandle).Name);
|
||||
}
|
||||
return _threads;
|
||||
}
|
||||
if (TryRequestWrap(out MatrixParallelWrap wrap))
|
||||
{
|
||||
wrap!.Magic = SingleThreadAllocation.Count;
|
||||
SingleThreadAllocation.Add(wrap);
|
||||
if (wrap.TryGetLeaderHandle(out handle!)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal ReadOnlySpan<MatrixParallelThread> ThreadsUnsafe
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _threads;
|
||||
}
|
||||
|
||||
private bool IsMultithread { get; init; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) {
|
||||
return;
|
||||
}
|
||||
handle = null!;
|
||||
return false;
|
||||
MatrixParallel.ReleaseParallelLeadership(ref _threads);
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryRequestWrap([NotNull] out MatrixParallelWrap? wrap)
|
||||
private static (int count_3, int count_4) FastDecompose(int c)
|
||||
{
|
||||
if (SpareWrap.TryPop(out wrap))
|
||||
/*
|
||||
* Thread wrapping to accelerate matrix operations by dividing threads into groups (pool).
|
||||
* For M pool, each containing an average of N threads, we satisfy the condition M * N = T - 4,
|
||||
* where T is the total thread count of the CPU. Typically, for modern consumer CPUs, T is around 16.
|
||||
* For simplicity, let M * N = 16.
|
||||
*
|
||||
* The total time complexity for publishing tasks is O(M + N):
|
||||
* - O(M) accounts for distributing tasks across M pool.
|
||||
* - O(N) accounts for threads within each wrap fetching tasks from the wrap queue.
|
||||
*
|
||||
* Using the inequality Sqrt(M * N) <= (M + N) / 2 (derived from the arithmetic-geometric mean inequality),
|
||||
* the optimal configuration occurs when M = N. For M * N = 16, this gives M = N = 4.
|
||||
*
|
||||
* To ensure compatibility across a wide range of CPU architectures,
|
||||
* we allow each wrap to contain either 3 or 4 threads, providing flexibility while maintaining efficiency.
|
||||
*/
|
||||
if (c < 7)
|
||||
{
|
||||
SpareThreadCount -= wrap.WrapTaskCapacity;
|
||||
return true;
|
||||
return (0, 0);
|
||||
}
|
||||
wrap = null!;
|
||||
return false;
|
||||
|
||||
int t = c - 7;
|
||||
int k = t / 14;
|
||||
int d = t % 14;
|
||||
|
||||
(int a0, int b0) = LookupTable[d];
|
||||
|
||||
return (a0 + (2 * k), b0 + (2 * k));
|
||||
}
|
||||
/**/
|
||||
|
||||
private void SliceToWraps()
|
||||
{
|
||||
if (_disposed) {
|
||||
throw new ObjectDisposedException(typeof(MatrixParallelHandle).Name);
|
||||
}
|
||||
if (_wraps is not null) {
|
||||
return;
|
||||
}
|
||||
int count = _threads.Length;
|
||||
if (count < 6)
|
||||
{
|
||||
MatrixParallelWrap wrap = new MatrixParallelWrap(_threads);
|
||||
_wraps = [ wrap ];
|
||||
}
|
||||
(int c3,int c4) = FastDecompose(_threads.Length);
|
||||
_wraps = new(c3 + c4);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,13 @@ namespace StgSharp.Mathematics.Numeric
|
||||
{
|
||||
|
||||
private readonly MatrixParallelThread[] _threads;
|
||||
private int _begin;
|
||||
|
||||
internal MatrixParallelWrap(MatrixParallelThread[] threads)
|
||||
{
|
||||
_threads = threads;
|
||||
Count = threads.Length;
|
||||
}
|
||||
|
||||
public MatrixParallelWrap()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user