From ea0e6f3ff66ac2b8f052d20def70a56ff2ccffd7 Mon Sep 17 00:00:00 2001
From: Youmu Liu <105042599+Nitload-NSI@users.noreply.github.com>
Date: Wed, 5 Nov 2025 20:33:58 +0800
Subject: [PATCH 1/3] optimize para performance of buffered stack
---
.../ConcurrentCapacityFixedStack.cs | 287 ++++++++++++++++++
StgSharp.Common/Controlling/KeyEnum.cs | 32 +-
.../Memory/ConcurrentBufferStack.cs | 124 ++++----
StgSharp.Common/Internal/Graphic/gl.cs | 2 +-
.../Threading/BufferExpansionLock.cs | 18 +-
.../Numeric/MatrixCompute.Basic.cs | 3 +
.../Numeric/MatrixParallel.Publish.cs | 13 +-
.../Numeric/MatrixParallelPool.cs | 3 +-
.../Numeric/MatrixParallelThread.cs | 5 +
StgSharpDebug/Program.cs | 120 +++++++-
10 files changed, 516 insertions(+), 91 deletions(-)
create mode 100644 StgSharp.Common/Collections/ConcurrentCapacityFixedStack.cs
diff --git a/StgSharp.Common/Collections/ConcurrentCapacityFixedStack.cs b/StgSharp.Common/Collections/ConcurrentCapacityFixedStack.cs
new file mode 100644
index 00000000..69dadc9e
--- /dev/null
+++ b/StgSharp.Common/Collections/ConcurrentCapacityFixedStack.cs
@@ -0,0 +1,287 @@
+//-----------------------------------------------------------------------
+// -----------------------------------------------------------------------
+// file="ConcurrentCapacityFixedStack"
+// Project: StgSharp
+// AuthorGroup: Nitload
+// Copyright (c) Nitload. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+// -----------------------------------------------------------------------
+// -----------------------------------------------------------------------
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Threading;
+
+namespace StgSharp.Collections
+{
+ ///
+ /// Lock-free, fixed-capacity stack implemented on top of an array using atomic index
+ /// operations. - No linked list; only array slots - Uses CAS on a shared top index to
+ /// reserve/release slots - Per-slot state flag to safely publish/consume values under races
+ ///
+ public class ConcurrentCapacityFixedStack : IEnumerable
+ {
+
+ private readonly int[] _states; // 0 = empty, 1 = full
+ private readonly T[] _values;
+ private readonly int _capacity;
+
+ // Number of full slots (observable count)
+ private int _count;
+
+ // Next push position (0.._capacity). Only modified via CAS.
+ private int _top;
+
+ public ConcurrentCapacityFixedStack(int capacity)
+ {
+ if (capacity <= 0) {
+ throw new ArgumentOutOfRangeException(nameof(capacity));
+ }
+
+ _capacity = capacity;
+ _values = new T[capacity];
+ _states = new int[capacity];
+ _top = 0;
+ _count = 0;
+ }
+
+ public bool IsEmpty => Count == 0;
+
+ public int Capacity => _capacity;
+
+ public int Count => Volatile.Read(ref _count);
+
+ public void Clear()
+ {
+ // Pop all elements to return slots to empty state
+ while (TryPop(out _)) { }
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ T[] snap = ToArraySnapshot();
+ for (int i = 0; i < snap.Length; i++) {
+ yield return snap[i];
+ }
+ }
+
+ public T Peek()
+ {
+ if (TryPeek(out T? v)) {
+ return v;
+ }
+
+ throw new InvalidOperationException("Stack is empty.");
+ }
+
+ public T Pop()
+ {
+ if (TryPop(out T value)) {
+ return value;
+ }
+
+ throw new InvalidOperationException("Stack is empty.");
+ }
+
+ public T[] PopRange(int count)
+ {
+ if (count <= 0) {
+ return Array.Empty();
+ }
+
+ T[] result = new T[count];
+ int i = 0;
+ for (; i < count; i++)
+ {
+ if (!TryPop(out result[i]))
+ {
+ break;
+ }
+ }
+ if (i == count) {
+ return result;
+ }
+
+ if (i == 0) {
+ return Array.Empty();
+ }
+
+ T[] trimmed = new T[i];
+ Array.Copy(result, trimmed, i);
+ return trimmed;
+ }
+
+ public void Push(T item)
+ {
+ SpinWait sw = default;
+ while (true)
+ {
+ int oldTop = Volatile.Read(ref _top);
+ if (oldTop >= _capacity) {
+ throw new InvalidOperationException("Stack is full.");
+ }
+
+ if (Interlocked.CompareExchange(ref _top, oldTop + 1, oldTop) != oldTop)
+ {
+ sw.SpinOnce();
+ continue; // failed to reserve slot
+ }
+
+ // We exclusively own slot oldTop. Publish value then state.
+ _values[oldTop] = item;
+ Volatile.Write(ref _states[oldTop], 1); // publish
+ _ = Interlocked.Increment(ref _count);
+ return;
+ }
+ }
+
+ public void PushRange(ReadOnlySpan span)
+ {
+ for (int i = 0; i < span.Length; i++) {
+ Push(span[i]);
+ }
+ }
+
+ ///
+ /// Best-effort snapshot for enumeration; not strongly consistent under concurrent
+ /// mutations.
+ ///
+ public T[] ToArraySnapshot()
+ {
+ int expected = Math.Min(Volatile.Read(ref _count), _capacity);
+ if (expected == 0) {
+ return Array.Empty();
+ }
+
+ T[] snap = new T[expected];
+ int copied = 0;
+ int idx = Volatile.Read(ref _top) - 1;
+ while (idx >= 0 && copied < expected)
+ {
+ if (Volatile.Read(ref _states[idx]) == 1) {
+ snap[copied++] = _values[idx];
+ }
+ idx--;
+ }
+
+ if (copied == snap.Length) {
+ return snap;
+ }
+
+ T[] trimmed = new T[copied];
+ Array.Copy(snap, trimmed, copied);
+ return trimmed;
+ }
+
+ public bool TryPeek(out T value)
+ {
+ value = default!;
+ SpinWait sw = default;
+
+ while (true)
+ {
+ int top = Volatile.Read(ref _top);
+ if (top <= 0) {
+ return false;
+ }
+ int idx = top - 1;
+
+ // If not yet published or racing, retry/spin
+ if (Volatile.Read(ref _states[idx]) == 0)
+ {
+ if (Volatile.Read(ref _top) != top)
+ {
+ continue; // head moved, retry
+ }
+ sw.SpinOnce();
+ continue;
+ }
+
+ T v = _values[idx];
+
+ // Validate consistency
+ if (Volatile.Read(ref _top) == top && Volatile.Read(ref _states[idx]) == 1)
+ {
+ value = v;
+ return true;
+ }
+ }
+ }
+
+ public bool TryPop(out T value)
+ {
+ value = default!;
+ SpinWait sw = default;
+
+ while (true)
+ {
+ int oldTop = Volatile.Read(ref _top);
+ if (oldTop <= 0)
+ {
+ return false; // empty
+ }
+
+ int newTop = oldTop - 1; // index to consume
+ if (Interlocked.CompareExchange(ref _top, newTop, oldTop) != oldTop)
+ {
+ sw.SpinOnce();
+ continue; // failed to reserve element
+ }
+
+ // If producer hasn't published yet, roll back our reservation and retry.
+ if (Volatile.Read(ref _states[newTop]) == 0)
+ {
+ // attempt to restore top; ok if it fails due to concurrent operations
+ Interlocked.CompareExchange(ref _top, newTop + 1, newTop);
+ sw.SpinOnce();
+ continue;
+ }
+
+ value = _values[newTop];
+ _values[newTop] = default!; // avoid ref retention
+ Volatile.Write(ref _states[newTop], 0); // mark empty
+ Interlocked.Decrement(ref _count);
+ return true;
+ }
+ }
+
+ public int TryPopRange(Span destination)
+ {
+ if (destination.IsEmpty) {
+ return 0;
+ }
+
+ int i = 0;
+ for (; i < destination.Length; i++)
+ {
+ if (!TryPop(out destination[i]))
+ {
+ break;
+ }
+ }
+ return i;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ }
+}
diff --git a/StgSharp.Common/Controlling/KeyEnum.cs b/StgSharp.Common/Controlling/KeyEnum.cs
index 5505e64a..3e94f4ed 100644
--- a/StgSharp.Common/Controlling/KeyEnum.cs
+++ b/StgSharp.Common/Controlling/KeyEnum.cs
@@ -171,22 +171,22 @@ namespace StgSharp
#pragma warning restore CA1027
{
- Joystick_1 = glConst.GLFW_JOYSTICK_1,
- Joystick_2 = glConst.GLFW_JOYSTICK_2,
- Joystick_3 = glConst.GLFW_JOYSTICK_3,
- Joystick_4 = glConst.GLFW_JOYSTICK_4,
- Joystick_5 = glConst.GLFW_JOYSTICK_5,
- Joystick_6 = glConst.GLFW_JOYSTICK_6,
- Joystick_7 = glConst.GLFW_JOYSTICK_7,
- Joystick_8 = glConst.GLFW_JOYSTICK_8,
- Joystick_9 = glConst.GLFW_JOYSTICK_9,
- Joystick_10 = glConst.GLFW_JOYSTICK_10,
- Joystick_11 = glConst.GLFW_JOYSTICK_11,
- Joystick_12 = glConst.GLFW_JOYSTICK_12,
- Joystick_13 = glConst.GLFW_JOYSTICK_13,
- Joystick_14 = glConst.GLFW_JOYSTICK_14,
- Joystick_15 = glConst.GLFW_JOYSTICK_15,
- Joystick_16 = glConst.GLFW_JOYSTICK_16,
+ Joystick_1 = Constant.GLFW_JOYSTICK_1,
+ Joystick_2 = Constant.GLFW_JOYSTICK_2,
+ Joystick_3 = Constant.GLFW_JOYSTICK_3,
+ Joystick_4 = Constant.GLFW_JOYSTICK_4,
+ Joystick_5 = Constant.GLFW_JOYSTICK_5,
+ Joystick_6 = Constant.GLFW_JOYSTICK_6,
+ Joystick_7 = Constant.GLFW_JOYSTICK_7,
+ Joystick_8 = Constant.GLFW_JOYSTICK_8,
+ Joystick_9 = Constant.GLFW_JOYSTICK_9,
+ Joystick_10 = Constant.GLFW_JOYSTICK_10,
+ Joystick_11 = Constant.GLFW_JOYSTICK_11,
+ Joystick_12 = Constant.GLFW_JOYSTICK_12,
+ Joystick_13 = Constant.GLFW_JOYSTICK_13,
+ Joystick_14 = Constant.GLFW_JOYSTICK_14,
+ Joystick_15 = Constant.GLFW_JOYSTICK_15,
+ Joystick_16 = Constant.GLFW_JOYSTICK_16,
First = Joystick_1,
Last = Joystick_16
diff --git a/StgSharp.Common/HighPerformance/Memory/ConcurrentBufferStack.cs b/StgSharp.Common/HighPerformance/Memory/ConcurrentBufferStack.cs
index 99171b6b..e045af13 100644
--- a/StgSharp.Common/HighPerformance/Memory/ConcurrentBufferStack.cs
+++ b/StgSharp.Common/HighPerformance/Memory/ConcurrentBufferStack.cs
@@ -65,7 +65,7 @@ namespace StgSharp.HighPerformance.Memory
return new ConcurrentBufferStack(4); // Default small capacity
}
- ConcurrentBufferStack stack = new ConcurrentBufferStack(span.Length);
+ ConcurrentBufferStack stack = new(span.Length);
// Use internal method for efficient bulk initialization
@@ -106,12 +106,20 @@ namespace StgSharp.HighPerformance.Memory
public void Clear()
{
- Header* header = (Header*)_buffer;
_lock.EnterExpansionProcess();
try
{
- header->Top = 0;
- Volatile.Write(ref _count, 0);
+ _lock.EnterBufferCopy(); // exclusive while mutating Top
+ try
+ {
+ Header* header = (Header*)_buffer;
+ header->Top = 0;
+ Volatile.Write(ref _count, 0);
+ }
+ finally
+ {
+ _lock.ExitBufferCopy();
+ }
}
finally
{
@@ -127,29 +135,29 @@ namespace StgSharp.HighPerformance.Memory
public T Pop()
{
- Header* header = (Header*)_buffer;
while (true)
{
- nuint oldTop = Volatile.Read(ref header->Top);
- if (oldTop <= 0) {
- throw new InvalidOperationException("Stack underflow");
- }
-
- if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
+ _lock.EnterBufferRead(); // prevent buffer being reallocated while reading/modifying Top
+ try
{
- _lock.EnterBufferRead();
- try
+ Header* header = (Header*)_buffer;
+ nuint oldTop = Volatile.Read(ref header->Top);
+ if (oldTop <= 0) {
+ throw new InvalidOperationException("Stack underflow");
+ }
+
+ if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
{
T* dataStart = (T*)(_buffer + sizeof(Header));
_ = Interlocked.Decrement(ref _count);
return dataStart[oldTop - 1];
}
- finally
- {
- _lock.ExitBufferRead();
- }
}
- Thread.Sleep(0); // Yield to avoid busy waiting
+ finally
+ {
+ _lock.ExitBufferRead();
+ }
+ Thread.Sleep(0);
}
}
@@ -157,14 +165,16 @@ namespace StgSharp.HighPerformance.Memory
{
while (true)
{
+ // Ensure capacity, expanding if needed
_lock.EnterMetaDataRead();
try
{
Header* header = (Header*)_buffer;
- nuint oldTop = Volatile.Read(ref header->Top);
+ nuint topSnapshot = Volatile.Read(ref header->Top);
nuint cap = Volatile.Read(ref header->Capacity);
- if (oldTop >= cap)
+ if (topSnapshot >= cap)
{
+ // Need expansion
_lock.ExitMetaDataRead();
_lock.EnterExpansionProcess();
try
@@ -193,22 +203,6 @@ namespace StgSharp.HighPerformance.Memory
}
continue;
}
-
- if (Interlocked.CompareExchange(ref header->Top, oldTop + 1, oldTop) == oldTop)
- {
- _lock.EnterBufferRead();
- try
- {
- T* dataStart = (T*)(_buffer + sizeof(Header));
- dataStart[oldTop] = item;
- _ = Interlocked.Increment(ref _count);
- return;
- }
- finally
- {
- _lock.ExitBufferRead();
- }
- }
}
finally
{
@@ -216,39 +210,63 @@ namespace StgSharp.HighPerformance.Memory
_lock.ExitMetaDataRead();
}
}
- Thread.Sleep(0); // Yield to avoid busy waiting
+
+ // Reserve and write under exclusive buffer lock to avoid consumers reading unwritten data
+ _lock.EnterBufferCopy();
+ try
+ {
+ Header* header = (Header*)_buffer;
+ nuint idx = header->Top; // reserve next slot
+ if (idx >= header->Capacity)
+ {
+ // lost race against expander or other producer; retry
+ continue;
+ }
+
+ T* dataStart = (T*)(_buffer + sizeof(Header));
+ dataStart[idx] = item; // write first
+ header->Top = idx + 1; // then publish by increasing top
+ _ = Interlocked.Increment(ref _count);
+ return;
+ }
+ finally
+ {
+ _lock.ExitBufferCopy();
+ }
}
}
public bool TryPop(out T value)
{
- Header* header = (Header*)_buffer;
while (true)
{
- nuint oldTop = Volatile.Read(ref header->Top);
- if (oldTop <= 0)
+ _lock.EnterBufferRead();
+ try
{
- value = default;
- return false;
- }
+ Header* header = (Header*)_buffer;
+ nuint oldTop = Volatile.Read(ref header->Top);
+ if (oldTop <= 0)
+ {
+ value = default;
+ return false;
+ }
- if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
- {
- _lock.EnterBufferRead();
- try
+ if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
{
T* dataStart = (T*)(_buffer + sizeof(Header));
value = dataStart[oldTop - 1];
_ = Interlocked.Decrement(ref _count);
+ return true;
}
- finally
- {
- _lock.ExitBufferRead();
- }
- return true;
}
- Thread.Sleep(0); // Yield to avoid busy waiting
+ finally
+ {
+ _lock.ExitBufferRead();
+ }
+ value = default;
+ return true;
}
+ Thread.Sleep(0); // Yield to avoid busy waiting
}
// Internal method for efficient bulk initialization
diff --git a/StgSharp.Common/Internal/Graphic/gl.cs b/StgSharp.Common/Internal/Graphic/gl.cs
index 07ac1aaa..02a7dc6d 100644
--- a/StgSharp.Common/Internal/Graphic/gl.cs
+++ b/StgSharp.Common/Internal/Graphic/gl.cs
@@ -38,7 +38,7 @@ namespace StgSharp.Internal
internal static partial class GraphicFramework
{
- internal static uint[] GLtype = [];
+ internal static uint[] GLtype = new uint[16];
static GraphicFramework()
{
diff --git a/StgSharp.Common/Threading/BufferExpansionLock.cs b/StgSharp.Common/Threading/BufferExpansionLock.cs
index 6ecbc3f9..df5fbedb 100644
--- a/StgSharp.Common/Threading/BufferExpansionLock.cs
+++ b/StgSharp.Common/Threading/BufferExpansionLock.cs
@@ -42,7 +42,7 @@ namespace StgSharp.Threading
public class BufferExpansionLock : IDisposable
{
- private static readonly ThreadLocal Count = new();
+ private static readonly ThreadLocal Count = new(() => 0);
private readonly ReaderWriterLockSlim _copyLock;
private readonly ReaderWriterLockSlim _expansionLock;
@@ -74,13 +74,11 @@ namespace StgSharp.Threading
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnterBufferRead()
{
- if (++Count.Value == 1)
- {
+ int c = Count.Value;
+ if (c == 0) {
_copyLock.EnterReadLock();
- } else
- {
- Count.Value++;
}
+ Count.Value = c + 1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -104,12 +102,10 @@ namespace StgSharp.Threading
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExitBufferRead()
{
- if (--Count.Value == 0)
- {
+ int c = Count.Value - 1;
+ Count.Value = c;
+ if (c == 0) {
_copyLock.ExitReadLock();
- } else
- {
- Count.Value--;
}
}
diff --git a/StgSharp.Mathematics/Numeric/MatrixCompute.Basic.cs b/StgSharp.Mathematics/Numeric/MatrixCompute.Basic.cs
index 2af8c137..cfdc1a55 100644
--- a/StgSharp.Mathematics/Numeric/MatrixCompute.Basic.cs
+++ b/StgSharp.Mathematics/Numeric/MatrixCompute.Basic.cs
@@ -91,6 +91,7 @@ namespace StgSharp.Mathematics.Numeric
* wait
*/
}
+ MatrixParallel.ReleaseParallelLeadership(ref pool);
MatrixKernel* leftPtr = left.Buffer;
MatrixKernel* rightPtr = right.Buffer;
MatrixKernel* ansPtr = ans.Buffer;
@@ -98,6 +99,8 @@ namespace StgSharp.Mathematics.Numeric
NativeIntrinsic.Intrinsic.f32_sub(leftPtr + i, rightPtr + i, ansPtr + i);
}
+ // thread.
+
return ans;
}
Stack taskGroup = MatrixParallel.PublicTask(left.GetColumnEnumeration(), right.GetColumnEnumeration(), ans.GetColumnEnumeration(),
diff --git a/StgSharp.Mathematics/Numeric/MatrixParallel.Publish.cs b/StgSharp.Mathematics/Numeric/MatrixParallel.Publish.cs
index 894aa176..1e138806 100644
--- a/StgSharp.Mathematics/Numeric/MatrixParallel.Publish.cs
+++ b/StgSharp.Mathematics/Numeric/MatrixParallel.Publish.cs
@@ -820,9 +820,9 @@ namespace StgSharp.Mathematics.Numeric
int tileWidth = (1024 / secCount) + 1;
int tileCount = (primCount + tileWidth - 1) / tileWidth;
- MatrixParallelPool pool = LeadParallel();
+ CapacityFixedStack pool = LeadParallel();
- int threadCount = pool.SpareThreadCount;
+ int threadCount = pool.Count;
int tilePerThreadLess = tileCount / threadCount; // tiles per thread without extra slice
int columnAfterBaseTile = tileCount % threadCount * tileWidth; // tile sliced into columns
int extraColumnPerThread = columnAfterBaseTile / threadCount;
@@ -833,7 +833,7 @@ namespace StgSharp.Mathematics.Numeric
MatrixParallelTaskPackage* current = package;
while (true)
{
- if (!pool.TryRequestWrap(out MatrixParallelWrap? wrap) || wrap is null)
+ if (!pool.TryPop(out MatrixParallelThread? wrap) || wrap is null)
{
#if DEBUG
throw new InvalidOperationException("Someone steal parallel wraps from the pool.");
@@ -846,7 +846,7 @@ namespace StgSharp.Mathematics.Numeric
bool isLargeSlice = extraColumnCount > 0;
// count tiles for wrap
- int wrapWidth = wrap!.WrapTaskCapacity;
+ int wrapWidth =/**/ 3/* wrap!.WrapTaskCapacity/**/;
int columnFromSlicingCount = (hasSlice ? extraColumnPerThread : 0) * wrapWidth;
int baseColumn = wrapWidth * tilePerThreadLess * tileWidth;
int columnInWrap = baseColumn + columnFromSlicingCount + (isLargeSlice ? 1 : 0);
@@ -872,8 +872,9 @@ namespace StgSharp.Mathematics.Numeric
total -= columnInWrap;
// set to wrap and push
- wrap.WrapTask = (MatrixParallelTaskPackageNonGeneric*)current;
- taskBunch.Push(wrap);
+ wrap.Tasks.Enqueue((IntPtr)current);
+
+ // taskBunch.Push(wrap);
if (total <= 0)
diff --git a/StgSharp.Mathematics/Numeric/MatrixParallelPool.cs b/StgSharp.Mathematics/Numeric/MatrixParallelPool.cs
index bcaa6b75..0370dfaa 100644
--- a/StgSharp.Mathematics/Numeric/MatrixParallelPool.cs
+++ b/StgSharp.Mathematics/Numeric/MatrixParallelPool.cs
@@ -65,6 +65,7 @@ namespace StgSharp.Mathematics.Numeric
internal CapacityFixedStack SpareWrap { get; set; }
+ /*
public void ReturnIndividualHandle(ref MatrixParallelWrap.Handle handle)
{
MatrixParallelWrap wrap = handle.Wrap;
@@ -108,6 +109,6 @@ namespace StgSharp.Mathematics.Numeric
wrap = null!;
return false;
}
-
+ /**/
}
}
diff --git a/StgSharp.Mathematics/Numeric/MatrixParallelThread.cs b/StgSharp.Mathematics/Numeric/MatrixParallelThread.cs
index c4fa8070..62e6b10e 100644
--- a/StgSharp.Mathematics/Numeric/MatrixParallelThread.cs
+++ b/StgSharp.Mathematics/Numeric/MatrixParallelThread.cs
@@ -96,6 +96,11 @@ namespace StgSharp.Mathematics.Numeric
_resetEvent.Set();
}
+ public void ReturnToPool()
+ {
+ // MatrixParallel.(this);
+ }
+
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
diff --git a/StgSharpDebug/Program.cs b/StgSharpDebug/Program.cs
index f4835a5b..b583fc05 100644
--- a/StgSharpDebug/Program.cs
+++ b/StgSharpDebug/Program.cs
@@ -2,8 +2,8 @@
// -----------------------------------------------------------------------
// file="Program"
// Project: StgSharp
-// AuthorGroup: Nitload Space
-// Copyright (c) Nitload Space. All rights reserved.
+// AuthorGroup: Nitload
+// Copyright (c) Nitload. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -40,10 +40,12 @@ using StgSharp.Script;
using StgSharp.Script.Express;
using System;
+using System.Collections.Concurrent;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
+using System.Threading;
using System.Xml.XPath;
namespace StgSharp
@@ -51,7 +53,7 @@ namespace StgSharp
internal static partial class Program
{
- private static string testExpPath = @"D:\Programmer\stepcode\data\ap203\ap203.exp";
+ private static string testExpPath = @"D:\\Programmer\\stepcode\\data\\ap203\\ap203.exp";
internal static unsafe void Main(string[] args)
{
@@ -59,6 +61,118 @@ namespace StgSharp
{
World.Init();
+ // New test with synchronized start:
+ // - 8 producer threads, each pushing 1000 unique integers
+ // - 8 consumer threads, each attempting to pop 1000 times
+ // - Consumers print one line per successful pop
+ // - Finally, print popped/remaining counts and values
+
+ const int producerThreads = 8;
+ const int consumerThreads = 8;
+ const int perProducer = 1000;
+ const int perConsumerAttempts = 1000;
+
+ ConcurrentBufferStack stack = new ConcurrentBufferStack();
+ Thread[] producers = new Thread[producerThreads];
+ Thread[] consumers = new Thread[consumerThreads];
+ ConcurrentBag popped = new ConcurrentBag();
+
+ int totalThreads = producerThreads + consumerThreads;
+ using CountdownEvent ready = new CountdownEvent(totalThreads);
+ using ManualResetEventSlim startGate = new ManualResetEventSlim(false);
+
+ for (int c = 0; c < consumerThreads; c++)
+ {
+ consumers[c] = new Thread(() => {
+ ready.Signal();
+ startGate.Wait();
+
+ for (int k = 0; k < perConsumerAttempts; k++)
+ {
+ if (stack.TryPop(out int v))
+ {
+ popped.Add(v);
+ Console.WriteLine($"pop: {v}");
+ } else
+ {
+ Thread.Yield();
+ }
+ }
+ })
+ {
+ IsBackground = true
+ };
+ }
+
+ for (int p = 0; p < producerThreads; p++)
+ {
+ int captured = p;
+ producers[p] = new Thread(() => {
+ ready.Signal();
+ startGate.Wait();
+
+ int begin = captured * perProducer;
+ for (int j = 0; j < perProducer; j++)
+ {
+ stack.Push(begin + j);
+ if ((j & 0xFF) == 0) {
+ Thread.Yield();
+ }
+ }
+ })
+ {
+ IsBackground = true
+ };
+ }
+
+ foreach (Thread t in consumers) {
+ t.Start();
+ }
+
+ foreach (Thread t in producers) {
+ t.Start();
+ }
+
+ // Ensure all threads are ready, then start simultaneously
+ ready.Wait();
+ startGate.Set();
+
+ foreach (Thread t in producers) {
+ t.Join();
+ }
+
+ foreach (Thread t in consumers) {
+ t.Join();
+ }
+
+ // Gather remaining data
+ List remaining = new System.Collections.Generic.List();
+ while (stack.TryPop(out int value)) {
+ remaining.Add(value);
+ }
+
+ Console.WriteLine($"Popped count: {popped.Count}");
+ Console.WriteLine("Popped values:");
+ foreach (int v in popped)
+ {
+ Console.Write(v);
+ Console.Write('\t');
+ }
+ Console.WriteLine();
+
+ Console.WriteLine($"Remaining count: {remaining.Count}");
+ Console.WriteLine("Remaining values:");
+ foreach (int v in remaining)
+ {
+ Console.Write(v);
+ Console.Write('\t');
+ }
+ Console.WriteLine();
+
+ // Sanity check
+ Console.WriteLine($"Produced: {producerThreads * perProducer}, Popped+Remaining: {popped.Count + remaining.Count}");
+
+ /*
// TestMatrixParallel();
// 原有的矩阵测试代码
From ec1b8dbd512ba19be4c563058bf71e088fda197b Mon Sep 17 00:00:00 2001
From: Youmu Liu <105042599+Nitload-NSI@users.noreply.github.com>
Date: Thu, 6 Nov 2025 18:12:04 +0800
Subject: [PATCH 2/3] working on dynamic wrapping
---
.../ConcurrentCapacityFixedStack.cs | 8 +-
.../Memory/ConcurrentBufferStack.cs | 38 +-
.../Threading/SamplingAdaptiveBackoff.cs | 457 ------------------
.../Numeric/MatrixCompute.Basic.cs | 8 +-
.../Numeric/MatrixParallel.Publish.cs | 24 +-
.../Numeric/MatrixParallel.cs | 57 +--
.../Numeric/MatrixParallelPool.cs | 148 +++---
.../Numeric/MatrixParallelWrap.cs | 7 +
StgSharpDebug/Program.cs | 10 +-
9 files changed, 145 insertions(+), 612 deletions(-)
delete mode 100644 StgSharp.Common/Threading/SamplingAdaptiveBackoff.cs
diff --git a/StgSharp.Common/Collections/ConcurrentCapacityFixedStack.cs b/StgSharp.Common/Collections/ConcurrentCapacityFixedStack.cs
index 69dadc9e..92884837 100644
--- a/StgSharp.Common/Collections/ConcurrentCapacityFixedStack.cs
+++ b/StgSharp.Common/Collections/ConcurrentCapacityFixedStack.cs
@@ -105,7 +105,7 @@ namespace StgSharp.Collections
public T[] PopRange(int count)
{
if (count <= 0) {
- return Array.Empty();
+ return [];
}
T[] result = new T[count];
@@ -122,7 +122,7 @@ namespace StgSharp.Collections
}
if (i == 0) {
- return Array.Empty();
+ return [];
}
T[] trimmed = new T[i];
@@ -251,7 +251,7 @@ namespace StgSharp.Collections
if (Volatile.Read(ref _states[newTop]) == 0)
{
// attempt to restore top; ok if it fails due to concurrent operations
- Interlocked.CompareExchange(ref _top, newTop + 1, newTop);
+ _ = Interlocked.CompareExchange(ref _top, newTop + 1, newTop);
sw.SpinOnce();
continue;
}
@@ -259,7 +259,7 @@ namespace StgSharp.Collections
value = _values[newTop];
_values[newTop] = default!; // avoid ref retention
Volatile.Write(ref _states[newTop], 0); // mark empty
- Interlocked.Decrement(ref _count);
+ _ = Interlocked.Decrement(ref _count);
return true;
}
}
diff --git a/StgSharp.Common/HighPerformance/Memory/ConcurrentBufferStack.cs b/StgSharp.Common/HighPerformance/Memory/ConcurrentBufferStack.cs
index e045af13..1e2a2b51 100644
--- a/StgSharp.Common/HighPerformance/Memory/ConcurrentBufferStack.cs
+++ b/StgSharp.Common/HighPerformance/Memory/ConcurrentBufferStack.cs
@@ -47,7 +47,7 @@ namespace StgSharp.HighPerformance.Memory
return new BufferStack(4); // Default small capacity
}
- BufferStack stack = new BufferStack(span.Length);
+ BufferStack stack = new(span.Length);
// Use internal method for efficient bulk initialization
@@ -82,7 +82,7 @@ namespace StgSharp.HighPerformance.Memory
private byte* _buffer;
private readonly BufferExpansionLock _lock = new();
- private ulong _count;
+ private long _count;
public ConcurrentBufferStack()
{
@@ -101,7 +101,7 @@ namespace StgSharp.HighPerformance.Memory
public ulong Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => Volatile.Read(ref _count);
+ get => (ulong)Volatile.Read(ref _count);
}
public void Clear()
@@ -141,7 +141,7 @@ namespace StgSharp.HighPerformance.Memory
try
{
Header* header = (Header*)_buffer;
- nuint oldTop = Volatile.Read(ref header->Top);
+ int oldTop = Volatile.Read(ref header->Top);
if (oldTop <= 0) {
throw new InvalidOperationException("Stack underflow");
}
@@ -170,8 +170,8 @@ namespace StgSharp.HighPerformance.Memory
try
{
Header* header = (Header*)_buffer;
- nuint topSnapshot = Volatile.Read(ref header->Top);
- nuint cap = Volatile.Read(ref header->Capacity);
+ int topSnapshot = Volatile.Read(ref header->Top);
+ int cap = Volatile.Read(ref header->Capacity);
if (topSnapshot >= cap)
{
// Need expansion
@@ -182,8 +182,8 @@ namespace StgSharp.HighPerformance.Memory
header = (Header*)_buffer;
if (header->Top >= header->Capacity)
{
- nuint newCap = header->Capacity * 2;
- nuint newSize = (nuint)sizeof(Header) + (newCap * (nuint)sizeof(T));
+ int newCap = header->Capacity * 2;
+ nuint newSize = (nuint)sizeof(Header) + ((nuint)newCap * (nuint)sizeof(T));
_lock.EnterBufferCopy();
try
@@ -216,7 +216,7 @@ namespace StgSharp.HighPerformance.Memory
try
{
Header* header = (Header*)_buffer;
- nuint idx = header->Top; // reserve next slot
+ int idx = header->Top; // reserve next slot
if (idx >= header->Capacity)
{
// lost race against expander or other producer; retry
@@ -244,7 +244,7 @@ namespace StgSharp.HighPerformance.Memory
try
{
Header* header = (Header*)_buffer;
- nuint oldTop = Volatile.Read(ref header->Top);
+ int oldTop = Volatile.Read(ref header->Top);
if (oldTop <= 0)
{
value = default;
@@ -263,10 +263,8 @@ namespace StgSharp.HighPerformance.Memory
{
_lock.ExitBufferRead();
}
- value = default;
- return true;
+ Thread.Sleep(0);
}
- Thread.Sleep(0); // Yield to avoid busy waiting
}
// Internal method for efficient bulk initialization
@@ -282,28 +280,28 @@ namespace StgSharp.HighPerformance.Memory
fixed (T* sourcePtr = span) {
Buffer.MemoryCopy(sourcePtr, dataStart,
- header->Capacity * (nuint)sizeof(T),
+ (nuint)header->Capacity * (nuint)sizeof(T),
(nuint)span.Length * (nuint)sizeof(T));
}
- header->Top = (nuint)span.Length;
- Volatile.Write(ref _count, (ulong)span.Length);
+ header->Top = span.Length;
+ Volatile.Write(ref _count, span.Length);
}
private void AllocateBuffer(int capacity)
{
- nuint size = (nuint)sizeof(Header) + ((nuint)capacity * (nuint)sizeof(T));
+ nuint size = (nuint)sizeof(Header) + (nuint)capacity * (nuint)sizeof(T);
_buffer = (byte*)NativeMemory.Alloc(size);
Header* header = (Header*)_buffer;
header->Top = 0;
- header->Capacity = (nuint)capacity;
+ header->Capacity = capacity;
}
private struct Header
{
- public nuint Capacity; // max capacity of the stack (number of elements)
- public nuint Top; // next available index in the stack
+ public int Capacity; // max capacity of the stack (number of elements)
+ public int Top; // next available index in the stack
}
diff --git a/StgSharp.Common/Threading/SamplingAdaptiveBackoff.cs b/StgSharp.Common/Threading/SamplingAdaptiveBackoff.cs
deleted file mode 100644
index 71e7994c..00000000
--- a/StgSharp.Common/Threading/SamplingAdaptiveBackoff.cs
+++ /dev/null
@@ -1,457 +0,0 @@
-//-----------------------------------------------------------------------
-// -----------------------------------------------------------------------
-// file="SamplingAdaptiveBackoff"
-// Project: StgSharp
-// AuthorGroup: Nitload
-// Copyright (c) Nitload. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// -----------------------------------------------------------------------
-// -----------------------------------------------------------------------
-using System;
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using System.Threading;
-
-///
-/// Optimized sampling-based adaptive backoff strategy that re-samples and evaluates the current
-/// contention situation and adjusts the strategy after a certain number of fallbacks
-///
-public struct SamplingAdaptiveBackoff
-{
- /*
- private const int CONFIDENCE_THRESHOLD = 3; // Requires N consecutive sampling confirmations to switch strategies
-
- // Configuration constants
- private const int FALLBACK_SAMPLING_THRESHOLD = 64; // Triggers re-sampling every 64 fallbacks
- private const int INITIAL_SPINWAIT_THRESHOLD = 8; // Cheap spinning for the first few SpinWait attempts
- private const int MAX_SAMPLING_WINDOW_MS = 1000; // Maximum sampling window time
- private const int MIN_SAMPLING_WINDOW_MS = 100; // Minimum sampling window time
- private const int PERFORMANCE_HISTORY_MASK = 7; // Use 3-bit mask for 8 entries (power of 2)
- private const double SIGNIFICANT_IMPROVEMENT = 0.15; // 15% improvement is considered significant
-
- private BackoffMethod _candidateMethod;
- private BackoffMethod _currentMethod;
- private byte _confidenceCounter;
- private byte _consecutiveFailures;
-
- // Packed performance data to reduce memory footprint
- private PackedMethodPerformance _packedPerformances; // Bit-packed performance data for all 3 methods
- private SamplingWindow _currentSample;
-
- // Instance state
- private SpinWait _spinWait;
- private uint _rng;
-
- ///
- /// Gets the current method
- ///
- public readonly BackoffMethod CurrentMethod => _currentMethod;
-
- ///
- /// Creates a new optimized sampling adaptive backoff strategy instance
- ///
- public static SamplingAdaptiveBackoff Create()
- {
- // Choose initial strategy based on system characteristics
- BackoffMethod initialMethod = DetermineInitialMethod();
-
- SamplingAdaptiveBackoff instance = new()
- {
- _spinWait = default,
- _currentMethod = initialMethod,
- _consecutiveFailures = 0,
- _confidenceCounter = 0,
- _candidateMethod = initialMethod,
- _rng = GenerateInitialSeed(),
- _packedPerformances = PackedMethodPerformance.Initialize()
- };
-
- // Start the first sampling window
- instance._currentSample.Reset(initialMethod);
-
- return instance;
- }
-
- ///
- /// Gets the current sampling status (for debugging)
- ///
- public readonly (int attempts, int successes, int fallbacks, double successRate, double fallbackRate) GetCurrentSampleStats()
- {
- return (_currentSample.TotalAttempts,
- _currentSample.SuccessfulAttempts,
- _currentSample.FallbackCount,
- _currentSample.SuccessRate,
- _currentSample.FallbackRate);
- }
-
- ///
- /// Gets the historical performance of the specified method (for monitoring)
- ///
- public readonly (double successRate, double fallbackRate, double overallScore) GetMethodPerformance(
- BackoffMethod method)
- {
- var (successRate, fallbackRate, score) = _packedPerformances.GetPerformance(method);
- return (successRate, fallbackRate, score);
- }
-
- ///
- /// Called when attempting CAS (must be called regardless of success or failure)
- ///
- ///
- /// Whether this CAS attempt was successful
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void OnCASAttempt(bool success)
- {
- _currentSample.TotalAttempts++;
-
- if (success)
- {
- _currentSample.SuccessfulAttempts++;
- _consecutiveFailures = 0;
- _spinWait = default; // Reset SpinWait after success
- } else
- {
- _consecutiveFailures++;
- }
- }
-
- ///
- /// Called when CAS fails and fallback needs to be executed, performs backoff and may trigger
- /// re-sampling
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void OnFallbackAndBackoff()
- {
- _currentSample.FallbackCount++;
-
- // Check if re-sampling threshold is reached using bitwise operations for faster comparison
- if ((_currentSample.FallbackCount & ~(FALLBACK_SAMPLING_THRESHOLD - 1)) != 0 ||
- _currentSample.ElapsedTicks > MAX_SAMPLING_WINDOW_MS)
- {
- if (_currentSample.ElapsedTicks >= MIN_SAMPLING_WINDOW_MS) {
- EvaluateCurrentSample();
- }
- }
-
- // Perform backoff using jump table approach for better branch prediction
- PerformBackoffOptimized();
- }
-
- ///
- /// Determines initial method based on system characteristics
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static BackoffMethod DetermineInitialMethod()
- {
- int logicalCores = Environment.ProcessorCount;
- return logicalCores <= 16 ? BackoffMethod.SpinWait : BackoffMethod.Sleep0;
- }
-
- ///
- /// Determines the method to test in the next sampling window using optimized random selection
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private BackoffMethod DetermineNextTestMethod()
- {
- // Use bitwise operations for 80%/20% probability split
- return (NextRandom() & 0xF) < 13 ? _candidateMethod : (BackoffMethod)(NextRandom() & 3);
- }
-
- ///
- /// Evaluates the current sampling window and may adjust the strategy
- ///
- [MethodImpl(MethodImplOptions.NoInlining)]
- private void EvaluateCurrentSample()
- {
- // Update performance record for the current method using packed data
- _packedPerformances.UpdatePerformance(
- _currentSample.TestedMethod, _currentSample.SuccessRate, _currentSample.FallbackRate,
- _currentSample.ElapsedTicks / Math.Max(1, _currentSample.TotalAttempts));
-
- // Find the currently best performing method
- BackoffMethod bestMethod = _packedPerformances.FindBestMethod();
-
- // Use branchless logic for confidence tracking
- bool methodChanged = bestMethod != _currentMethod;
- bool candidateMatches = bestMethod == _candidateMethod;
-
- _confidenceCounter = methodChanged ? (candidateMatches ? (byte)(_confidenceCounter + 1) : (byte)1) : (byte)0;
-
- if (methodChanged && candidateMatches && _confidenceCounter >= CONFIDENCE_THRESHOLD)
- {
- SwitchToMethod(bestMethod);
- } else if (methodChanged && !candidateMatches)
- {
- _candidateMethod = bestMethod;
- } else if (!methodChanged) {
- _candidateMethod = _currentMethod;
- }
-
- // Start new sampling window
- BackoffMethod nextTestMethod = DetermineNextTestMethod();
- _currentSample.Reset(nextTestMethod);
-
- if (nextTestMethod != _currentMethod)
- {
- _currentMethod = nextTestMethod;
- _spinWait = default;
- }
- }
-
- ///
- /// Generates initial seed for RNG
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static uint GenerateInitialSeed()
- {
- return (uint)(Environment.TickCount64 ^ (Thread.CurrentThread.ManagedThreadId * 0x9E3779B9U));
- }
-
- ///
- /// Fast pseudo-random number generator using XorShift32
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private uint NextRandom()
- {
- // XorShift32 - faster than LCG with better distribution
- _rng ^= _rng << 13;
- _rng ^= _rng >> 17;
- _rng ^= _rng << 5;
- return _rng;
- }
-
- ///
- /// Optimized backoff operation using jump table approach
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void PerformBackoffOptimized()
- {
- // Use computed goto pattern for better branch prediction
- switch (_currentMethod)
- {
- case BackoffMethod.SpinWait:
- PerformSpinWaitBackoff();
- break;
- case BackoffMethod.Yield:
- _ = Thread.Yield();
- break;
- case BackoffMethod.Sleep0:
- Thread.Sleep(0);
- break;
- default:
- break;
- }
- }
-
- ///
- /// Specialized SpinWait backoff with optimized logic
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void PerformSpinWaitBackoff()
- {
- if (_consecutiveFailures > INITIAL_SPINWAIT_THRESHOLD && _spinWait.NextSpinWillYield)
- {
- // Optimized random yield count using bitwise operations
- int yields = (int)(NextRandom() & 7) + 1;
- for (int i = 0; i < yields; i++) {
- _ = Thread.Yield();
- }
- } else
- {
- _spinWait.SpinOnce(sleep1Threshold:-1);
- }
- }
-
- ///
- /// Switches to a new method with optimized state reset
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void SwitchToMethod(BackoffMethod method)
- {
- _currentMethod = method;
- _candidateMethod = method;
- _confidenceCounter = 0;
- _spinWait = default;
- }
-
- public enum BackoffMethod : byte
- {
-
- SpinWait = 0,
- Yield = 1,
- Sleep0 = 2
-
- }
-
- // Optimized sampling window state with better packing
- private struct SamplingWindow
- {
-
- public BackoffMethod TestedMethod; // Current method being tested
- public int FallbackCount; // Number of fallbacks
- public int SuccessfulAttempts; // Number of successful attempts
- public int TotalAttempts; // Total number of attempts
- public long StartTicks; // Sampling window start time
-
- public readonly double SuccessRate
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => TotalAttempts == 0 ? 0.0 : (double)SuccessfulAttempts / TotalAttempts;
- }
-
- public readonly double FallbackRate
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => TotalAttempts == 0 ? 0.0 : (double)FallbackCount / TotalAttempts;
- }
-
- public readonly long ElapsedTicks
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => Environment.TickCount64 - StartTicks;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Reset(BackoffMethod method)
- {
- StartTicks = Environment.TickCount64;
- TotalAttempts = 0;
- SuccessfulAttempts = 0;
- FallbackCount = 0;
- TestedMethod = method;
- }
-
- }
-
- // Bit-packed performance data structure for memory efficiency
- private struct PackedMethodPerformance
- {
-
- // Pack all 3 methods' performance into compact format
- private ulong _packed0; // Method 0 (SpinWait) data
- private ulong _packed1; // Method 1 (Yield) data
- private ulong _packed2; // Method 2 (Sleep0) data
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public readonly BackoffMethod FindBestMethod()
- {
- (double _, double _, double score0) = UnpackPerformanceData(_packed0);
- (double _, double _, double score1) = UnpackPerformanceData(_packed1);
- (double _, double _, double score2) = UnpackPerformanceData(_packed2);
-
- // Use branchless comparison for best performance
- BackoffMethod best = score1 > score0 + SIGNIFICANT_IMPROVEMENT ?
- BackoffMethod.Yield :
- BackoffMethod.SpinWait;
- best = score2 > GetScore(best) + SIGNIFICANT_IMPROVEMENT ? BackoffMethod.Sleep0 : best;
-
- return best;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public readonly (double successRate, double fallbackRate, double overallScore) GetPerformance(
- BackoffMethod method)
- {
- ulong packed = method switch
- {
- BackoffMethod.SpinWait => _packed0,
- BackoffMethod.Yield => _packed1,
- BackoffMethod.Sleep0 => _packed2,
- _ => _packed0
- };
-
- return UnpackPerformanceData(packed);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static PackedMethodPerformance Initialize()
- {
- return new PackedMethodPerformance
- {
- _packed0 = PackPerformanceData(0.5, 0.5, 0),
- _packed1 = PackPerformanceData(0.5, 0.5, 0),
- _packed2 = PackPerformanceData(0.5, 0.5, 0)
- };
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void UpdatePerformance(BackoffMethod method, double successRate, double fallbackRate, long avgLatency)
- {
- ulong packed = PackPerformanceData(successRate, fallbackRate, avgLatency);
-
- switch (method)
- {
- case BackoffMethod.SpinWait:
- _packed0 = packed;
- break;
- case BackoffMethod.Yield:
- _packed1 = packed;
- break;
- case BackoffMethod.Sleep0:
- _packed2 = packed;
- break;
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private readonly double GetScore(BackoffMethod method)
- {
- return method switch
- {
- BackoffMethod.SpinWait => UnpackPerformanceData(_packed0).overallScore,
- BackoffMethod.Yield => UnpackPerformanceData(_packed1).overallScore,
- BackoffMethod.Sleep0 => UnpackPerformanceData(_packed2).overallScore,
- _ => 0.0
- };
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static ulong PackPerformanceData(double successRate, double fallbackRate, long avgLatency)
- {
- // Pack data into 64-bit value: 20 bits success rate, 20 bits fallback rate, 24 bits latency
- uint success = (uint)(successRate * 1048575); // 20 bits (2^20 - 1)
- uint fallback = (uint)(fallbackRate * 1048575); // 20 bits
- uint latency = (uint)Math.Min(avgLatency, 16777215); // 24 bits (2^24 - 1)
-
- return ((ulong)success << 44) | ((ulong)fallback << 24) | latency;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static (double successRate, double fallbackRate, double overallScore) UnpackPerformanceData(
- ulong packed)
- {
- double successRate = ((packed >> 44) & 0xFFFFF) / 1048575.0;
- double fallbackRate = ((packed >> 24) & 0xFFFFF) / 1048575.0;
- long avgLatency = (long)(packed & 0xFFFFFF);
-
- // Calculate overall score with optimized formula
- double baseScore = successRate * 0.6 + (1.0 - fallbackRate) * 0.4;
- double latencyPenalty = avgLatency > 0 ? Math.Min(0.1, avgLatency / 10000.0) : 0;
- double overallScore = baseScore - latencyPenalty;
-
- return (successRate, fallbackRate, overallScore);
- }
-
- }
- /**/
-
-}
\ No newline at end of file
diff --git a/StgSharp.Mathematics/Numeric/MatrixCompute.Basic.cs b/StgSharp.Mathematics/Numeric/MatrixCompute.Basic.cs
index cfdc1a55..9ec89dec 100644
--- a/StgSharp.Mathematics/Numeric/MatrixCompute.Basic.cs
+++ b/StgSharp.Mathematics/Numeric/MatrixCompute.Basic.cs
@@ -49,8 +49,8 @@ namespace StgSharp.Mathematics.Numeric
long count = (long)left.KernelColumnLength * left.KernelRowLength;
if (count <= 1024)
{
- CapacityFixedStack pool = MatrixParallel.LeadParallel();
- if (!pool.TryPop(out MatrixParallelThread thread))
+ ReadOnlySpan 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 pool = MatrixParallel.LeadParallel();
- if (!pool.TryPop(out MatrixParallelThread thread))
+ ReadOnlySpan pool = MatrixParallel.LeadParallel(1);
+ if (pool.Length == 0)
{
/*
* wait
diff --git a/StgSharp.Mathematics/Numeric/MatrixParallel.Publish.cs b/StgSharp.Mathematics/Numeric/MatrixParallel.Publish.cs
index 1e138806..47622543 100644
--- a/StgSharp.Mathematics/Numeric/MatrixParallel.Publish.cs
+++ b/StgSharp.Mathematics/Numeric/MatrixParallel.Publish.cs
@@ -820,9 +820,15 @@ namespace StgSharp.Mathematics.Numeric
int tileWidth = (1024 / secCount) + 1;
int tileCount = (primCount + tileWidth - 1) / tileWidth;
- CapacityFixedStack pool = LeadParallel();
+ ReadOnlySpan 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 taskBunch = new Stack(threadCount);
MatrixParallelTaskPackage* 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;
}
diff --git a/StgSharp.Mathematics/Numeric/MatrixParallel.cs b/StgSharp.Mathematics/Numeric/MatrixParallel.cs
index 4e1977a7..0ff8b4c9 100644
--- a/StgSharp.Mathematics/Numeric/MatrixParallel.cs
+++ b/StgSharp.Mathematics/Numeric/MatrixParallel.cs
@@ -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 _threads;
+ private static ConcurrentCapacityFixedStack _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 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 pool)
+ public static void ReleaseParallelLeadership(ref MatrixParallelThread[] pool)
{
#if NET9_0_OR_GREATER
#else
diff --git a/StgSharp.Mathematics/Numeric/MatrixParallelPool.cs b/StgSharp.Mathematics/Numeric/MatrixParallelPool.cs
index 0370dfaa..c5efa236 100644
--- a/StgSharp.Mathematics/Numeric/MatrixParallelPool.cs
+++ b/StgSharp.Mathematics/Numeric/MatrixParallelPool.cs
@@ -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 wrapSpan)
- {
- SpareWrap = CapacityFixedStackBuilder.Create(wrapSpan);
- SingleThreadAllocation = new CapacityFixedBag(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 wrapSpan)
+ private MatrixParallelThread[] _threads;
+ private bool _disposed = false;
+ private CapacityFixedStack _wraps;
+
+ internal MatrixParallelHandle(MatrixParallelThread[] threadArray)
{
- SpareWrap = wrapSpan;
- SingleThreadAllocation = new CapacityFixedBag(wrapSpan.Count);
- SpareThreadCount = threadCount;
+ _threads = threadArray;
+ IsMultithread = threadArray.Length > 1;
+ SliceToWraps();
}
- public int SpareThreadCount { get; set; }
-
- internal CapacityFixedBag SingleThreadAllocation { get; set; }
-
- internal CapacityFixedStack SpareWrap { get; set; }
-
- /*
- public void ReturnIndividualHandle(ref MatrixParallelWrap.Handle handle)
+ public ReadOnlySpan 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 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);
+ }
+
}
}
diff --git a/StgSharp.Mathematics/Numeric/MatrixParallelWrap.cs b/StgSharp.Mathematics/Numeric/MatrixParallelWrap.cs
index 21e4cbbb..16bb7bfb 100644
--- a/StgSharp.Mathematics/Numeric/MatrixParallelWrap.cs
+++ b/StgSharp.Mathematics/Numeric/MatrixParallelWrap.cs
@@ -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()
{
diff --git a/StgSharpDebug/Program.cs b/StgSharpDebug/Program.cs
index b583fc05..38db3ab8 100644
--- a/StgSharpDebug/Program.cs
+++ b/StgSharpDebug/Program.cs
@@ -92,7 +92,8 @@ namespace StgSharp
if (stack.TryPop(out int v))
{
popped.Add(v);
- Console.WriteLine($"pop: {v}");
+
+ // Console.WriteLine($"pop: {v}");
} else
{
Thread.Yield();
@@ -111,7 +112,7 @@ namespace StgSharp
ready.Signal();
startGate.Wait();
- int begin = captured * perProducer;
+ int begin = captured * perProducer + 10 * perProducer;
for (int j = 0; j < perProducer; j++)
{
stack.Push(begin + j);
@@ -146,12 +147,13 @@ namespace StgSharp
}
// Gather remaining data
- List remaining = new System.Collections.Generic.List();
+ List remaining = [];
while (stack.TryPop(out int value)) {
remaining.Add(value);
}
Console.WriteLine($"Popped count: {popped.Count}");
+ /**/
Console.WriteLine("Popped values:");
foreach (int v in popped)
{
@@ -159,7 +161,7 @@ namespace StgSharp
Console.Write('\t');
}
Console.WriteLine();
-
+ /**/
Console.WriteLine($"Remaining count: {remaining.Count}");
Console.WriteLine("Remaining values:");
foreach (int v in remaining)
From f959339e1861e7a7e2713b759a0792ac1237e191 Mon Sep 17 00:00:00 2001
From: Youmu Liu <105042599+Nitload-NSI@users.noreply.github.com>
Date: Fri, 7 Nov 2025 18:22:50 +0800
Subject: [PATCH 3/3] working on dynamic wrapping, a good awaiting is on way
---
.../Right Margin Line/Right Margin Line.xml | 2 +-
.../Editor/Formatting/Wrapping.CSharp.xml | 4 +-
.../Collections/CapacityFixedStack.cs | 3 +
.../Internal/Intrinsic/MatInterface.cs | 35 --
.../Mathematics/Graphic/Matrix44.cs | 110 +++---
StgSharp.Common/Mathematics/Graphic/Vec4.cs | 32 +-
StgSharp.Common/Mathematics/Linear/IMatrix.cs | 1 +
.../Numeric/MatrixCompute.Basic.cs | 50 +--
.../Numeric/MatrixParallel.Await.cs | 116 ++++++
.../Numeric/MatrixParallel.Publish.cs | 329 +++++++++---------
.../Numeric/MatrixParallel.cs | 35 +-
...arallelTask.cs => MatrixParallelHandle.cs} | 0
.../Numeric/MatrixParallelPool.cs | 32 +-
.../Numeric/MatrixParallelThread.cs | 45 ++-
.../Numeric/MatrixParallelWrap.cs | 6 +-
StgSharpDebug/Program.cs | 2 +
16 files changed, 447 insertions(+), 355 deletions(-)
delete mode 100644 StgSharp.Common/Internal/Intrinsic/MatInterface.cs
create mode 100644 StgSharp.Mathematics/Numeric/MatrixParallel.Await.cs
rename StgSharp.Mathematics/Numeric/{MatrixParallelTask.cs => MatrixParallelHandle.cs} (100%)
diff --git a/.cr/personal/Editor/Right Margin Line/Right Margin Line.xml b/.cr/personal/Editor/Right Margin Line/Right Margin Line.xml
index b18e300f..4f8bf4f3 100644
--- a/.cr/personal/Editor/Right Margin Line/Right Margin Line.xml
+++ b/.cr/personal/Editor/Right Margin Line/Right Margin Line.xml
@@ -2,7 +2,7 @@
True
-
+
2
\ No newline at end of file
diff --git a/.cr/team/Editor/Formatting/Wrapping.CSharp.xml b/.cr/team/Editor/Formatting/Wrapping.CSharp.xml
index ee502938..8b5b7c97 100644
--- a/.cr/team/Editor/Formatting/Wrapping.CSharp.xml
+++ b/.cr/team/Editor/Formatting/Wrapping.CSharp.xml
@@ -11,11 +11,13 @@
-
-
+
+ WrapAfter
+ 4
diff --git a/StgSharp.Common/Collections/CapacityFixedStack.cs b/StgSharp.Common/Collections/CapacityFixedStack.cs
index 002e91db..e8389de7 100644
--- a/StgSharp.Common/Collections/CapacityFixedStack.cs
+++ b/StgSharp.Common/Collections/CapacityFixedStack.cs
@@ -29,6 +29,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
+using System.Threading;
namespace StgSharp.Collections
{
@@ -45,6 +46,8 @@ namespace StgSharp.Collections
_index = -1;
}
+ public T? First => Volatile.Read(ref _index) > 0 ? _values[0] : default;
+
public bool IsEmpty => _index == -1;
public int Count => _index + 1;
diff --git a/StgSharp.Common/Internal/Intrinsic/MatInterface.cs b/StgSharp.Common/Internal/Intrinsic/MatInterface.cs
deleted file mode 100644
index d2033627..00000000
--- a/StgSharp.Common/Internal/Intrinsic/MatInterface.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-//-----------------------------------------------------------------------
-// -----------------------------------------------------------------------
-// file="MatInterface"
-// Project: StgSharp
-// AuthorGroup: Nitload
-// Copyright (c) Nitload. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// -----------------------------------------------------------------------
-// -----------------------------------------------------------------------
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace StgSharp.Internal.Intrinsic
-{
- public interface IMat { }
-}
diff --git a/StgSharp.Common/Mathematics/Graphic/Matrix44.cs b/StgSharp.Common/Mathematics/Graphic/Matrix44.cs
index e747948e..dc37b507 100644
--- a/StgSharp.Common/Mathematics/Graphic/Matrix44.cs
+++ b/StgSharp.Common/Mathematics/Graphic/Matrix44.cs
@@ -27,14 +27,17 @@
// -----------------------------------------------------------------------
using StgSharp.Mathematics.Numeric;
using System;
+using System.Collections;
+using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace StgSharp.Mathematics.Graphic
{
- [StructLayout(LayoutKind.Explicit, Size = 2 * 16 * sizeof(float) + sizeof(bool), Pack = 16)]
- public unsafe struct Matrix44 : IEquatable, IMatrix
+ [CollectionBuilder(builderType:typeof(Matrix44), methodName: nameof(Matrix44.FromSpan))]
+ [StructLayout(LayoutKind.Explicit, Size = (2 * 16 * sizeof(float)) + sizeof(bool), Pack = 16)]
+ public unsafe struct Matrix44 : IEquatable, IMatrix, IEnumerable
{
[FieldOffset(2 * 64 * sizeof(float))] internal bool isTransposed;
@@ -160,12 +163,6 @@ namespace StgSharp.Mathematics.Graphic
return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref mat), 16);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public unsafe float Det()
- {
- return 0;
- }
-
public override bool Equals(object? obj)
{
return obj is Matrix44 x && Equals(x);
@@ -176,11 +173,24 @@ namespace StgSharp.Mathematics.Graphic
return mat.Equals(other.mat);
}
+ public static Matrix44 FromSpan(ReadOnlySpan rowSpan)
+ {
+ Matrix44 mat = new Matrix44();
+
+ mat.transpose.colum0 = rowSpan[0].vec;
+ mat.transpose.colum1 = rowSpan[1].vec;
+ mat.transpose.colum2 = rowSpan[2].vec;
+ mat.transpose.colum3 = rowSpan[3].vec;
+
+ NativeIntrinsic.Intrinsic.f32_transpose(&mat.transpose, &mat.mat);
+ mat.isTransposed = false;
+ return mat;
+ }
+
public override int GetHashCode()
{
- return HashCode.Combine(
- mat.colum0.GetHashCode(), mat.colum1.GetHashCode(), mat.colum2.GetHashCode(),
- mat.colum3.GetHashCode());
+ return HashCode.Combine(mat.colum0.GetHashCode(), mat.colum1.GetHashCode(), mat.colum2.GetHashCode(),
+ mat.colum3.GetHashCode());
}
public override string ToString()
@@ -194,11 +204,8 @@ namespace StgSharp.Mathematics.Graphic
{
if (!isTransposed)
{
- fixed (ColumnSet4* source = &mat, target = &transpose)
- {
+ fixed (ColumnSet4* source = &mat, target = &transpose) {
NativeIntrinsic.Intrinsic.f32_transpose(source, target);
-
- // InternalIO.Intrinsic.transpose44(source, target);
}
isTransposed = true;
}
@@ -207,13 +214,9 @@ namespace StgSharp.Mathematics.Graphic
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe Matrix44 operator -(Matrix44 left, Matrix44 right)
{
- return new Matrix44
- {
- column0 = right.column0 - left.column0,
- column1 = right.column1 - left.column1,
- column2 = right.column2 - left.column2,
- column3 = right.column3 - left.column3
- };
+ Matrix44 ret = new();
+ NativeIntrinsic.Intrinsic.f32_sub(&left, &right, &ret);
+ return ret;
}
public static bool operator !=(Matrix44 left, Matrix44 right)
@@ -224,47 +227,37 @@ namespace StgSharp.Mathematics.Graphic
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe Matrix44 operator *(Matrix44 mat, float value)
{
- return new Matrix44(
- mat.mat.colum0 * value, mat.mat.colum1 * value, mat.mat.colum2 * value,
- mat.mat.colum3 * value);
+ return new Matrix44(mat.mat.colum0 * value, mat.mat.colum1 * value, mat.mat.colum2 * value,
+ mat.mat.colum3 * value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix44 operator *(Matrix44 left, Matrix44 right)
{
left.InternalTranspose();
- return new Matrix44(
- Vector4.Dot(left.transpose.colum0, right.mat.colum0),
- Vector4.Dot(left.transpose.colum0, right.mat.colum1),
- Vector4.Dot(left.transpose.colum0, right.mat.colum2),
- Vector4.Dot(left.transpose.colum0, right.mat.colum3),
-
- Vector4.Dot(left.transpose.colum1, right.mat.colum0),
- Vector4.Dot(left.transpose.colum1, right.mat.colum1),
- Vector4.Dot(left.transpose.colum1, right.mat.colum2),
- Vector4.Dot(left.transpose.colum1, right.mat.colum3),
-
- Vector4.Dot(left.transpose.colum2, right.mat.colum0),
- Vector4.Dot(left.transpose.colum2, right.mat.colum1),
- Vector4.Dot(left.transpose.colum2, right.mat.colum2),
- Vector4.Dot(left.transpose.colum2, right.mat.colum3),
-
- Vector4.Dot(left.transpose.colum3, right.mat.colum0),
- Vector4.Dot(left.transpose.colum3, right.mat.colum1),
- Vector4.Dot(left.transpose.colum3, right.mat.colum2),
- Vector4.Dot(left.transpose.colum3, right.mat.colum3));
+ return new Matrix44(Vector4.Dot(left.transpose.colum0, right.mat.colum0),
+ Vector4.Dot(left.transpose.colum0, right.mat.colum1),
+ Vector4.Dot(left.transpose.colum0, right.mat.colum2),
+ Vector4.Dot(left.transpose.colum0, right.mat.colum3),
+ Vector4.Dot(left.transpose.colum1, right.mat.colum0),
+ Vector4.Dot(left.transpose.colum1, right.mat.colum1),
+ Vector4.Dot(left.transpose.colum1, right.mat.colum2),
+ Vector4.Dot(left.transpose.colum1, right.mat.colum3),
+ Vector4.Dot(left.transpose.colum2, right.mat.colum0),
+ Vector4.Dot(left.transpose.colum2, right.mat.colum1),
+ Vector4.Dot(left.transpose.colum2, right.mat.colum2),
+ Vector4.Dot(left.transpose.colum2, right.mat.colum3),
+ Vector4.Dot(left.transpose.colum3, right.mat.colum0),
+ Vector4.Dot(left.transpose.colum3, right.mat.colum1),
+ Vector4.Dot(left.transpose.colum3, right.mat.colum2),
+ Vector4.Dot(left.transpose.colum3, right.mat.colum3));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe Matrix44 operator +(Matrix44 left, Matrix44 right)
{
- Matrix44 ret = new()
- {
- column0 = left.column0 + right.column0,
- column1 = left.column1 + right.column1,
- column2 = left.column2 + right.column2,
- column3 = left.column3 + right.column3
- };
+ Matrix44 ret = new();
+ NativeIntrinsic.Intrinsic.f32_add(&left, &right, &ret);
return ret;
}
@@ -273,5 +266,18 @@ namespace StgSharp.Mathematics.Graphic
return left.Equals(right);
}
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ throw new NotImplementedException();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ ref Vec4 col0 = ref column0;
+ for (int i = 0; i < 4; i++) {
+ yield return Unsafe.Add(ref col0, i);
+ }
+ }
+
}
}
diff --git a/StgSharp.Common/Mathematics/Graphic/Vec4.cs b/StgSharp.Common/Mathematics/Graphic/Vec4.cs
index b4186d61..44edf4ae 100644
--- a/StgSharp.Common/Mathematics/Graphic/Vec4.cs
+++ b/StgSharp.Common/Mathematics/Graphic/Vec4.cs
@@ -36,8 +36,9 @@ using System.Runtime.InteropServices;
namespace StgSharp.Mathematics.Graphic
{
+ [CollectionBuilder(builderType:typeof(Vec4), methodName: nameof(Vec4.FromSpan))]
[StructLayout(LayoutKind.Explicit, Size = 16, Pack = 16)]
- public struct Vec4 : IEquatable, IUnmanagedVector
+ public unsafe struct Vec4 : IEquatable, IUnmanagedVector
{
[FieldOffset(0)] internal unsafe fixed float num[4];
@@ -127,11 +128,33 @@ namespace StgSharp.Mathematics.Graphic
return left + right;
}
+ public ReadOnlySpan AsSpan()
+ {
+ return MemoryMarshal.CreateReadOnlySpan(ref reg.Member(0), 4);
+ }
+
public override bool Equals(object obj)
{
return obj is Vec4 v && v == this;
}
+ public static Vec4 FromSpan(ReadOnlySpan span)
+ {
+ return new Vec4
+ {
+ X = span[0],
+ Y = span[1],
+ Z = span[2],
+ W = span[3]
+ };
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlySpan.Enumerator GetEnumerator()
+ {
+ return AsSpan().GetEnumerator();
+ }
+
public override int GetHashCode()
{
return vec.GetHashCode();
@@ -163,11 +186,8 @@ namespace StgSharp.Mathematics.Graphic
public static Vec4 operator *(Matrix44 mat, Vec4 vec)
{
mat.InternalTranspose();
- return new Vec4(
- Vector4.Dot(mat.transpose.colum0, vec.vec),
- Vector4.Dot(mat.transpose.colum1, vec.vec),
- Vector4.Dot(mat.transpose.colum2, vec.vec),
- Vector4.Dot(mat.transpose.colum3, vec.vec));
+ return new Vec4(Vector4.Dot(mat.transpose.colum0, vec.vec), Vector4.Dot(mat.transpose.colum1, vec.vec),
+ Vector4.Dot(mat.transpose.colum2, vec.vec), Vector4.Dot(mat.transpose.colum3, vec.vec));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/StgSharp.Common/Mathematics/Linear/IMatrix.cs b/StgSharp.Common/Mathematics/Linear/IMatrix.cs
index e19a0f81..f626e927 100644
--- a/StgSharp.Common/Mathematics/Linear/IMatrix.cs
+++ b/StgSharp.Common/Mathematics/Linear/IMatrix.cs
@@ -30,6 +30,7 @@ using StgSharp.Graphics;
using System;
using System.Collections.Generic;
using System.Numerics;
+using System.Runtime.CompilerServices;
using System.Text;
namespace StgSharp.Mathematics.Numeric
diff --git a/StgSharp.Mathematics/Numeric/MatrixCompute.Basic.cs b/StgSharp.Mathematics/Numeric/MatrixCompute.Basic.cs
index 9ec89dec..5dd527ac 100644
--- a/StgSharp.Mathematics/Numeric/MatrixCompute.Basic.cs
+++ b/StgSharp.Mathematics/Numeric/MatrixCompute.Basic.cs
@@ -47,6 +47,16 @@ namespace StgSharp.Mathematics.Numeric
}
Matrix ans = Matrix.FromDefault(left.ColumnLength, left.RowLength);
long count = (long)left.KernelColumnLength * left.KernelRowLength;
+ if (count <= 256)
+ {
+ MatrixKernel* leftPtr = left.Buffer;
+ MatrixKernel* rightPtr = right.Buffer;
+ MatrixKernel* ansPtr = ans.Buffer;
+ for (int i = 0; i < count; i++) {
+ NativeIntrinsic.Intrinsic.f32_add(leftPtr + i, rightPtr + i, ansPtr + i);
+ }
+ return ans;
+ }
if (count <= 1024)
{
ReadOnlySpan pool = MatrixParallel.LeadParallel(1);
@@ -64,50 +74,14 @@ namespace StgSharp.Mathematics.Numeric
}
return ans;
}
- Stack taskGroup = MatrixParallel.PublicTask(left.GetColumnEnumeration(), right.GetColumnEnumeration(), ans.GetColumnEnumeration(),
+ MatrixParallelHandle taskGroup = MatrixParallel.PublicTask(left.GetColumnEnumeration(), right.GetColumnEnumeration(), ans.GetColumnEnumeration(),
(delegate**, MatrixKernel*, MatrixKernel*, void>)NativeIntrinsic.Intrinsic.f32_add);
MatrixParallel.LaunchParallel(taskGroup, SleepMode.DeepSleep);
/*
* run parallel
*/
- return ans;
- }
-
- public static unsafe Matrix Sub(Matrix left, Matrix right)
- {
- if (left.ColumnLength != right.ColumnLength || left.RowLength != right.RowLength) {
- throw new IndexOutOfRangeException("Matrix dimensions must agree.");
- }
- Matrix ans = Matrix.FromDefault(left.ColumnLength, left.RowLength);
- long count = (long)left.KernelColumnLength * left.KernelRowLength;
- if (count <= 1024)
- {
- // TODO: this is very awkward, blocking thread renting, need a better way to handle small tasks
- ReadOnlySpan pool = MatrixParallel.LeadParallel(1);
- if (pool.Length == 0)
- {
- /*
- * wait
- */
- }
- MatrixParallel.ReleaseParallelLeadership(ref pool);
- MatrixKernel* leftPtr = left.Buffer;
- MatrixKernel* rightPtr = right.Buffer;
- MatrixKernel* ansPtr = ans.Buffer;
- for (int i = 0; i < count; i++) {
- NativeIntrinsic.Intrinsic.f32_sub(leftPtr + i, rightPtr + i, ansPtr + i);
- }
-
- // thread.
-
- return ans;
- }
- Stack taskGroup = MatrixParallel.PublicTask(left.GetColumnEnumeration(), right.GetColumnEnumeration(), ans.GetColumnEnumeration(),
- (delegate**, MatrixKernel*, MatrixKernel*, void>)NativeIntrinsic.Intrinsic.f32_sub);
- /*
- * run parallel
- */
+ taskGroup.Dispose();
return ans;
}
diff --git a/StgSharp.Mathematics/Numeric/MatrixParallel.Await.cs b/StgSharp.Mathematics/Numeric/MatrixParallel.Await.cs
new file mode 100644
index 00000000..a64d2b77
--- /dev/null
+++ b/StgSharp.Mathematics/Numeric/MatrixParallel.Await.cs
@@ -0,0 +1,116 @@
+//-----------------------------------------------------------------------
+// -----------------------------------------------------------------------
+// file="MatrixParallel.Await"
+// Project: StgSharp
+// AuthorGroup: Nitload
+// Copyright (c) Nitload. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+// -----------------------------------------------------------------------
+// -----------------------------------------------------------------------
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace StgSharp.Mathematics.Numeric
+{
+ public static partial class MatrixParallel
+ {
+
+ private static ConcurrentQueue _awaitingLeaderHandle;
+
+ private static volatile int _createGeneration = 0;
+ private static int _sortState;
+
+ public static MatrixParallelThread[] LeadParallel(int recommendCount)
+ {
+ int min = int.Min(recommendCount, _threads.Capacity);
+ min /= 2;
+ MatrixParallelThread[] ret;
+ if (_threads.Count < min)
+ {
+ WaitHandle handle = new WaitHandle
+ {
+ CreateGeneration = _createGeneration,
+ LeastCount = min,
+ BestCount = recommendCount
+ };
+ _awaitingLeaderHandle.Enqueue(handle);
+ int cycleGeneration = _createGeneration;
+ while (true)
+ {
+ // TODO 排队等待,对前八个按照优先级排序,优先级最高的独占进行等待,其他继续排队
+ if (_createGeneration != cycleGeneration)
+ {
+ if (Interlocked.Exchange(ref _sortState, 1) == 0)
+ {
+ // TODO 排序
+ _ = Interlocked.Exchange(ref _sortState, 0);
+ } else
+ {
+ cycleGeneration = _createGeneration;
+ continue;
+ }
+ }
+ if (_awaitingLeaderHandle.TryPeek(out WaitHandle? p) && p == handle)
+ {
+ // get the leader
+ ret = _threads.PopRange(recommendCount);
+ _awaitingLeaderHandle.TryDequeue(out _);
+ break;
+ }
+ Thread.Sleep(0);
+ }
+ } else
+ {
+ // directly get the leader
+ ret = _threads.PopRange(recommendCount);
+ }
+ _ = Interlocked.Increment(ref _createGeneration);
+ return ret;
+ }
+
+ public static void ReturnParallelResources(ref MatrixParallelThread[] pool)
+ {
+ _threads.PushRange(pool);
+ pool = null!;
+ }
+
+ private record class WaitHandle
+ {
+
+ public int CreateGeneration { get; set; }
+
+ public int LeastCount { get; set; }
+
+ public int BestCount { get; set; }
+
+ public int CurrentPriority(int currentGeneration)
+ {
+ return (currentGeneration - CreateGeneration) * (MatrixParallel._threads.Capacity - LeastCount);
+ }
+
+ }
+
+ }
+}
diff --git a/StgSharp.Mathematics/Numeric/MatrixParallel.Publish.cs b/StgSharp.Mathematics/Numeric/MatrixParallel.Publish.cs
index 47622543..eda3cf23 100644
--- a/StgSharp.Mathematics/Numeric/MatrixParallel.Publish.cs
+++ b/StgSharp.Mathematics/Numeric/MatrixParallel.Publish.cs
@@ -40,10 +40,10 @@ namespace StgSharp.Mathematics.Numeric
public static partial class MatrixParallel
{
- public static unsafe Stack PublicTask(
- MatrixSegmentEnumeration source,
- MatrixSegmentEnumeration ans,
- delegate**, MatrixKernel*, MatrixKernel*, void> Operation)
+ public static unsafe MatrixParallelHandle PublicTask(
+ MatrixSegmentEnumeration source,
+ MatrixSegmentEnumeration ans,
+ delegate**, MatrixKernel*, MatrixKernel*, void> Operation)
where T: unmanaged, INumber
{
MatrixParallelTaskPackage* package = MatrixParallelFactory.CreateBaseTask();
@@ -66,11 +66,11 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate(package);
}
- public static unsafe Stack PublicTask(
- MatrixSegmentEnumeration left,
- MatrixSegmentEnumeration right,
- MatrixSegmentEnumeration ans,
- delegate**, MatrixKernel*, MatrixKernel*, void> Operation)
+ public static unsafe MatrixParallelHandle PublicTask(
+ MatrixSegmentEnumeration left,
+ MatrixSegmentEnumeration right,
+ MatrixSegmentEnumeration ans,
+ delegate**, MatrixKernel*, MatrixKernel*, void> Operation)
where T: unmanaged, INumber
{
MatrixParallelTaskPackage* package = MatrixParallelFactory.CreateBaseTask();
@@ -99,11 +99,11 @@ namespace StgSharp.Mathematics.Numeric
}
// Unary with 1..8 scalars (each scalar is independent generic type)
- public static unsafe Stack PublicTask(
- MatrixSegmentEnumeration source,
- MatrixSegmentEnumeration ans,
- delegate**, MatrixKernel*, MatrixKernel*, void> Operation,
- TScalar0 s0)
+ public static unsafe MatrixParallelHandle PublicTask(
+ MatrixSegmentEnumeration source,
+ MatrixSegmentEnumeration ans,
+ delegate**, MatrixKernel*, MatrixKernel*, void> Operation,
+ TScalar0 s0)
where T: unmanaged, INumber
where TScalar0: unmanaged, INumber
{
@@ -130,12 +130,12 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate(package);
}
- public static unsafe Stack PublicTask(
- MatrixSegmentEnumeration source,
- MatrixSegmentEnumeration ans,
- delegate**, MatrixKernel*, MatrixKernel*, void> Operation,
- TScalar0 s0,
- TScalar1 s1)
+ public static unsafe MatrixParallelHandle PublicTask(
+ MatrixSegmentEnumeration source,
+ MatrixSegmentEnumeration ans,
+ delegate**, MatrixKernel*, MatrixKernel*, void> Operation,
+ TScalar0 s0,
+ TScalar1 s1)
where T: unmanaged, INumber
where TScalar0: unmanaged, INumber
where TScalar1: unmanaged, INumber
@@ -165,12 +165,12 @@ namespace StgSharp.Mathematics.Numeric
}
// Binary with 1..8 scalars (each scalar is independent generic type)
- public static unsafe Stack PublicTask(
- MatrixSegmentEnumeration left,
- MatrixSegmentEnumeration right,
- MatrixSegmentEnumeration ans,
- delegate**, MatrixKernel*, MatrixKernel*, void> Operation,
- TScalar0 s0)
+ public static unsafe MatrixParallelHandle PublicTask(
+ MatrixSegmentEnumeration left,
+ MatrixSegmentEnumeration right,
+ MatrixSegmentEnumeration ans,
+ delegate**, MatrixKernel*, MatrixKernel*, void> Operation,
+ TScalar0 s0)
where T: unmanaged, INumber
where TScalar0: unmanaged, INumber
{
@@ -202,13 +202,13 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate(package);
}
- public static unsafe Stack PublicTask(
- MatrixSegmentEnumeration source,
- MatrixSegmentEnumeration ans,
- delegate**, MatrixKernel*, MatrixKernel*, void> Operation,
- TScalar0 s0,
- TScalar1 s1,
- TScalar2 s2)
+ public static unsafe MatrixParallelHandle PublicTask(
+ MatrixSegmentEnumeration source,
+ MatrixSegmentEnumeration ans,
+ delegate**, MatrixKernel*, MatrixKernel*, void> Operation,
+ TScalar0 s0,
+ TScalar1 s1,
+ TScalar2 s2)
where T: unmanaged, INumber
where TScalar0: unmanaged, INumber
where TScalar1: unmanaged, INumber
@@ -239,13 +239,13 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate(package);
}
- public static unsafe Stack PublicTask(
- MatrixSegmentEnumeration left,
- MatrixSegmentEnumeration right,
- MatrixSegmentEnumeration ans,
- delegate**, MatrixKernel*, MatrixKernel*, void> Operation,
- TScalar0 s0,
- TScalar1 s1)
+ public static unsafe MatrixParallelHandle PublicTask(
+ MatrixSegmentEnumeration left,
+ MatrixSegmentEnumeration right,
+ MatrixSegmentEnumeration ans,
+ delegate**, MatrixKernel*, MatrixKernel*, void> Operation,
+ TScalar0 s0,
+ TScalar1 s1)
where T: unmanaged, INumber
where TScalar0: unmanaged, INumber
where TScalar1: unmanaged, INumber
@@ -279,14 +279,14 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate(package);
}
- public static unsafe Stack PublicTask(
- MatrixSegmentEnumeration source,
- MatrixSegmentEnumeration ans,
- delegate**, MatrixKernel*, MatrixKernel*, void> Operation,
- TScalar0 s0,
- TScalar1 s1,
- TScalar2 s2,
- TScalar3 s3)
+ public static unsafe MatrixParallelHandle PublicTask(
+ MatrixSegmentEnumeration source,
+ MatrixSegmentEnumeration ans,
+ delegate**, MatrixKernel*, MatrixKernel*, void> Operation,
+ TScalar0 s0,
+ TScalar1 s1,
+ TScalar2 s2,
+ TScalar3 s3)
where T: unmanaged, INumber
where TScalar0: unmanaged, INumber
where TScalar1: unmanaged, INumber
@@ -319,14 +319,14 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate(package);
}
- public static unsafe Stack PublicTask(
- MatrixSegmentEnumeration left,
- MatrixSegmentEnumeration right,
- MatrixSegmentEnumeration ans,
- delegate**, MatrixKernel*, MatrixKernel*, void> Operation,
- TScalar0 s0,
- TScalar1 s1,
- TScalar2 s2)
+ public static unsafe MatrixParallelHandle PublicTask(
+ MatrixSegmentEnumeration left,
+ MatrixSegmentEnumeration right,
+ MatrixSegmentEnumeration ans,
+ delegate**, MatrixKernel*, MatrixKernel*, void> Operation,
+ TScalar0 s0,
+ TScalar1 s1,
+ TScalar2 s2)
where T: unmanaged, INumber
where TScalar0: unmanaged, INumber
where TScalar1: unmanaged, INumber