diff --git a/ReadMe.md b/ReadMe.md index 233f6a3a..a515a9c6 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -11,6 +11,7 @@ StgSharp provides both low-level and high-level bindings to OpenGL, multimedia c ## Core Modules ### StgSharp.Common + The main library containing core functionality: - **Mathematics**: High-performance linear algebra, matrix operations, and mathematical utilities @@ -22,6 +23,7 @@ The main library containing core functionality: - **Environment**: Platform abstraction and system integration ### StgSharp.Script + General-purpose syntax analysis framework: - **Syntax Analysis Framework**: Provides a flexible framework for parsing and analyzing various programming languages @@ -31,6 +33,7 @@ General-purpose syntax analysis framework: - **Grammar Parsing**: Extensible grammar parsing and validation system ### StgSharp.Model + Geometric model loading and interpretation: - **Geometric Model Loading**: Responsible for loading and interpreting geometric models from various formats @@ -40,6 +43,7 @@ Geometric model loading and interpretation: - **Schema Validation**: Model structure validation and verification ### StgSharp.Native + Native library integration: - **GLFW Integration**: Cross-platform window and input management @@ -48,6 +52,7 @@ Native library integration: - **Context Management**: Graphics context and resource management ### StgSharp.TerminalDialogue + Terminal-based user interface: - **Interactive Terminal**: Command-line interface components @@ -57,34 +62,48 @@ Terminal-based user interface: ## Key Features ### High-Performance Computing + - **Parallel Matrix Operations**: Multi-threaded matrix computations with optimized scheduling - **SIMD Acceleration**: Hardware-optimized vector operations - **Memory Management**: Custom allocators including HLSF (Hybrid Layer Segregated Fit) - **Thread Pool Management**: Efficient task scheduling and execution #### HLSF Benchmark Snapshot (BenchmarkDotNet) -Environment: Windows 11 (10.0.26100.7623), AMD Ryzen 7 6800H, .NET 8.0.23, x64 RyuJIT AVX2, GC = Concurrent Workstation. -| Method | Mean | Error | StdDev | Ratio | RatioSD | Allocated | Alloc Ratio | -|------- |-----:|-----:|------:|------:|-------:|----------:|-----------:| -| Hlsf | 838.4 µs | 16.67 µs | 40.58 µs | 1.00 | 0.12 | 64 B | 1.00 | -| Libc | 849.8 µs | 34.93 µs | 100.23 µs | 1.01 | 0.17 | 64 B | 1.00 | +``` -This shows HLSF matching libc allocation throughput under the same workload. See the HLSF document for details and caveats. +BenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.7623) +AMD Ryzen 7 6800H with Radeon Graphics, 1 CPU, 16 logical and 8 physical cores +.NET SDK 10.0.102 + [Host] : .NET 8.0.23 (8.0.2325.60607), X64 RyuJIT AVX2 + +Toolchain=InProcessEmitToolchain InvocationCount=2048 UnrollFactor=1 + +``` + +| Method | Mean | Error | StdDev | Ratio | RatioSD | +|------- |-----:|-----:|------:|------:|-------:| +| Hlsf | 76.23 µs | 0.355 µs | 0.315 µs | 0.44 | 0.01 | +| Libc | 172.98 µs | 3.042 µs | 2.988 µs | 1.00 | 0.02 | + +This run shows HLSF significantly faster than libc under the current configuration. See the HLSF document for details and caveats. ### Graphics and Rendering + - **OpenGL Integration**: Complete OpenGL API coverage - **Cross-Platform Support**: Windows and Linux compatibility - **Rendering Pipeline**: Flexible and extensible graphics framework - **Shader Management**: Dynamic shader compilation and management ### Game Engine Capabilities + - **Entity System**: Flexible game object management - **State Machine**: Game state and behavior management - **Input Handling**: Comprehensive input and control systems - **Resource Management**: Efficient asset loading and management ### Data Processing + - **STEP File Support**: Industrial data format processing - **EXPRESS Language**: Domain-specific language compilation - **Pipeline Processing**: Efficient data transformation workflows diff --git a/StgSharp.Benchmark/HlsfAllocatorBench.cs b/StgSharp.Benchmark/HlsfAllocatorBench.cs index c0221e5a..156a06fb 100644 --- a/StgSharp.Benchmark/HlsfAllocatorBench.cs +++ b/StgSharp.Benchmark/HlsfAllocatorBench.cs @@ -32,10 +32,12 @@ using System.Runtime.InteropServices; namespace StgSharp.Benchmark { [MemoryDiagnoser] - [InvocationCount(16)] + [InvocationCount(2048)] public class HlsfAllocatorBench { + private const uint Alignment = 64; + private const int AllocationCount = 2048; private static readonly int[] AllocSizes = [ @@ -55,7 +57,7 @@ namespace StgSharp.Benchmark [GlobalCleanup] public void Cleanup() { - _hlsfAllocator.Dispose(); + _hlsfAllocator?.Dispose(); _hlsfLocalList.Clear(); _hlsfLocalList.TrimExcess(); _libcLocalList.Clear(); @@ -65,27 +67,7 @@ namespace StgSharp.Benchmark [Benchmark] public void Hlsf() { - for (int i = 0; i < AllocationCount; i++) - { - lock (_hlsfAllocator) - { - HybridLayerSegregatedFitAllocationHandle allocate = _hlsfAllocator.Alloc(GetNextRandomSize()); - _hlsfLocalList.Add(allocate); - } - } - - for (int i = 0; i < _hlsfLocalList.Count; i++) - { - lock (_hlsfAllocator) { - _hlsfAllocator.Free(_hlsfLocalList[i]); - } - } - } - - public void InvocationSetup() - { - _hlsfLocalList.Clear(); - _libcLocalList.Clear(); + RunHlsf(_hlsfAllocator, FreePolicy.NoCollect); } [IterationSetup] @@ -98,9 +80,15 @@ namespace StgSharp.Benchmark [Benchmark(Baseline = true)] public unsafe void Libc() { + _libcLocalList.Clear(); + for (int i = 0; i < AllocationCount; i++) { - void* allocate = NativeMemory.AlignedAlloc(GetNextRandomSize(), 64); + uint size = AlignSize(GetNextRandomSize()); + void* allocate = NativeMemory.AlignedAlloc(size, Alignment); + if (allocate == null) { + throw new OutOfMemoryException("NativeMemory.AlignedAlloc returned null."); + } _libcLocalList.Add((nint)allocate); } @@ -113,7 +101,15 @@ namespace StgSharp.Benchmark public void Setup() { _random = new Random(42); - _hlsfAllocator = new HybridLayerSegregatedFitAllocator(GetArenaBytes(), align:64); + _hlsfAllocator = new HybridLayerSegregatedFitAllocator(GetArenaBytes(), align:(int)Alignment); + } + + private static uint AlignSize( + uint size + ) + { + uint mask = Alignment - 1; + return (size + mask) & ~mask; } private static nuint GetArenaBytes() @@ -134,5 +130,24 @@ namespace StgSharp.Benchmark private uint GetNextRandomSize() => (uint)AllocSizes[_random.Next(AllocSizes.Length)]; + private void RunHlsf( + HybridLayerSegregatedFitAllocator allocator, + FreePolicy policy + ) + { + _hlsfLocalList.Clear(); + + for (int i = 0; i < AllocationCount; i++) + { + uint size = AlignSize(GetNextRandomSize()); + HybridLayerSegregatedFitAllocationHandle allocate = allocator.Alloc(size); + _hlsfLocalList.Add(allocate); + } + + for (int i = 0; i < _hlsfLocalList.Count; i++) { + allocator.Free(_hlsfLocalList[i], policy); + } + } + } } diff --git a/StgSharp.Benchmark/Program.cs b/StgSharp.Benchmark/Program.cs index 310d2d88..8d1c787a 100644 --- a/StgSharp.Benchmark/Program.cs +++ b/StgSharp.Benchmark/Program.cs @@ -25,7 +25,11 @@ // // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Running; +using BenchmarkDotNet.Toolchains.InProcess.Emit; using StgSharp.Benchmark; namespace StgSharp.Benchmark @@ -37,7 +41,10 @@ namespace StgSharp.Benchmark string[] args ) { - BenchmarkRunner.Run(); + IConfig config = ManualConfig.Create(DefaultConfig.Instance) + .AddLogger(ConsoleLogger.Default) + .AddJob(Job.Default.WithToolchain(InProcessEmitToolchain.Instance)); + BenchmarkRunner.Run(config); } } diff --git a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Free.cs b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Free.cs index 63d61d08..2ddcfe85 100644 --- a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Free.cs +++ b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Free.cs @@ -39,6 +39,15 @@ using hlsfHandle = StgSharp.HighPerformance.Memory.HybridLayerSegregatedFitAlloc namespace StgSharp.HighPerformance.Memory { + public enum FreePolicy + { + + FullCollect, + LazyCollect, + NoCollect + + } + public unsafe partial class HybridLayerSegregatedFitAllocator { @@ -79,9 +88,11 @@ namespace StgSharp.HighPerformance.Memory /// /// /// Handle to the allocated memory block + /// /// public void Free( - hlsfHandle handle + hlsfHandle handle, + FreePolicy collectPolicy = FreePolicy.NoCollect ) { Entry* e = handle.EntryHandle; @@ -96,7 +107,10 @@ namespace StgSharp.HighPerformance.Memory e->State = EntryState.Empty; // Try merge with adjacent free blocks and remove them from buckets - MergeAndRemoveFromBuckets(e); + if (collectPolicy == FreePolicy.LazyCollect) + { + MergeAndRemoveFromBuckets(e); + } // Calculate final level and segment for the merged block long finalSize = e->Size; @@ -108,6 +122,10 @@ namespace StgSharp.HighPerformance.Memory PushBucketToLevel(finalLevel, finalSegment, (BucketNode*)e->Position); // Console.WriteLine("one block freed"); + if (collectPolicy == FreePolicy.FullCollect) + { + Collect(); + } } } diff --git a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.cs b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.cs index fe67ce53..405b4d7b 100644 --- a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.cs +++ b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.cs @@ -77,7 +77,7 @@ namespace StgSharp.HighPerformance.Memory uint maxBlockSize ) { - _entries = SlabAllocator.Create(64, SlabBufferLayout.Chunked); + _entries = SlabAllocator.Create(64, SlabBufferLayout.Chunked, false); m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, (nuint)16); _align = 16; _size = byteSize; @@ -111,7 +111,7 @@ namespace StgSharp.HighPerformance.Memory throw new ArgumentException( "Alignment must be a power of two and at least 16 bytes."); } - _entries = SlabAllocator.Create(64, SlabBufferLayout.Chunked); + _entries = SlabAllocator.Create(64, SlabBufferLayout.Chunked, false); m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, (nuint)align); _align = align; _size = byteSize; @@ -138,7 +138,7 @@ namespace StgSharp.HighPerformance.Memory throw new ArgumentException( "Alignment must be a power of two and at least 16 bytes."); } - _entries = SlabAllocator.Create(64, SlabBufferLayout.Chunked); + _entries = SlabAllocator.Create(64, SlabBufferLayout.Chunked, false); m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, (nuint)align); _align = align; _size = byteSize; diff --git a/StgSharp.Common/HighPerformance/Memory/SlabAllocator.Chunked.cs b/StgSharp.Common/HighPerformance/Memory/SlabAllocator.Chunked.cs index 5c092ef5..1f572ee8 100644 --- a/StgSharp.Common/HighPerformance/Memory/SlabAllocator.Chunked.cs +++ b/StgSharp.Common/HighPerformance/Memory/SlabAllocator.Chunked.cs @@ -38,6 +38,8 @@ namespace StgSharp.HighPerformance.Memory internal sealed unsafe class ChunkedSlabAllocator : SlabAllocator where T : unmanaged { + private bool _disposed; + private BufferStack _buffers; private BufferStack _recycle; private readonly int _elementSize = Unsafe.SizeOf(); @@ -90,6 +92,10 @@ namespace StgSharp.HighPerformance.Memory public override void Dispose() { + if (_disposed) { + return; + } + _disposed = true; // NativeMemory.Free((void*)_currentBuffer); while (_buffers.TryPop(out nuint buffer)) { @@ -97,6 +103,7 @@ namespace StgSharp.HighPerformance.Memory } _buffers.Dispose(); _recycle.Dispose(); + GC.SuppressFinalize(this); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/StgSharp.Common/HighPerformance/Memory/SlabAllocator.ChunkedConcurrent.cs b/StgSharp.Common/HighPerformance/Memory/SlabAllocator.ChunkedConcurrent.cs index 2191a256..73ba7421 100644 --- a/StgSharp.Common/HighPerformance/Memory/SlabAllocator.ChunkedConcurrent.cs +++ b/StgSharp.Common/HighPerformance/Memory/SlabAllocator.ChunkedConcurrent.cs @@ -41,6 +41,8 @@ namespace StgSharp.HighPerformance.Memory internal sealed unsafe class ConcurrentChunkedSlabAllocator : SlabAllocator where T : unmanaged { + private bool _disposed; + private readonly BufferExpansionLock _lock = new(); private ConcurrentBufferStack _buffers; private ConcurrentBufferStack _recycle; @@ -114,6 +116,10 @@ namespace StgSharp.HighPerformance.Memory public override void Dispose() { + if (_disposed) { + return; + } + _disposed = true; NativeMemory.Free((void*)_currentBuffer); while (_buffers.TryPop(out nuint buffer)) { NativeMemory.Free((void*)buffer); @@ -121,6 +127,7 @@ namespace StgSharp.HighPerformance.Memory _lock.Dispose(); _buffers.Dispose(); _recycle.Dispose(); + GC.SuppressFinalize(this); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/StgSharp.Common/HighPerformance/Memory/SlabAllocator.SequencialConcurrent.cs b/StgSharp.Common/HighPerformance/Memory/SlabAllocator.SequencialConcurrent.cs index dc5ab1bd..98f58135 100644 --- a/StgSharp.Common/HighPerformance/Memory/SlabAllocator.SequencialConcurrent.cs +++ b/StgSharp.Common/HighPerformance/Memory/SlabAllocator.SequencialConcurrent.cs @@ -37,6 +37,8 @@ namespace StgSharp.HighPerformance.Memory public sealed unsafe class ConcurrentSequentialSlabAllocator : SlabAllocator where T: unmanaged { + private bool _disposed; + private T* _buffer; private readonly BufferExpansionLock _lock = new(); @@ -120,9 +122,14 @@ namespace StgSharp.HighPerformance.Memory public override void Dispose() { + if (_disposed) { + return; + } + _disposed = true; _stack.Dispose(); NativeMemory.Free(_buffer); _lock.Dispose(); + GC.SuppressFinalize(this); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/StgSharp.Native/sn-src/m.fma.x86.c b/StgSharp.Native/sn-src/m.fma.x86.c index 4f93dcc9..d97013ce 100644 --- a/StgSharp.Native/sn-src/m.fma.x86.c +++ b/StgSharp.Native/sn-src/m.fma.x86.c @@ -51,10 +51,10 @@ DECLARE_KER_PROC_LEFT_RIGHT_ANS(float, sse, , fma) c3 = _mm_add_ps(c3, _mm_mul_ps(a2, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(2)))); c3 = _mm_add_ps(c3, _mm_mul_ps(a3, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(3)))); - ans->f32_x[0] = c0; - ans->f32_x[1] = c1; - ans->f32_x[2] = c2; - ans->f32_x[3] = c3; + _mm_store_ps((float *)&ans->f32_x[0], c0); + _mm_store_ps((float *)&ans->f32_x[1], c1); + _mm_store_ps((float *)&ans->f32_x[2], c2); + _mm_store_ps((float *)&ans->f32_x[3], c3); } DECLARE_KER_PROC_LEFT_RIGHT_ANS(float, avx, , fma) diff --git a/StgSharpDebug/Program.cs b/StgSharpDebug/Program.cs index a7327df0..21e9189a 100644 --- a/StgSharpDebug/Program.cs +++ b/StgSharpDebug/Program.cs @@ -49,7 +49,10 @@ namespace StgSharp .UseModule() .UseModule() .InitializeModule(); - TestMatrix(); + + HlsfStressTest.Run(); + + // TestMatrix(); // --- Logarithmic time sequence test: print seconds at each emitted span ---