Merge branch 'giga' of ssh://ssh.github.com:443/Nitload-NSI/CCD_Github into local

merge newest code to main branch
This commit is contained in:
Nitload
2025-11-08 21:46:17 +08:00
21 changed files with 1049 additions and 999 deletions
@@ -2,7 +2,7 @@
<Root Type="DevExpress.CodeRush.VisualizePlugins.RightMarginLine.Options.RightMarginLineOptions"> <Root Type="DevExpress.CodeRush.VisualizePlugins.RightMarginLine.Options.RightMarginLineOptions">
<Options Language="Neutral"> <Options Language="Neutral">
<Enabled>True</Enabled> <Enabled>True</Enabled>
<Style>DashDot</Style> <Style>Dot</Style>
<Width>2</Width> <Width>2</Width>
</Options> </Options>
</Root> </Root>
@@ -11,11 +11,13 @@
<Item Id="{10e904ea-f51d-4c32-9320-1b93fa557564}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.AssignmentExpressionFormattingOption" /> <Item Id="{10e904ea-f51d-4c32-9320-1b93fa557564}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.AssignmentExpressionFormattingOption" />
<Item Id="{25746bdc-0a43-4741-a740-20784691314b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.InvocationArgumentListFormattingOption"> <Item Id="{25746bdc-0a43-4741-a740-20784691314b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.InvocationArgumentListFormattingOption">
<WrapOpenParen> <WrapOpenParen>
<Option>WrapIfLong</Option> <Option>NoWrap</Option>
</WrapOpenParen> </WrapOpenParen>
<WrapArgSeparator> <WrapArgSeparator>
<Option>SimpleWrap</Option> <Option>SimpleWrap</Option>
<Kind>WrapAfter</Kind>
</WrapArgSeparator> </WrapArgSeparator>
<ArgCountTolerance>4</ArgCountTolerance>
</Item> </Item>
<Item Id="{D5EE2ECB-9FA6-47F7-A11F-007FC45E872D}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.AttributeArgumentsFormattingOption" /> <Item Id="{D5EE2ECB-9FA6-47F7-A11F-007FC45E872D}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.AttributeArgumentsFormattingOption" />
<Item Id="{5A186AB9-03A5-4BFE-83B1-B99B3B8408BC}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.DeclarationParameterListFormattingOption" /> <Item Id="{5A186AB9-03A5-4BFE-83B1-B99B3B8408BC}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.DeclarationParameterListFormattingOption" />
@@ -29,6 +29,7 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading;
namespace StgSharp.Collections namespace StgSharp.Collections
{ {
@@ -45,6 +46,8 @@ namespace StgSharp.Collections
_index = -1; _index = -1;
} }
public T? First => Volatile.Read(ref _index) > 0 ? _values[0] : default;
public bool IsEmpty => _index == -1; public bool IsEmpty => _index == -1;
public int Count => _index + 1; public int Count => _index + 1;
@@ -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
{
/// <summary>
/// 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
/// </summary>
public class ConcurrentCapacityFixedStack<T> : IEnumerable<T>
{
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<T> 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 [];
}
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 [];
}
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<T> span)
{
for (int i = 0; i < span.Length; i++) {
Push(span[i]);
}
}
/// <summary>
/// Best-effort snapshot for enumeration; not strongly consistent under concurrent
/// mutations.
/// </summary>
public T[] ToArraySnapshot()
{
int expected = Math.Min(Volatile.Read(ref _count), _capacity);
if (expected == 0) {
return Array.Empty<T>();
}
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<T> 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();
}
}
@@ -47,7 +47,7 @@ namespace StgSharp.HighPerformance.Memory
return new BufferStack<T>(4); // Default small capacity return new BufferStack<T>(4); // Default small capacity
} }
BufferStack<T> stack = new BufferStack<T>(span.Length); BufferStack<T> stack = new(span.Length);
// Use internal method for efficient bulk initialization // Use internal method for efficient bulk initialization
@@ -65,7 +65,7 @@ namespace StgSharp.HighPerformance.Memory
return new ConcurrentBufferStack<T>(4); // Default small capacity return new ConcurrentBufferStack<T>(4); // Default small capacity
} }
ConcurrentBufferStack<T> stack = new ConcurrentBufferStack<T>(span.Length); ConcurrentBufferStack<T> stack = new(span.Length);
// Use internal method for efficient bulk initialization // Use internal method for efficient bulk initialization
@@ -82,7 +82,7 @@ namespace StgSharp.HighPerformance.Memory
private byte* _buffer; private byte* _buffer;
private readonly BufferExpansionLock _lock = new(); private readonly BufferExpansionLock _lock = new();
private ulong _count; private long _count;
public ConcurrentBufferStack() public ConcurrentBufferStack()
{ {
@@ -101,17 +101,25 @@ namespace StgSharp.HighPerformance.Memory
public ulong Count public ulong Count
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Volatile.Read(ref _count); get => (ulong)Volatile.Read(ref _count);
} }
public void Clear() public void Clear()
{ {
Header* header = (Header*)_buffer;
_lock.EnterExpansionProcess(); _lock.EnterExpansionProcess();
try try
{ {
header->Top = 0; _lock.EnterBufferCopy(); // exclusive while mutating Top
Volatile.Write(ref _count, 0); try
{
Header* header = (Header*)_buffer;
header->Top = 0;
Volatile.Write(ref _count, 0);
}
finally
{
_lock.ExitBufferCopy();
}
} }
finally finally
{ {
@@ -127,29 +135,29 @@ namespace StgSharp.HighPerformance.Memory
public T Pop() public T Pop()
{ {
Header* header = (Header*)_buffer;
while (true) while (true)
{ {
nuint oldTop = Volatile.Read(ref header->Top); _lock.EnterBufferRead(); // prevent buffer being reallocated while reading/modifying Top
if (oldTop <= 0) { try
throw new InvalidOperationException("Stack underflow");
}
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
{ {
_lock.EnterBufferRead(); Header* header = (Header*)_buffer;
try int 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)); T* dataStart = (T*)(_buffer + sizeof(Header));
_ = Interlocked.Decrement(ref _count); _ = Interlocked.Decrement(ref _count);
return dataStart[oldTop - 1]; 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) while (true)
{ {
// Ensure capacity, expanding if needed
_lock.EnterMetaDataRead(); _lock.EnterMetaDataRead();
try try
{ {
Header* header = (Header*)_buffer; Header* header = (Header*)_buffer;
nuint oldTop = Volatile.Read(ref header->Top); int topSnapshot = Volatile.Read(ref header->Top);
nuint cap = Volatile.Read(ref header->Capacity); int cap = Volatile.Read(ref header->Capacity);
if (oldTop >= cap) if (topSnapshot >= cap)
{ {
// Need expansion
_lock.ExitMetaDataRead(); _lock.ExitMetaDataRead();
_lock.EnterExpansionProcess(); _lock.EnterExpansionProcess();
try try
@@ -172,8 +182,8 @@ namespace StgSharp.HighPerformance.Memory
header = (Header*)_buffer; header = (Header*)_buffer;
if (header->Top >= header->Capacity) if (header->Top >= header->Capacity)
{ {
nuint newCap = header->Capacity * 2; int newCap = header->Capacity * 2;
nuint newSize = (nuint)sizeof(Header) + (newCap * (nuint)sizeof(T)); nuint newSize = (nuint)sizeof(Header) + ((nuint)newCap * (nuint)sizeof(T));
_lock.EnterBufferCopy(); _lock.EnterBufferCopy();
try try
@@ -193,22 +203,6 @@ namespace StgSharp.HighPerformance.Memory
} }
continue; 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 finally
{ {
@@ -216,38 +210,60 @@ namespace StgSharp.HighPerformance.Memory
_lock.ExitMetaDataRead(); _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;
int 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) public bool TryPop(out T value)
{ {
Header* header = (Header*)_buffer;
while (true) while (true)
{ {
nuint oldTop = Volatile.Read(ref header->Top); _lock.EnterBufferRead();
if (oldTop <= 0) try
{ {
value = default; Header* header = (Header*)_buffer;
return false; int oldTop = Volatile.Read(ref header->Top);
} if (oldTop <= 0)
{
value = default;
return false;
}
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop) if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
{
_lock.EnterBufferRead();
try
{ {
T* dataStart = (T*)(_buffer + sizeof(Header)); T* dataStart = (T*)(_buffer + sizeof(Header));
value = dataStart[oldTop - 1]; value = dataStart[oldTop - 1];
_ = Interlocked.Decrement(ref _count); _ = Interlocked.Decrement(ref _count);
return true;
} }
finally
{
_lock.ExitBufferRead();
}
return true;
} }
Thread.Sleep(0); // Yield to avoid busy waiting finally
{
_lock.ExitBufferRead();
}
Thread.Sleep(0);
} }
} }
@@ -264,28 +280,28 @@ namespace StgSharp.HighPerformance.Memory
fixed (T* sourcePtr = span) { fixed (T* sourcePtr = span) {
Buffer.MemoryCopy(sourcePtr, dataStart, Buffer.MemoryCopy(sourcePtr, dataStart,
header->Capacity * (nuint)sizeof(T), (nuint)header->Capacity * (nuint)sizeof(T),
(nuint)span.Length * (nuint)sizeof(T)); (nuint)span.Length * (nuint)sizeof(T));
} }
header->Top = (nuint)span.Length; header->Top = span.Length;
Volatile.Write(ref _count, (ulong)span.Length); Volatile.Write(ref _count, span.Length);
} }
private void AllocateBuffer(int capacity) 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); _buffer = (byte*)NativeMemory.Alloc(size);
Header* header = (Header*)_buffer; Header* header = (Header*)_buffer;
header->Top = 0; header->Top = 0;
header->Capacity = (nuint)capacity; header->Capacity = capacity;
} }
private struct Header private struct Header
{ {
public nuint Capacity; // max capacity of the stack (number of elements) public int Capacity; // max capacity of the stack (number of elements)
public nuint Top; // next available index in the stack public int Top; // next available index in the stack
} }
+1 -1
View File
@@ -38,7 +38,7 @@ namespace StgSharp.Internal
internal static partial class GraphicFramework internal static partial class GraphicFramework
{ {
internal static uint[] GLtype = []; internal static uint[] GLtype = new uint[16];
static GraphicFramework() static GraphicFramework()
{ {
@@ -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 { }
}
+58 -52
View File
@@ -27,14 +27,17 @@
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using StgSharp.Mathematics.Numeric; using StgSharp.Mathematics.Numeric;
using System; using System;
using System.Collections;
using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace StgSharp.Mathematics.Graphic namespace StgSharp.Mathematics.Graphic
{ {
[StructLayout(LayoutKind.Explicit, Size = 2 * 16 * sizeof(float) + sizeof(bool), Pack = 16)] [CollectionBuilder(builderType:typeof(Matrix44), methodName: nameof(Matrix44.FromSpan))]
public unsafe struct Matrix44 : IEquatable<Matrix44>, IMatrix<float> [StructLayout(LayoutKind.Explicit, Size = (2 * 16 * sizeof(float)) + sizeof(bool), Pack = 16)]
public unsafe struct Matrix44 : IEquatable<Matrix44>, IMatrix<float>, IEnumerable<Vec4>
{ {
[FieldOffset(2 * 64 * sizeof(float))] internal bool isTransposed; [FieldOffset(2 * 64 * sizeof(float))] internal bool isTransposed;
@@ -160,12 +163,6 @@ namespace StgSharp.Mathematics.Graphic
return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<ColumnSet4, float>(ref mat), 16); return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<ColumnSet4, float>(ref mat), 16);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe float Det()
{
return 0;
}
public override bool Equals(object? obj) public override bool Equals(object? obj)
{ {
return obj is Matrix44 x && Equals(x); return obj is Matrix44 x && Equals(x);
@@ -176,11 +173,24 @@ namespace StgSharp.Mathematics.Graphic
return mat.Equals(other.mat); return mat.Equals(other.mat);
} }
public static Matrix44 FromSpan(ReadOnlySpan<Vec4> 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() public override int GetHashCode()
{ {
return HashCode.Combine( return HashCode.Combine(mat.colum0.GetHashCode(), mat.colum1.GetHashCode(), mat.colum2.GetHashCode(),
mat.colum0.GetHashCode(), mat.colum1.GetHashCode(), mat.colum2.GetHashCode(), mat.colum3.GetHashCode());
mat.colum3.GetHashCode());
} }
public override string ToString() public override string ToString()
@@ -194,11 +204,8 @@ namespace StgSharp.Mathematics.Graphic
{ {
if (!isTransposed) if (!isTransposed)
{ {
fixed (ColumnSet4* source = &mat, target = &transpose) fixed (ColumnSet4* source = &mat, target = &transpose) {
{
NativeIntrinsic.Intrinsic.f32_transpose(source, target); NativeIntrinsic.Intrinsic.f32_transpose(source, target);
// InternalIO.Intrinsic.transpose44(source, target);
} }
isTransposed = true; isTransposed = true;
} }
@@ -207,13 +214,9 @@ namespace StgSharp.Mathematics.Graphic
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe Matrix44 operator -(Matrix44 left, Matrix44 right) public static unsafe Matrix44 operator -(Matrix44 left, Matrix44 right)
{ {
return new Matrix44 Matrix44 ret = new();
{ NativeIntrinsic.Intrinsic.f32_sub(&left, &right, &ret);
column0 = right.column0 - left.column0, return ret;
column1 = right.column1 - left.column1,
column2 = right.column2 - left.column2,
column3 = right.column3 - left.column3
};
} }
public static bool operator !=(Matrix44 left, Matrix44 right) public static bool operator !=(Matrix44 left, Matrix44 right)
@@ -224,47 +227,37 @@ namespace StgSharp.Mathematics.Graphic
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe Matrix44 operator *(Matrix44 mat, float value) public static unsafe Matrix44 operator *(Matrix44 mat, float value)
{ {
return new Matrix44( return new Matrix44(mat.mat.colum0 * value, mat.mat.colum1 * value, mat.mat.colum2 * value,
mat.mat.colum0 * value, mat.mat.colum1 * value, mat.mat.colum2 * value, mat.mat.colum3 * value);
mat.mat.colum3 * value);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix44 operator *(Matrix44 left, Matrix44 right) public static Matrix44 operator *(Matrix44 left, Matrix44 right)
{ {
left.InternalTranspose(); left.InternalTranspose();
return new Matrix44( return new Matrix44(Vector4.Dot(left.transpose.colum0, right.mat.colum0),
Vector4.Dot(left.transpose.colum0, right.mat.colum0), Vector4.Dot(left.transpose.colum0, right.mat.colum1),
Vector4.Dot(left.transpose.colum0, right.mat.colum1), Vector4.Dot(left.transpose.colum0, right.mat.colum2),
Vector4.Dot(left.transpose.colum0, right.mat.colum2), Vector4.Dot(left.transpose.colum0, right.mat.colum3),
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.colum0), Vector4.Dot(left.transpose.colum1, right.mat.colum2),
Vector4.Dot(left.transpose.colum1, right.mat.colum1), Vector4.Dot(left.transpose.colum1, right.mat.colum3),
Vector4.Dot(left.transpose.colum1, right.mat.colum2), Vector4.Dot(left.transpose.colum2, right.mat.colum0),
Vector4.Dot(left.transpose.colum1, right.mat.colum3), Vector4.Dot(left.transpose.colum2, right.mat.colum1),
Vector4.Dot(left.transpose.colum2, right.mat.colum2),
Vector4.Dot(left.transpose.colum2, right.mat.colum0), Vector4.Dot(left.transpose.colum2, right.mat.colum3),
Vector4.Dot(left.transpose.colum2, right.mat.colum1), Vector4.Dot(left.transpose.colum3, right.mat.colum0),
Vector4.Dot(left.transpose.colum2, right.mat.colum2), Vector4.Dot(left.transpose.colum3, right.mat.colum1),
Vector4.Dot(left.transpose.colum2, right.mat.colum3), Vector4.Dot(left.transpose.colum3, right.mat.colum2),
Vector4.Dot(left.transpose.colum3, 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe Matrix44 operator +(Matrix44 left, Matrix44 right) public static unsafe Matrix44 operator +(Matrix44 left, Matrix44 right)
{ {
Matrix44 ret = new() Matrix44 ret = new();
{ NativeIntrinsic.Intrinsic.f32_add(&left, &right, &ret);
column0 = left.column0 + right.column0,
column1 = left.column1 + right.column1,
column2 = left.column2 + right.column2,
column3 = left.column3 + right.column3
};
return ret; return ret;
} }
@@ -273,5 +266,18 @@ namespace StgSharp.Mathematics.Graphic
return left.Equals(right); return left.Equals(right);
} }
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator<Vec4> IEnumerable<Vec4>.GetEnumerator()
{
ref Vec4 col0 = ref column0;
for (int i = 0; i < 4; i++) {
yield return Unsafe.Add(ref col0, i);
}
}
} }
} }
+26 -6
View File
@@ -36,8 +36,9 @@ using System.Runtime.InteropServices;
namespace StgSharp.Mathematics.Graphic namespace StgSharp.Mathematics.Graphic
{ {
[CollectionBuilder(builderType:typeof(Vec4), methodName: nameof(Vec4.FromSpan))]
[StructLayout(LayoutKind.Explicit, Size = 16, Pack = 16)] [StructLayout(LayoutKind.Explicit, Size = 16, Pack = 16)]
public struct Vec4 : IEquatable<Vec4>, IUnmanagedVector<Vec4> public unsafe struct Vec4 : IEquatable<Vec4>, IUnmanagedVector<Vec4, float>
{ {
[FieldOffset(0)] internal unsafe fixed float num[4]; [FieldOffset(0)] internal unsafe fixed float num[4];
@@ -127,11 +128,33 @@ namespace StgSharp.Mathematics.Graphic
return left + right; return left + right;
} }
public ReadOnlySpan<float> AsSpan()
{
return MemoryMarshal.CreateReadOnlySpan(ref reg.Member<float>(0), 4);
}
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return obj is Vec4 v && v == this; return obj is Vec4 v && v == this;
} }
public static Vec4 FromSpan(ReadOnlySpan<float> span)
{
return new Vec4
{
X = span[0],
Y = span[1],
Z = span[2],
W = span[3]
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<float>.Enumerator GetEnumerator()
{
return AsSpan().GetEnumerator();
}
public override int GetHashCode() public override int GetHashCode()
{ {
return vec.GetHashCode(); return vec.GetHashCode();
@@ -163,11 +186,8 @@ namespace StgSharp.Mathematics.Graphic
public static Vec4 operator *(Matrix44 mat, Vec4 vec) public static Vec4 operator *(Matrix44 mat, Vec4 vec)
{ {
mat.InternalTranspose(); mat.InternalTranspose();
return new Vec4( return new Vec4(Vector4.Dot(mat.transpose.colum0, vec.vec), Vector4.Dot(mat.transpose.colum1, vec.vec),
Vector4.Dot(mat.transpose.colum0, vec.vec), Vector4.Dot(mat.transpose.colum2, vec.vec), Vector4.Dot(mat.transpose.colum3, 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -30,6 +30,7 @@ using StgSharp.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text; using System.Text;
namespace StgSharp.Mathematics.Numeric namespace StgSharp.Mathematics.Numeric
@@ -42,7 +42,7 @@ namespace StgSharp.Threading
public class BufferExpansionLock : IDisposable public class BufferExpansionLock : IDisposable
{ {
private static readonly ThreadLocal<int> Count = new(); private static readonly ThreadLocal<int> Count = new(() => 0);
private readonly ReaderWriterLockSlim _copyLock; private readonly ReaderWriterLockSlim _copyLock;
private readonly ReaderWriterLockSlim _expansionLock; private readonly ReaderWriterLockSlim _expansionLock;
@@ -74,13 +74,11 @@ namespace StgSharp.Threading
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnterBufferRead() public void EnterBufferRead()
{ {
if (++Count.Value == 1) int c = Count.Value;
{ if (c == 0) {
_copyLock.EnterReadLock(); _copyLock.EnterReadLock();
} else
{
Count.Value++;
} }
Count.Value = c + 1;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -104,12 +102,10 @@ namespace StgSharp.Threading
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExitBufferRead() public void ExitBufferRead()
{ {
if (--Count.Value == 0) int c = Count.Value - 1;
{ Count.Value = c;
if (c == 0) {
_copyLock.ExitReadLock(); _copyLock.ExitReadLock();
} else
{
Count.Value--;
} }
} }
@@ -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;
/// <summary>
/// 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
/// </summary>
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;
/// <summary>
/// Gets the current method
/// </summary>
public readonly BackoffMethod CurrentMethod => _currentMethod;
/// <summary>
/// Creates a new optimized sampling adaptive backoff strategy instance
/// </summary>
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;
}
/// <summary>
/// Gets the current sampling status (for debugging)
/// </summary>
public readonly (int attempts, int successes, int fallbacks, double successRate, double fallbackRate) GetCurrentSampleStats()
{
return (_currentSample.TotalAttempts,
_currentSample.SuccessfulAttempts,
_currentSample.FallbackCount,
_currentSample.SuccessRate,
_currentSample.FallbackRate);
}
/// <summary>
/// Gets the historical performance of the specified method (for monitoring)
/// </summary>
public readonly (double successRate, double fallbackRate, double overallScore) GetMethodPerformance(
BackoffMethod method)
{
var (successRate, fallbackRate, score) = _packedPerformances.GetPerformance(method);
return (successRate, fallbackRate, score);
}
/// <summary>
/// Called when attempting CAS (must be called regardless of success or failure)
/// </summary>
/// <param name="success">
/// Whether this CAS attempt was successful
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnCASAttempt(bool success)
{
_currentSample.TotalAttempts++;
if (success)
{
_currentSample.SuccessfulAttempts++;
_consecutiveFailures = 0;
_spinWait = default; // Reset SpinWait after success
} else
{
_consecutiveFailures++;
}
}
/// <summary>
/// Called when CAS fails and fallback needs to be executed, performs backoff and may trigger
/// re-sampling
/// </summary>
[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();
}
/// <summary>
/// Determines initial method based on system characteristics
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static BackoffMethod DetermineInitialMethod()
{
int logicalCores = Environment.ProcessorCount;
return logicalCores <= 16 ? BackoffMethod.SpinWait : BackoffMethod.Sleep0;
}
/// <summary>
/// Determines the method to test in the next sampling window using optimized random selection
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private BackoffMethod DetermineNextTestMethod()
{
// Use bitwise operations for 80%/20% probability split
return (NextRandom() & 0xF) < 13 ? _candidateMethod : (BackoffMethod)(NextRandom() & 3);
}
/// <summary>
/// Evaluates the current sampling window and may adjust the strategy
/// </summary>
[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;
}
}
/// <summary>
/// Generates initial seed for RNG
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint GenerateInitialSeed()
{
return (uint)(Environment.TickCount64 ^ (Thread.CurrentThread.ManagedThreadId * 0x9E3779B9U));
}
/// <summary>
/// Fast pseudo-random number generator using XorShift32
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private uint NextRandom()
{
// XorShift32 - faster than LCG with better distribution
_rng ^= _rng << 13;
_rng ^= _rng >> 17;
_rng ^= _rng << 5;
return _rng;
}
/// <summary>
/// Optimized backoff operation using jump table approach
/// </summary>
[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;
}
}
/// <summary>
/// Specialized SpinWait backoff with optimized logic
/// </summary>
[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);
}
}
/// <summary>
/// Switches to a new method with optimized state reset
/// </summary>
[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);
}
}
/**/
}
@@ -47,10 +47,20 @@ namespace StgSharp.Mathematics.Numeric
} }
Matrix<float> ans = Matrix<float>.FromDefault(left.ColumnLength, left.RowLength); Matrix<float> ans = Matrix<float>.FromDefault(left.ColumnLength, left.RowLength);
long count = (long)left.KernelColumnLength * left.KernelRowLength; long count = (long)left.KernelColumnLength * left.KernelRowLength;
if (count <= 256)
{
MatrixKernel<float>* leftPtr = left.Buffer;
MatrixKernel<float>* rightPtr = right.Buffer;
MatrixKernel<float>* 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) if (count <= 1024)
{ {
CapacityFixedStack<MatrixParallelThread> pool = MatrixParallel.LeadParallel(); ReadOnlySpan<MatrixParallelThread> pool = MatrixParallel.LeadParallel(1);
if (!pool.TryPop(out MatrixParallelThread thread)) if (pool.Length == 0)
{ {
/* /*
* wait * wait
@@ -64,47 +74,14 @@ namespace StgSharp.Mathematics.Numeric
} }
return ans; return ans;
} }
Stack<MatrixParallelWrap> taskGroup = MatrixParallel.PublicTask(left.GetColumnEnumeration(), right.GetColumnEnumeration(), ans.GetColumnEnumeration(), MatrixParallelHandle taskGroup = MatrixParallel.PublicTask(left.GetColumnEnumeration(), right.GetColumnEnumeration(), ans.GetColumnEnumeration(),
(delegate*<MatrixKernel<float>*, MatrixKernel<float>*, MatrixKernel<float>*, void>)NativeIntrinsic.Intrinsic.f32_add); (delegate*<MatrixKernel<float>*, MatrixKernel<float>*, MatrixKernel<float>*, void>)NativeIntrinsic.Intrinsic.f32_add);
MatrixParallel.LaunchParallel(taskGroup, SleepMode.DeepSleep); MatrixParallel.LaunchParallel(taskGroup, SleepMode.DeepSleep);
/* /*
* run parallel * run parallel
*/ */
return ans; taskGroup.Dispose();
}
public static unsafe Matrix<float> Sub(Matrix<float> left, Matrix<float> right)
{
if (left.ColumnLength != right.ColumnLength || left.RowLength != right.RowLength) {
throw new IndexOutOfRangeException("Matrix dimensions must agree.");
}
Matrix<float> ans = Matrix<float>.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
CapacityFixedStack<MatrixParallelThread> pool = MatrixParallel.LeadParallel();
if (!pool.TryPop(out MatrixParallelThread thread))
{
/*
* wait
*/
}
MatrixKernel<float>* leftPtr = left.Buffer;
MatrixKernel<float>* rightPtr = right.Buffer;
MatrixKernel<float>* ansPtr = ans.Buffer;
for (int i = 0; i < count; i++) {
NativeIntrinsic.Intrinsic.f32_sub(leftPtr + i, rightPtr + i, ansPtr + i);
}
return ans;
}
Stack<MatrixParallelWrap> taskGroup = MatrixParallel.PublicTask(left.GetColumnEnumeration(), right.GetColumnEnumeration(), ans.GetColumnEnumeration(),
(delegate*<MatrixKernel<float>*, MatrixKernel<float>*, MatrixKernel<float>*, void>)NativeIntrinsic.Intrinsic.f32_sub);
/*
* run parallel
*/
return ans; return ans;
} }
@@ -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<WaitHandle> _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);
}
}
}
}
@@ -40,10 +40,10 @@ namespace StgSharp.Mathematics.Numeric
public static partial class MatrixParallel public static partial class MatrixParallel
{ {
public static unsafe Stack<MatrixParallelWrap> PublicTask<T>( public static unsafe MatrixParallelHandle PublicTask<T>(
MatrixSegmentEnumeration<T> source, MatrixSegmentEnumeration<T> source,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation) delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
{ {
MatrixParallelTaskPackage<T>* package = MatrixParallelFactory.CreateBaseTask<T>(); MatrixParallelTaskPackage<T>* package = MatrixParallelFactory.CreateBaseTask<T>();
@@ -66,11 +66,11 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate<T>(package); return PublicTaskPrivate<T>(package);
} }
public static unsafe Stack<MatrixParallelWrap> PublicTask<T>( public static unsafe MatrixParallelHandle PublicTask<T>(
MatrixSegmentEnumeration<T> left, MatrixSegmentEnumeration<T> left,
MatrixSegmentEnumeration<T> right, MatrixSegmentEnumeration<T> right,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation) delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
{ {
MatrixParallelTaskPackage<T>* package = MatrixParallelFactory.CreateBaseTask<T>(); MatrixParallelTaskPackage<T>* package = MatrixParallelFactory.CreateBaseTask<T>();
@@ -99,11 +99,11 @@ namespace StgSharp.Mathematics.Numeric
} }
// Unary with 1..8 scalars (each scalar is independent generic type) // Unary with 1..8 scalars (each scalar is independent generic type)
public static unsafe Stack<MatrixParallelWrap> PublicTask<T, TScalar0>( public static unsafe MatrixParallelHandle PublicTask<T, TScalar0>(
MatrixSegmentEnumeration<T> source, MatrixSegmentEnumeration<T> source,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation, delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation,
TScalar0 s0) TScalar0 s0)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
where TScalar0: unmanaged, INumber<TScalar0> where TScalar0: unmanaged, INumber<TScalar0>
{ {
@@ -130,12 +130,12 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate<T>(package); return PublicTaskPrivate<T>(package);
} }
public static unsafe Stack<MatrixParallelWrap> PublicTask<T, TScalar0, TScalar1>( public static unsafe MatrixParallelHandle PublicTask<T, TScalar0, TScalar1>(
MatrixSegmentEnumeration<T> source, MatrixSegmentEnumeration<T> source,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation, delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation,
TScalar0 s0, TScalar0 s0,
TScalar1 s1) TScalar1 s1)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
where TScalar0: unmanaged, INumber<TScalar0> where TScalar0: unmanaged, INumber<TScalar0>
where TScalar1: unmanaged, INumber<TScalar1> where TScalar1: unmanaged, INumber<TScalar1>
@@ -165,12 +165,12 @@ namespace StgSharp.Mathematics.Numeric
} }
// Binary with 1..8 scalars (each scalar is independent generic type) // Binary with 1..8 scalars (each scalar is independent generic type)
public static unsafe Stack<MatrixParallelWrap> PublicTask<T, TScalar0>( public static unsafe MatrixParallelHandle PublicTask<T, TScalar0>(
MatrixSegmentEnumeration<T> left, MatrixSegmentEnumeration<T> left,
MatrixSegmentEnumeration<T> right, MatrixSegmentEnumeration<T> right,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation, delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation,
TScalar0 s0) TScalar0 s0)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
where TScalar0: unmanaged, INumber<TScalar0> where TScalar0: unmanaged, INumber<TScalar0>
{ {
@@ -202,13 +202,13 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate<T>(package); return PublicTaskPrivate<T>(package);
} }
public static unsafe Stack<MatrixParallelWrap> PublicTask<T, TScalar0, TScalar1, TScalar2>( public static unsafe MatrixParallelHandle PublicTask<T, TScalar0, TScalar1, TScalar2>(
MatrixSegmentEnumeration<T> source, MatrixSegmentEnumeration<T> source,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation, delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation,
TScalar0 s0, TScalar0 s0,
TScalar1 s1, TScalar1 s1,
TScalar2 s2) TScalar2 s2)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
where TScalar0: unmanaged, INumber<TScalar0> where TScalar0: unmanaged, INumber<TScalar0>
where TScalar1: unmanaged, INumber<TScalar1> where TScalar1: unmanaged, INumber<TScalar1>
@@ -239,13 +239,13 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate<T>(package); return PublicTaskPrivate<T>(package);
} }
public static unsafe Stack<MatrixParallelWrap> PublicTask<T, TScalar0, TScalar1>( public static unsafe MatrixParallelHandle PublicTask<T, TScalar0, TScalar1>(
MatrixSegmentEnumeration<T> left, MatrixSegmentEnumeration<T> left,
MatrixSegmentEnumeration<T> right, MatrixSegmentEnumeration<T> right,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation, delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation,
TScalar0 s0, TScalar0 s0,
TScalar1 s1) TScalar1 s1)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
where TScalar0: unmanaged, INumber<TScalar0> where TScalar0: unmanaged, INumber<TScalar0>
where TScalar1: unmanaged, INumber<TScalar1> where TScalar1: unmanaged, INumber<TScalar1>
@@ -279,14 +279,14 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate<T>(package); return PublicTaskPrivate<T>(package);
} }
public static unsafe Stack<MatrixParallelWrap> PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3>( public static unsafe MatrixParallelHandle PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3>(
MatrixSegmentEnumeration<T> source, MatrixSegmentEnumeration<T> source,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation, delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation,
TScalar0 s0, TScalar0 s0,
TScalar1 s1, TScalar1 s1,
TScalar2 s2, TScalar2 s2,
TScalar3 s3) TScalar3 s3)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
where TScalar0: unmanaged, INumber<TScalar0> where TScalar0: unmanaged, INumber<TScalar0>
where TScalar1: unmanaged, INumber<TScalar1> where TScalar1: unmanaged, INumber<TScalar1>
@@ -319,14 +319,14 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate<T>(package); return PublicTaskPrivate<T>(package);
} }
public static unsafe Stack<MatrixParallelWrap> PublicTask<T, TScalar0, TScalar1, TScalar2>( public static unsafe MatrixParallelHandle PublicTask<T, TScalar0, TScalar1, TScalar2>(
MatrixSegmentEnumeration<T> left, MatrixSegmentEnumeration<T> left,
MatrixSegmentEnumeration<T> right, MatrixSegmentEnumeration<T> right,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation, delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation,
TScalar0 s0, TScalar0 s0,
TScalar1 s1, TScalar1 s1,
TScalar2 s2) TScalar2 s2)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
where TScalar0: unmanaged, INumber<TScalar0> where TScalar0: unmanaged, INumber<TScalar0>
where TScalar1: unmanaged, INumber<TScalar1> where TScalar1: unmanaged, INumber<TScalar1>
@@ -362,15 +362,15 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate<T>(package); return PublicTaskPrivate<T>(package);
} }
public static unsafe Stack<MatrixParallelWrap> PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3, TScalar4>( public static unsafe MatrixParallelHandle PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3, TScalar4>(
MatrixSegmentEnumeration<T> source, MatrixSegmentEnumeration<T> source,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation, delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation,
TScalar0 s0, TScalar0 s0,
TScalar1 s1, TScalar1 s1,
TScalar2 s2, TScalar2 s2,
TScalar3 s3, TScalar3 s3,
TScalar4 s4) TScalar4 s4)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
where TScalar0: unmanaged, INumber<TScalar0> where TScalar0: unmanaged, INumber<TScalar0>
where TScalar1: unmanaged, INumber<TScalar1> where TScalar1: unmanaged, INumber<TScalar1>
@@ -405,15 +405,15 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate<T>(package); return PublicTaskPrivate<T>(package);
} }
public static unsafe Stack<MatrixParallelWrap> PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3>( public static unsafe MatrixParallelHandle PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3>(
MatrixSegmentEnumeration<T> left, MatrixSegmentEnumeration<T> left,
MatrixSegmentEnumeration<T> right, MatrixSegmentEnumeration<T> right,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation, delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation,
TScalar0 s0, TScalar0 s0,
TScalar1 s1, TScalar1 s1,
TScalar2 s2, TScalar2 s2,
TScalar3 s3) TScalar3 s3)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
where TScalar0: unmanaged, INumber<TScalar0> where TScalar0: unmanaged, INumber<TScalar0>
where TScalar1: unmanaged, INumber<TScalar1> where TScalar1: unmanaged, INumber<TScalar1>
@@ -451,16 +451,16 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate<T>(package); return PublicTaskPrivate<T>(package);
} }
public static unsafe Stack<MatrixParallelWrap> PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3, TScalar4, TScalar5>( public static unsafe MatrixParallelHandle PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3, TScalar4, TScalar5>(
MatrixSegmentEnumeration<T> source, MatrixSegmentEnumeration<T> source,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation, delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation,
TScalar0 s0, TScalar0 s0,
TScalar1 s1, TScalar1 s1,
TScalar2 s2, TScalar2 s2,
TScalar3 s3, TScalar3 s3,
TScalar4 s4, TScalar4 s4,
TScalar5 s5) TScalar5 s5)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
where TScalar0: unmanaged, INumber<TScalar0> where TScalar0: unmanaged, INumber<TScalar0>
where TScalar1: unmanaged, INumber<TScalar1> where TScalar1: unmanaged, INumber<TScalar1>
@@ -497,16 +497,16 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate<T>(package); return PublicTaskPrivate<T>(package);
} }
public static unsafe Stack<MatrixParallelWrap> PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3, TScalar4>( public static unsafe MatrixParallelHandle PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3, TScalar4>(
MatrixSegmentEnumeration<T> left, MatrixSegmentEnumeration<T> left,
MatrixSegmentEnumeration<T> right, MatrixSegmentEnumeration<T> right,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation, delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation,
TScalar0 s0, TScalar0 s0,
TScalar1 s1, TScalar1 s1,
TScalar2 s2, TScalar2 s2,
TScalar3 s3, TScalar3 s3,
TScalar4 s4) TScalar4 s4)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
where TScalar0: unmanaged, INumber<TScalar0> where TScalar0: unmanaged, INumber<TScalar0>
where TScalar1: unmanaged, INumber<TScalar1> where TScalar1: unmanaged, INumber<TScalar1>
@@ -546,17 +546,17 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate<T>(package); return PublicTaskPrivate<T>(package);
} }
public static unsafe Stack<MatrixParallelWrap> PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3, TScalar4, TScalar5, TScalar6>( public static unsafe MatrixParallelHandle PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3, TScalar4, TScalar5, TScalar6>(
MatrixSegmentEnumeration<T> source, MatrixSegmentEnumeration<T> source,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation, delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation,
TScalar0 s0, TScalar0 s0,
TScalar1 s1, TScalar1 s1,
TScalar2 s2, TScalar2 s2,
TScalar3 s3, TScalar3 s3,
TScalar4 s4, TScalar4 s4,
TScalar5 s5, TScalar5 s5,
TScalar6 s6) TScalar6 s6)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
where TScalar0: unmanaged, INumber<TScalar0> where TScalar0: unmanaged, INumber<TScalar0>
where TScalar1: unmanaged, INumber<TScalar1> where TScalar1: unmanaged, INumber<TScalar1>
@@ -595,17 +595,17 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate<T>(package); return PublicTaskPrivate<T>(package);
} }
public static unsafe Stack<MatrixParallelWrap> PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3, TScalar4, TScalar5>( public static unsafe MatrixParallelHandle PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3, TScalar4, TScalar5>(
MatrixSegmentEnumeration<T> left, MatrixSegmentEnumeration<T> left,
MatrixSegmentEnumeration<T> right, MatrixSegmentEnumeration<T> right,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation, delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation,
TScalar0 s0, TScalar0 s0,
TScalar1 s1, TScalar1 s1,
TScalar2 s2, TScalar2 s2,
TScalar3 s3, TScalar3 s3,
TScalar4 s4, TScalar4 s4,
TScalar5 s5) TScalar5 s5)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
where TScalar0: unmanaged, INumber<TScalar0> where TScalar0: unmanaged, INumber<TScalar0>
where TScalar1: unmanaged, INumber<TScalar1> where TScalar1: unmanaged, INumber<TScalar1>
@@ -647,18 +647,18 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate<T>(package); return PublicTaskPrivate<T>(package);
} }
public static unsafe Stack<MatrixParallelWrap> PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3, TScalar4, TScalar5, TScalar6, TScalar7>( public static unsafe MatrixParallelHandle PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3, TScalar4, TScalar5, TScalar6, TScalar7>(
MatrixSegmentEnumeration<T> source, MatrixSegmentEnumeration<T> source,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation, delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation,
TScalar0 s0, TScalar0 s0,
TScalar1 s1, TScalar1 s1,
TScalar2 s2, TScalar2 s2,
TScalar3 s3, TScalar3 s3,
TScalar4 s4, TScalar4 s4,
TScalar5 s5, TScalar5 s5,
TScalar6 s6, TScalar6 s6,
TScalar7 s7) TScalar7 s7)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
where TScalar0: unmanaged, INumber<TScalar0> where TScalar0: unmanaged, INumber<TScalar0>
where TScalar1: unmanaged, INumber<TScalar1> where TScalar1: unmanaged, INumber<TScalar1>
@@ -699,18 +699,18 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate<T>(package); return PublicTaskPrivate<T>(package);
} }
public static unsafe Stack<MatrixParallelWrap> PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3, TScalar4, TScalar5, TScalar6>( public static unsafe MatrixParallelHandle PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3, TScalar4, TScalar5, TScalar6>(
MatrixSegmentEnumeration<T> left, MatrixSegmentEnumeration<T> left,
MatrixSegmentEnumeration<T> right, MatrixSegmentEnumeration<T> right,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation, delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation,
TScalar0 s0, TScalar0 s0,
TScalar1 s1, TScalar1 s1,
TScalar2 s2, TScalar2 s2,
TScalar3 s3, TScalar3 s3,
TScalar4 s4, TScalar4 s4,
TScalar5 s5, TScalar5 s5,
TScalar6 s6) TScalar6 s6)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
where TScalar0: unmanaged, INumber<TScalar0> where TScalar0: unmanaged, INumber<TScalar0>
where TScalar1: unmanaged, INumber<TScalar1> where TScalar1: unmanaged, INumber<TScalar1>
@@ -754,19 +754,19 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate<T>(package); return PublicTaskPrivate<T>(package);
} }
public static unsafe Stack<MatrixParallelWrap> PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3, TScalar4, TScalar5, TScalar6, TScalar7>( public static unsafe MatrixParallelHandle PublicTask<T, TScalar0, TScalar1, TScalar2, TScalar3, TScalar4, TScalar5, TScalar6, TScalar7>(
MatrixSegmentEnumeration<T> left, MatrixSegmentEnumeration<T> left,
MatrixSegmentEnumeration<T> right, MatrixSegmentEnumeration<T> right,
MatrixSegmentEnumeration<T> ans, MatrixSegmentEnumeration<T> ans,
delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation, delegate*<MatrixKernel<T>*, MatrixKernel<T>*, MatrixKernel<T>*, void> Operation,
TScalar0 s0, TScalar0 s0,
TScalar1 s1, TScalar1 s1,
TScalar2 s2, TScalar2 s2,
TScalar3 s3, TScalar3 s3,
TScalar4 s4, TScalar4 s4,
TScalar5 s5, TScalar5 s5,
TScalar6 s6, TScalar6 s6,
TScalar7 s7) TScalar7 s7)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
where TScalar0: unmanaged, INumber<TScalar0> where TScalar0: unmanaged, INumber<TScalar0>
where TScalar1: unmanaged, INumber<TScalar1> where TScalar1: unmanaged, INumber<TScalar1>
@@ -812,7 +812,7 @@ namespace StgSharp.Mathematics.Numeric
return PublicTaskPrivate<T>(package); return PublicTaskPrivate<T>(package);
} }
private static unsafe Stack<MatrixParallelWrap> PublicTaskPrivate<T>(MatrixParallelTaskPackage<T>* package) private static unsafe MatrixParallelHandle PublicTaskPrivate<T>(MatrixParallelTaskPackage<T>* package)
where T: unmanaged, INumber<T> where T: unmanaged, INumber<T>
{ {
int primCount = package->PrimCount; int primCount = package->PrimCount;
@@ -820,9 +820,21 @@ namespace StgSharp.Mathematics.Numeric
int tileWidth = (1024 / secCount) + 1; int tileWidth = (1024 / secCount) + 1;
int tileCount = (primCount + tileWidth - 1) / tileWidth; int tileCount = (primCount + tileWidth - 1) / tileWidth;
MatrixParallelPool pool = LeadParallel(); MatrixParallelThread[] pool = LeadParallel(tileCount);
int threadCount = pool.SpareThreadCount; if (pool.Length == 0)
{
/*
* wait until has enough threads
*/
}
MatrixParallelHandle handle = new MatrixParallelHandle(pool);
if (handle.Wraps.Count() == 1)
{
handle.Wraps.First().WrapTask = (MatrixParallelTaskPackageNonGeneric*)package;
return handle;
}
int threadCount = pool.Length;
int tilePerThreadLess = tileCount / threadCount; // tiles per thread without extra slice int tilePerThreadLess = tileCount / threadCount; // tiles per thread without extra slice
int columnAfterBaseTile = tileCount % threadCount * tileWidth; // tile sliced into columns int columnAfterBaseTile = tileCount % threadCount * tileWidth; // tile sliced into columns
int extraColumnPerThread = columnAfterBaseTile / threadCount; int extraColumnPerThread = columnAfterBaseTile / threadCount;
@@ -831,22 +843,13 @@ namespace StgSharp.Mathematics.Numeric
int total = primCount; int total = primCount;
Stack<MatrixParallelWrap> taskBunch = new Stack<MatrixParallelWrap>(threadCount); Stack<MatrixParallelWrap> taskBunch = new Stack<MatrixParallelWrap>(threadCount);
MatrixParallelTaskPackage<T>* current = package; MatrixParallelTaskPackage<T>* current = package;
while (true) foreach (MatrixParallelWrap wrap in handle.Wraps)
{ {
if (!pool.TryRequestWrap(out MatrixParallelWrap? wrap) || wrap is null)
{
#if DEBUG
throw new InvalidOperationException("Someone steal parallel wraps from the pool.");
#else
break;
#endif
}
bool hasSlice = columnAfterBaseTile > 0; bool hasSlice = columnAfterBaseTile > 0;
bool isLargeSlice = extraColumnCount > 0; bool isLargeSlice = extraColumnCount > 0;
// count tiles for wrap // count tiles for wrap
int wrapWidth = wrap!.WrapTaskCapacity; int wrapWidth = wrap!.WrapTaskCapacity/**/;
int columnFromSlicingCount = (hasSlice ? extraColumnPerThread : 0) * wrapWidth; int columnFromSlicingCount = (hasSlice ? extraColumnPerThread : 0) * wrapWidth;
int baseColumn = wrapWidth * tilePerThreadLess * tileWidth; int baseColumn = wrapWidth * tilePerThreadLess * tileWidth;
int columnInWrap = baseColumn + columnFromSlicingCount + (isLargeSlice ? 1 : 0); int columnInWrap = baseColumn + columnFromSlicingCount + (isLargeSlice ? 1 : 0);
@@ -873,8 +876,6 @@ namespace StgSharp.Mathematics.Numeric
// set to wrap and push // set to wrap and push
wrap.WrapTask = (MatrixParallelTaskPackageNonGeneric*)current; wrap.WrapTask = (MatrixParallelTaskPackageNonGeneric*)current;
taskBunch.Push(wrap);
if (total <= 0) if (total <= 0)
{ {
@@ -883,9 +884,8 @@ namespace StgSharp.Mathematics.Numeric
current = MatrixParallelFactory.FromExistPackage(current); current = MatrixParallelFactory.FromExistPackage(current);
current->PrimTileOffset += columnInWrap; current->PrimTileOffset += columnInWrap;
} }
ReleaseParallelLeadership(ref pool);
return taskBunch; return handle;
} }
} }
+5 -81
View File
@@ -27,6 +27,7 @@
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using StgSharp.Collections; using StgSharp.Collections;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -34,45 +35,12 @@ using System.Threading;
namespace StgSharp.Mathematics.Numeric 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 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 bool _meaningful;
private static CapacityFixedStack<MatrixParallelThread> _threads; private static ConcurrentCapacityFixedStack<MatrixParallelThread> _threads;
private static volatile int RemainThreadCount = 0; private static volatile int RemainThreadCount = 0;
private static readonly object _lock = new(); private static readonly object _lock = new();
@@ -91,22 +59,6 @@ namespace StgSharp.Mathematics.Numeric
Monitor.Exit(_lock); 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() public static void Init()
{ {
if (IsInitialized) { if (IsInitialized) {
@@ -123,46 +75,18 @@ namespace StgSharp.Mathematics.Numeric
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
_threads.Push(new()); _threads.Push(new());
} }
_awaitingLeaderHandle = new ConcurrentQueue<WaitHandle>();
IsInitialized = true; IsInitialized = true;
} }
public static void LaunchParallel(IEnumerable<MatrixParallelWrap> wraps, SleepMode afterWork) public static void LaunchParallel(MatrixParallelHandle handle, SleepMode afterWork)
{ {
RemainThreadCount = _threads.Count; RemainThreadCount = _threads.Count;
_AfterWork = (ThreadPriority)afterWork; _AfterWork = (ThreadPriority)afterWork;
ResetThreadCount(); foreach (MatrixParallelWrap w in handle.Wraps) {
foreach (MatrixParallelWrap w in wraps) {
w.SchedulerReset.Set(); w.SchedulerReset.Set();
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ResetThreadCount()
{
RemainThreadCount = _threads.Count;
}
#region wrap field
public static CapacityFixedStack<MatrixParallelThread> LeadParallel()
{
#if NET9_0_OR_GREATER
#else
Monitor.Enter(_lock);
#endif
return _threads;
}
public static void ReleaseParallelLeadership(ref CapacityFixedStack<MatrixParallelThread> pool)
{
#if NET9_0_OR_GREATER
#else
Monitor.Exit(_lock);
#endif
pool = null!;
}
#endregion
} }
} }
@@ -36,77 +36,142 @@ using System.Threading.Tasks;
namespace StgSharp.Mathematics.Numeric namespace StgSharp.Mathematics.Numeric
{ {
public class MatrixParallelPool public struct MatrixParallelHandle : IDisposable
{ {
private MatrixParallelPool() private static readonly (int a, int b)[] LookupTable = [
{ (1, 1), // 7
throw new NotSupportedException(); (0, 2), // 8 (no non-zero solution)
} (3, 0), // 9 (no non-zero solution)
/* (2, 1), // 10
internal MatrixParallelPool(int threadCount, ReadOnlySpan<MatrixParallelWrap> wrapSpan) (1, 2), // 11
{ (0, 3), // 12 (no non-zero solution)
SpareWrap = CapacityFixedStackBuilder.Create(wrapSpan); (3, 1), // 13
SingleThreadAllocation = new CapacityFixedBag<MatrixParallelWrap>(wrapSpan.Length); (2, 2), // 14
SpareThreadCount = threadCount; (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; _threads = threadArray;
SingleThreadAllocation = new CapacityFixedBag<MatrixParallelWrap>(wrapSpan.Count); IsMultithread = threadArray.Length > 1;
SpareThreadCount = threadCount; SliceToWraps();
} }
public int SpareThreadCount { get; set; } public ReadOnlySpan<MatrixParallelThread> Threads
internal CapacityFixedBag<MatrixParallelWrap> SingleThreadAllocation { get; set; }
internal CapacityFixedStack<MatrixParallelWrap> SpareWrap { get; set; }
public void ReturnIndividualHandle(ref MatrixParallelWrap.Handle handle)
{ {
MatrixParallelWrap wrap = handle.Wrap; get
handle.Role = 0;
if (wrap.IsAllAvailable)
{ {
SingleThreadAllocation.RemoveAt(wrap.Magic); if (_disposed) {
SpareWrap.Push(wrap); throw new ObjectDisposedException(typeof(MatrixParallelHandle).Name);
SpareThreadCount += wrap.WrapTaskCapacity;
}
}
public bool TryRentIndividualHandle(out MatrixParallelWrap.Handle handle)
{
foreach (MatrixParallelWrap item in SingleThreadAllocation)
{
if (item.TryGetLeaderHandle(out handle!)) {
return true;
} }
return _threads;
} }
if (TryRequestWrap(out MatrixParallelWrap wrap)) }
internal IEnumerable<MatrixParallelWrap> Wraps
{
get
{ {
wrap!.Magic = SingleThreadAllocation.Count; if (_disposed) {
SingleThreadAllocation.Add(wrap); throw new ObjectDisposedException(typeof(MatrixParallelHandle).Name);
if (wrap.TryGetLeaderHandle(out handle!)) {
return true;
} }
return _wraps;
} }
handle = null!; }
return false;
internal ReadOnlySpan<MatrixParallelThread> ThreadsUnsafe
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _threads;
}
private bool IsMultithread { get; init; }
public void Dispose()
{
if (_disposed) {
return;
}
MatrixParallel.ReturnParallelResources(ref _threads);
_disposed = true;
}
public MatrixParallelThread GetLeader()
{
Threads[0].Role = 2;
return Threads[0];
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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 (0, 0);
return true; }
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, 0, count);
_wraps = [ wrap ];
}
(int c3,int c4) = FastDecompose(_threads.Length);
_wraps = new(c3 + c4);
int begin = 0;
for (int i = 0; i < c3; i++)
{
_wraps.Push(new MatrixParallelWrap(_threads, begin, 3));
begin += 3;
}
for (int i = 0; i < c4; i++)
{
_wraps.Push(new MatrixParallelWrap(_threads, begin, 4));
begin += 4;
} }
wrap = null!;
return false;
} }
} }
@@ -96,6 +96,11 @@ namespace StgSharp.Mathematics.Numeric
_resetEvent.Set(); _resetEvent.Set();
} }
public void ReturnToPool()
{
// MatrixParallel.(this);
}
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (!disposedValue) if (!disposedValue)
@@ -113,30 +118,27 @@ namespace StgSharp.Mathematics.Numeric
private unsafe void MatrixParallelWorker() private unsafe void MatrixParallelWorker()
{ {
// Thread self = Thread.CurrentThread; // Thread self = Thread.CurrentThread;
do _resetEvent.Wait();
_resetEvent.Reset();
_managedThread.Priority = ThreadPriority.Highest;
switch (Role)
{ {
_resetEvent.Wait(); case 0://worker
_resetEvent.Reset(); // _ = Interlocked.Decrement(ref RemainThreadCount);
_managedThread.Priority = ThreadPriority.Highest; ExecuteTaskPackage(Tasks);
switch (Role) break;
{ case 1://scheduler
case 0://worker BindedWrap.PublishTaskToWorkers(Tasks);
// _ = Interlocked.Decrement(ref RemainThreadCount); BindedWrap.SetAllWorker();
ExecuteTaskPackage(Tasks); ExecuteTaskPackage(Tasks);
break;
case 1://scheduler
BindedWrap.PublishTaskToWorkers(Tasks);
BindedWrap.SetAllWorker();
ExecuteTaskPackage(Tasks);
break; break;
case 2://leader, error because leader should be at outer thread but not here case 2://leader, error because leader should be at outer thread but not here
break; break;
default: default:
break; break;
} }
_managedThread.Priority = AfterWork; _managedThread.Priority = AfterWork;
} while (true);
} }
#region wrap belong #region wrap belong
@@ -145,10 +147,10 @@ namespace StgSharp.Mathematics.Numeric
internal Queue<IntPtr> Tasks { get; } = []; internal Queue<IntPtr> Tasks { get; } = [];
internal int Role public int Role
{ {
get; get;
set internal set
{ {
if ((Role & (~3u)) == 0) if ((Role & (~3u)) == 0)
{ {
@@ -40,6 +40,15 @@ namespace StgSharp.Mathematics.Numeric
{ {
private readonly MatrixParallelThread[] _threads; private readonly MatrixParallelThread[] _threads;
private int _begin;
internal MatrixParallelWrap(MatrixParallelThread[] threads, int begin, int count)
{
_threads = threads;
_begin = begin;
Count = threads.Length;
_threads[_begin].Role = 1;
}
public MatrixParallelWrap() public MatrixParallelWrap()
{ {
@@ -83,7 +92,7 @@ namespace StgSharp.Mathematics.Numeric
int primAverage = primCount / Count; int primAverage = primCount / Count;
int secAverage = secCount / Count; int secAverage = secCount / Count;
bool shouldSliceSec = secAverage > 512; bool shouldSliceSec = secAverage > 512;
for (int i = 0; i < Count; i++) for (int i = _begin; i < _begin + Count; i++)
{ {
Queue<IntPtr> tasks = _threads[i].Tasks; Queue<IntPtr> tasks = _threads[i].Tasks;
MatrixParallelTaskPackageNonGeneric* subTask = MatrixParallelFactory.FromExistPackage(taskPackage); MatrixParallelTaskPackageNonGeneric* subTask = MatrixParallelFactory.FromExistPackage(taskPackage);
+121 -3
View File
@@ -2,8 +2,8 @@
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="Program" // file="Program"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -32,6 +32,7 @@ using StgSharp.Graphics;
using StgSharp.HighPerformance; using StgSharp.HighPerformance;
using StgSharp.HighPerformance.Memory; using StgSharp.HighPerformance.Memory;
using StgSharp.Mathematics; using StgSharp.Mathematics;
using StgSharp.Mathematics.Graphic;
using StgSharp.Mathematics.Numeric; using StgSharp.Mathematics.Numeric;
using StgSharp.Model.Step; using StgSharp.Model.Step;
using StgSharp.MVVM; using StgSharp.MVVM;
@@ -40,10 +41,12 @@ using StgSharp.Script;
using StgSharp.Script.Express; using StgSharp.Script.Express;
using System; using System;
using System.Collections.Concurrent;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
using System.Threading;
using System.Xml.XPath; using System.Xml.XPath;
namespace StgSharp namespace StgSharp
@@ -51,7 +54,7 @@ namespace StgSharp
internal static partial class Program 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) internal static unsafe void Main(string[] args)
{ {
@@ -59,6 +62,121 @@ namespace StgSharp
{ {
World.Init(); 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<int> stack = new ConcurrentBufferStack<int>();
Thread[] producers = new Thread[producerThreads];
Thread[] consumers = new Thread[consumerThreads];
ConcurrentBag<int> popped = new ConcurrentBag<int>();
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 + 10 * 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<int> 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)
{
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(); // TestMatrixParallel();
// 原有的矩阵测试代码 // 原有的矩阵测试代码