From 24a7d49ae2c1e793e89b7d7df49f64460e11a546 Mon Sep 17 00:00:00 2001 From: Misaki Date: Fri, 14 Nov 2025 11:14:09 +0900 Subject: [PATCH] Upgrade to .NET 10 and refactor core components Upgraded target framework to .NET 10 across all projects to leverage new features and improve performance. Refactored `JobScheduler` to fix method naming inconsistencies and ensure proper resource disposal. Enhanced `AllocationManager` with safer memory operations and better performance handling. Simplified `ReadOnlyUnsafeCollection` enumerator logic for efficiency. Overhauled `UnsafeBitSet` with new properties, improved bitwise operations, and optimized memory management. Updated `UnsafeSlotMap` and `ConcurrentSlotMap` for better validation and naming consistency. Revised `MemoryLeakException` to use `ReadOnlySpan` for improved performance. Simplified `MathematicsBenchmark` logic and integrated `BenchmarkDotNet` for testing. Added AOT compatibility settings for `Debug` and `Release` configurations. Introduced unit tests for `UnsafeBitSet` to validate functionality. Cleaned up unused code, improved readability, and ensured consistent naming conventions. Updated project references and metadata for consistency. Enabled inline methods for `NET10_0_OR_GREATER` in `VectorGenerator`. --- .../Misaki.HighPerformance.Image.csproj | 2 +- Misaki.HighPerformance.Jobs/JobScheduler.cs | 9 +- .../Misaki.HighPerformance.Jobs.csproj | 11 +- .../Buffer/AllocationManager.cs | 18 +- .../Collections/ReadOnlyUnsafeCollection.cs | 24 +- .../Collections/UnsafeBitSet.cs | 245 ++++++------------ .../Collections/UnsafeSlotMap.cs | 7 +- .../MemoryLeakException.cs | 38 +-- .../Misaki.HighPerformance.LowLevel.csproj | 6 +- .../Generators/VectorGenerator.cs | 2 +- .../Misaki.HighPerformance.Mathematics.csproj | 10 +- .../Benchmark/MathematicsBenchmark.cs | 7 +- .../Misaki.HighPerformance.Test.csproj | 2 +- Misaki.HighPerformance.Test/Program.cs | 16 +- .../UnitTest/Collections/TestUnsafeBitSet.cs | 78 ++++++ .../UnitTest/Collections/TestUnsafeSlotMap.cs | 2 +- .../Collections/ConcureentSlotMap.cs | 4 +- .../Misaki.HighPerformance.csproj | 2 +- 18 files changed, 223 insertions(+), 260 deletions(-) create mode 100644 Misaki.HighPerformance.Test/UnitTest/Collections/TestUnsafeBitSet.cs diff --git a/Misaki.HighPerformance.Image/Misaki.HighPerformance.Image.csproj b/Misaki.HighPerformance.Image/Misaki.HighPerformance.Image.csproj index 8c3570d..9bcdaa0 100644 --- a/Misaki.HighPerformance.Image/Misaki.HighPerformance.Image.csproj +++ b/Misaki.HighPerformance.Image/Misaki.HighPerformance.Image.csproj @@ -3,7 +3,7 @@ Public Domain true - net9.0 + net10.0 Misaki 1.0.0 $(AssemblyVersion) diff --git a/Misaki.HighPerformance.Jobs/JobScheduler.cs b/Misaki.HighPerformance.Jobs/JobScheduler.cs index 9464071..3e8ae66 100644 --- a/Misaki.HighPerformance.Jobs/JobScheduler.cs +++ b/Misaki.HighPerformance.Jobs/JobScheduler.cs @@ -30,10 +30,10 @@ public sealed unsafe class JobScheduler : IDisposable private bool _disposed = false; - public int WorkerCount => _workerThreads.Length; - internal bool IsCancellationRequested => _cts.IsCancellationRequested; + public int WorkerCount => _workerThreads.Length; + /// /// Initializes a new instance of the class with the specified number of worker threads. /// @@ -481,7 +481,7 @@ public sealed unsafe class JobScheduler : IDisposable var completedCount = 0; foreach (var handle in handles) { - if (!_jobInfoPool.Contain(handle._id, handle._generation)) + if (!_jobInfoPool.Contains(handle._id, handle._generation)) { completedCount++; } @@ -514,7 +514,7 @@ public sealed unsafe class JobScheduler : IDisposable { foreach (var handle in handles) { - if (!_jobInfoPool.Contain(handle._id, handle._generation)) + if (!_jobInfoPool.Contains(handle._id, handle._generation)) { return handle; } @@ -542,6 +542,7 @@ public sealed unsafe class JobScheduler : IDisposable _jobQueue.Clear(); _jobDataAllocator.Dispose(); + _workSignal.Dispose(); _cts.Dispose(); _disposed = true; diff --git a/Misaki.HighPerformance.Jobs/Misaki.HighPerformance.Jobs.csproj b/Misaki.HighPerformance.Jobs/Misaki.HighPerformance.Jobs.csproj index a052e6f..908b0f1 100644 --- a/Misaki.HighPerformance.Jobs/Misaki.HighPerformance.Jobs.csproj +++ b/Misaki.HighPerformance.Jobs/Misaki.HighPerformance.Jobs.csproj @@ -2,7 +2,7 @@ Library - net9.0 + net10.0 enable enable True @@ -14,8 +14,17 @@ https://git.personalnas.com/Misaki/Misaki.HighPerformance.git + + True + + + + True + + + diff --git a/Misaki.HighPerformance.LowLevel/Buffer/AllocationManager.cs b/Misaki.HighPerformance.LowLevel/Buffer/AllocationManager.cs index f0ff442..ed142a8 100644 --- a/Misaki.HighPerformance.LowLevel/Buffer/AllocationManager.cs +++ b/Misaki.HighPerformance.LowLevel/Buffer/AllocationManager.cs @@ -8,7 +8,7 @@ namespace Misaki.HighPerformance.LowLevel.Buffer; /// /// Holds information about a memory allocation. /// -public readonly unsafe struct AllocationInfo +public readonly struct AllocationInfo { /// /// Get the size of the allocation in bytes. @@ -69,7 +69,7 @@ public static unsafe class AllocationManager { var selfPtr = (ArenaAllocator*)instance; var newPtr = selfPtr->_arena.Allocate(newSize, alignment, allocationOption); - MemCpy(newPtr, ptr, newSize); + MemCpy(newPtr, ptr, Math.Min(oldSize, newSize)); if (allocationOption.HasFlag(AllocationOption.Clear)) { @@ -149,7 +149,7 @@ public static unsafe class AllocationManager private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption) { var newPtr = s_stack.Allocate(newSize, alignment, AllocationOption.None); - MemCpy(newPtr, ptr, newSize); + MemCpy(newPtr, ptr, Math.Min(oldSize, newSize)); if (allocationOption.HasFlag(AllocationOption.Clear)) { @@ -174,7 +174,7 @@ public static unsafe class AllocationManager } } - private const uint _DEFAULT_MEMORY_POOL_SIZE = 512 * 1024; + private const uint _DEFAULT_MEMORY_POOL_SIZE = 512 * 1024; // 512 KB private static readonly ArenaAllocator* s_pArenaAllocator; private static readonly HeapAllocator* s_pHeapAllocator; @@ -438,12 +438,10 @@ public static unsafe class AllocationManager } var newPtr = AlignedRealloc(ptr, newSize, alignment); - if (allocationOption.HasFlag(AllocationOption.Clear)) + if (allocationOption.HasFlag(AllocationOption.Clear) + && newSize > oldSize) { - if (newSize > oldSize) - { - MemClear((byte*)newPtr + oldSize, newSize - oldSize); - } + MemClear((byte*)newPtr + oldSize, newSize - oldSize); } return newPtr; @@ -535,7 +533,7 @@ public static unsafe class AllocationManager if (unfreeBytes > 0u) { - throw new MemoryLeakException(snapshot); + throw new MemoryLeakException(CollectionsMarshal.AsSpan(snapshot)); } } else if (s_activeHeapAllocations != 0) diff --git a/Misaki.HighPerformance.LowLevel/Collections/ReadOnlyUnsafeCollection.cs b/Misaki.HighPerformance.LowLevel/Collections/ReadOnlyUnsafeCollection.cs index 9aa9aff..5c1c0ad 100644 --- a/Misaki.HighPerformance.LowLevel/Collections/ReadOnlyUnsafeCollection.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/ReadOnlyUnsafeCollection.cs @@ -1,4 +1,4 @@ -using Misaki.HighPerformance.LowLevel.Utilities; +using Misaki.HighPerformance.LowLevel.Utilities; using System.Collections; using System.Runtime.CompilerServices; @@ -20,36 +20,21 @@ public readonly unsafe struct ReadOnlyUnsafeCollection : IEnumerable { private readonly ReadOnlyUnsafeCollection _collection; private int _index; - private T _value; - - public readonly T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _value; - } + public readonly T Current => _collection[_index]; readonly object IEnumerator.Current => Current; public Enumerator(ref readonly ReadOnlyUnsafeCollection array) { _collection = array; _index = -1; - _value = default; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { _index++; - - if (_index < _collection.Count) - { - _value = UnsafeUtility.ReadArrayElement(_collection._buffer, _index); - return true; - } - - _value = default; - return false; + return _index < _collection.Count; } public void Reset() @@ -79,7 +64,8 @@ public readonly unsafe struct ReadOnlyUnsafeCollection : IEnumerable get => UnsafeUtility.ReadArrayElement(_buffer, index); } - public IEnumerator GetEnumerator() => new Enumerator(in this); + public Enumerator GetEnumerator() => new Enumerator(in this); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public ReadOnlyUnsafeCollection(T* buffer, int count) diff --git a/Misaki.HighPerformance.LowLevel/Collections/UnsafeBitSet.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeBitSet.cs index 9b3f2ee..1230893 100644 --- a/Misaki.HighPerformance.LowLevel/Collections/UnsafeBitSet.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeBitSet.cs @@ -14,47 +14,38 @@ public unsafe struct UnsafeBitSet : IDisposable private const int _MASK = (1 << 5) - 1; // 0x1F, the mask to get the bit index inside a uint private static readonly int s_padding = Vector.Count; // The padding used for vectorization, the amount of uints required for being vectorized basically - /// - /// Determines the required length of an to hold the passed id or bit. - /// - /// The id or bit. - /// A size of required s for the bitset. - public static int RequiredLength(int id) - { - return (id >> 5) + int.Sign(id & _BIT_SIZE); - } - - /// - /// Rounds the given length to the next padding size. - /// - /// The length to round. - /// The rounded length. - public static int RoundToPadding(int length) - { - return (length + s_padding - 1) / s_padding * s_padding; - } - - /// - /// The bits from the bitset. - /// private UnsafeArray _bits; + private int _highestBit; + private int _max; + + /// + /// The highest uint index in use inside the -array. + /// + public readonly int HighestIndex => _max; /// /// The highest bit set. /// - private int _highestBit; + public readonly int HighestBit => _highestBit; /// - /// The maximum -index current in use. + /// Returns the count of the bitset, how many uints it consists of. /// - private int _max; + public readonly int Count => _bits.Count; + + /// + /// Gets the total number of bits represented by the current instance. + /// + public readonly int BitCount => _bits.Count << _INDEX_SIZE; + + public readonly bool IsCreated => _bits.IsCreated; /// /// Initializes a new instance of the class. /// public UnsafeBitSet() { - _bits = new UnsafeArray(s_padding, Allocator.Persistent, AllocationOption.None); + _bits = new UnsafeArray(0, Allocator.Invalid, AllocationOption.None); } /// @@ -94,33 +85,19 @@ public unsafe struct UnsafeBitSet : IDisposable } } - /// - /// The highest uint index in use inside the -array. - /// - public int HighestIndex + private static int RoundToPadding(int length) { - get => _max; + return (length + s_padding - 1) / s_padding * s_padding; } /// - /// The highest bit set. + /// Determines the required length of an to hold the passed id or bit. /// - public int HighestBit + /// The id or bit. + /// A size of required s for the bitset. + public static int RequiredLength(int id) { - get => _highestBit; - } - - /// - /// Returns the length of the bitset, how many ints it consists of. - /// - public int Length - { - get => _bits.Count; - } - - public bool IsCreated - { - get => _bits.IsCreated; + return (id >> _INDEX_SIZE) + int.Sign(id & _BIT_SIZE); } /// @@ -128,7 +105,7 @@ public unsafe struct UnsafeBitSet : IDisposable /// /// The index. /// True if it is, otherwise false - public bool IsSet(int index) + public readonly bool IsSet(int index) { var b = index >> _INDEX_SIZE; if (b >= _bits.Count) @@ -149,9 +126,10 @@ public unsafe struct UnsafeBitSet : IDisposable var b = index >> _INDEX_SIZE; if (b >= _bits.Count) { - _bits.Resize(RoundToPadding(b)); + _bits.Resize(index); } + // Track highest set bit _highestBit = Math.Max(_highestBit, index); _max = _highestBit / (_BIT_SIZE + 1) + 1; @@ -228,9 +206,12 @@ public unsafe struct UnsafeBitSet : IDisposable public void Resize(int minimalLength, AllocationOption option = AllocationOption.None) { + var oldSize = _bits.Count; var uints = (minimalLength >> _INDEX_SIZE) + int.Sign(minimalLength & _BIT_SIZE); var length = RoundToPadding(uints); + _bits.Resize(length, option); + _bits.AsSpan()[oldSize..].Clear(); } /// @@ -238,10 +219,9 @@ public unsafe struct UnsafeBitSet : IDisposable /// /// The other . /// True if they match, false if not. - [SkipLocalsInit] - public bool All(UnsafeBitSet other) + public readonly bool All(UnsafeBitSet other) { - var min = Math.Min(Math.Min(Length, other.Length), _max); + var min = Math.Min(Math.Min(Count, other.Count), _max); if (!Vector.IsHardwareAccelerated || min < s_padding) { var bits = _bits.AsSpan(); @@ -300,9 +280,9 @@ public unsafe struct UnsafeBitSet : IDisposable /// /// The other . /// True if they match, false if not. - public bool Any(UnsafeBitSet other) + public readonly bool Any(UnsafeBitSet other) { - var min = Math.Min(Math.Min(Length, other.Length), _max); + var min = Math.Min(Math.Min(Count, other.Count), _max); if (!Vector.IsHardwareAccelerated || min < s_padding) { var bits = _bits.AsSpan(); @@ -361,9 +341,9 @@ public unsafe struct UnsafeBitSet : IDisposable /// /// The other . /// True if none match, false if not. - public bool None(UnsafeBitSet other) + public readonly bool None(UnsafeBitSet other) { - var min = Math.Min(Math.Min(Length, other.Length), _max); + var min = Math.Min(Math.Min(Count, other.Count), _max); if (!Vector.IsHardwareAccelerated || min < s_padding) { var bits = _bits.AsSpan(); @@ -403,9 +383,9 @@ public unsafe struct UnsafeBitSet : IDisposable /// /// The other . /// True if they match, false if not. - public bool Exclusive(UnsafeBitSet other) + public readonly bool Exclusive(UnsafeBitSet other) { - var min = Math.Min(Math.Min(Length, other.Length), _max); + var min = Math.Min(Math.Min(Count, other.Count), _max); if (!Vector.IsHardwareAccelerated || min < s_padding) { @@ -460,152 +440,85 @@ public unsafe struct UnsafeBitSet : IDisposable return true; } - public unsafe void AndOperation(UnsafeBitSet other) + public void And(UnsafeBitSet other) { - var min = Math.Min(Length, other.Length); - var temp = stackalloc uint[min]; - - if (!Vector.IsHardwareAccelerated || min < s_padding) + if (Count != other.Count) { - for (var i = 0; i < min; i++) + throw new ArgumentException("Bitsets must be of the same length for AND operation."); + } + + if (!Vector.IsHardwareAccelerated || Count < s_padding) + { + for (var i = 0; i < Count; i++) { - temp[i] = _bits[i] & other._bits[i]; + _bits[i] &= other._bits[i]; } } else { - for (var i = 0; i < min; i += s_padding) + for (var i = 0; i < Count; i += s_padding) { var vectorLeft = new Vector(_bits.AsSpan()[i..]); var vectorRight = new Vector(other._bits.AsSpan()[i..]); var resultVector = Vector.BitwiseAnd(vectorLeft, vectorRight); - resultVector.CopyTo(new Span(temp + i, s_padding)); + + resultVector.CopyTo(_bits.AsSpan(i, s_padding)); } } - - _bits.CopyFrom(new Span(temp, min)); } - public unsafe void OrOperation(UnsafeBitSet other) + public void Or(UnsafeBitSet other) { - var min = Math.Min(Length, other.Length); - var temp = stackalloc uint[min]; - - if (!Vector.IsHardwareAccelerated || min < s_padding) + if (Count != other.Count) { - for (var i = 0; i < min; i++) + throw new ArgumentException("Bitsets must be of the same length for AND operation."); + } + + if (!Vector.IsHardwareAccelerated || Count < s_padding) + { + for (var i = 0; i < Count; i++) { - temp[i] = _bits[i] | other._bits[i]; + _bits[i] |= other._bits[i]; } } else { - for (var i = 0; i < min; i += s_padding) + for (var i = 0; i < Count; i += s_padding) { var vectorLeft = new Vector(_bits.AsSpan()[i..]); var vectorRight = new Vector(other._bits.AsSpan()[i..]); var resultVector = Vector.BitwiseOr(vectorLeft, vectorRight); - resultVector.CopyTo(new Span(temp + i, s_padding)); + + resultVector.CopyTo(_bits.AsSpan(i, s_padding)); } } - - _bits.CopyFrom(new Span(temp, min)); } - public unsafe void XorOperation(UnsafeBitSet other) + public void Xor(UnsafeBitSet other) { - var min = Math.Min(Length, other.Length); - var temp = stackalloc uint[min]; - - if (!Vector.IsHardwareAccelerated || min < s_padding) + if (Count != other.Count) { - for (var i = 0; i < min; i++) + throw new ArgumentException("Bitsets must be of the same length for AND operation."); + } + + if (!Vector.IsHardwareAccelerated || Count < s_padding) + { + for (var i = 0; i < Count; i++) { - temp[i] = _bits[i] ^ other._bits[i]; + _bits[i] ^= other._bits[i]; } } else { - for (var i = 0; i < min; i += s_padding) + for (var i = 0; i < Count; i += s_padding) { var vectorLeft = new Vector(_bits.AsSpan()[i..]); var vectorRight = new Vector(other._bits.AsSpan()[i..]); var resultVector = Vector.Xor(vectorLeft, vectorRight); - resultVector.CopyTo(new Span(temp + i, s_padding)); - } - } - _bits.CopyFrom(new Span(temp, min)); - } - - public static UnsafeBitSet operator &(UnsafeBitSet left, UnsafeBitSet right) - { - var min = Math.Min(left.Length, right.Length); - var result = new UnsafeBitSet(min, Allocator.Persistent); - if (!Vector.IsHardwareAccelerated || min < s_padding) - { - for (var i = 0; i < min; i++) - { - result._bits[i] = left._bits[i] & right._bits[i]; + resultVector.CopyTo(_bits.AsSpan(i, s_padding)); } } - else - { - for (var i = 0; i < min; i += s_padding) - { - var vectorLeft = new Vector(left._bits.AsSpan()[i..]); - var vectorRight = new Vector(right._bits.AsSpan()[i..]); - var resultVector = Vector.BitwiseAnd(vectorLeft, vectorRight); - resultVector.CopyTo(result._bits.AsSpan(i, s_padding)); - } - } - return result; - } - - public static UnsafeBitSet operator |(UnsafeBitSet left, UnsafeBitSet right) - { - var min = Math.Min(left.Length, right.Length); - var result = new UnsafeBitSet(min, Allocator.Persistent); - if (!Vector.IsHardwareAccelerated || min < s_padding) - { - for (var i = 0; i < min; i++) - { - result._bits[i] = left._bits[i] | right._bits[i]; - } - } - else - { - for (var i = 0; i < min; i += s_padding) - { - var vectorLeft = new Vector(left._bits.AsSpan()[i..]); - var vectorRight = new Vector(right._bits.AsSpan()[i..]); - var resultVector = Vector.BitwiseOr(vectorLeft, vectorRight); - resultVector.CopyTo(result._bits.AsSpan(i, s_padding)); - } - } - return result; - } - - public static UnsafeBitSet operator ~(UnsafeBitSet bitSet) - { - if (!Vector.IsHardwareAccelerated || bitSet.Length < s_padding) - { - for (var i = 0; i < bitSet.Length; i++) - { - bitSet._bits[i] = ~bitSet._bits[i]; - } - } - else - { - for (var i = 0; i < bitSet.Length; i += s_padding) - { - var vector = new Vector(bitSet._bits.AsSpan()[i..]); - var resultVector = ~vector; - resultVector.CopyTo(bitSet._bits.AsSpan(i, s_padding)); - } - } - - return bitSet; } /// @@ -624,10 +537,10 @@ public unsafe struct UnsafeBitSet : IDisposable /// The to copy into. /// If true, it will zero the unused space from the . /// The . - public Span AsSpan(Span span, bool zero = true) + public readonly Span AsSpan(Span span, bool zero = true) { // Copy everything thats possible from one to another - var length = Math.Min(Length, span.Length); + var length = Math.Min(Count, span.Length); for (var index = 0; index < length; index++) { span[index] = _bits[index]; @@ -639,10 +552,10 @@ public unsafe struct UnsafeBitSet : IDisposable span[index] = 0; } - return span[..Length]; + return span[..Count]; } - public override string ToString() + public readonly override string ToString() { // Convert uint to binary form for pretty printing var binaryBuilder = new StringBuilder(); @@ -652,7 +565,7 @@ public unsafe struct UnsafeBitSet : IDisposable } binaryBuilder.Length--; - return $"{nameof(_bits)}: {binaryBuilder}, {nameof(Length)}: {Length}"; + return $"{nameof(_bits)}: {binaryBuilder}, {nameof(Count)}: {Count}"; } public void Dispose() diff --git a/Misaki.HighPerformance.LowLevel/Collections/UnsafeSlotMap.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeSlotMap.cs index 165e283..da64ba7 100644 --- a/Misaki.HighPerformance.LowLevel/Collections/UnsafeSlotMap.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeSlotMap.cs @@ -57,7 +57,7 @@ public unsafe struct UnsafeSlotMap : IUnsafeCollection public readonly int Count => _count; public readonly int Capacity => _capacity; - public readonly bool IsCreated => _data.IsCreated && _freeSlots.IsCreated; + public readonly bool IsCreated => _data.IsCreated && _generations.IsCreated && _freeSlots.IsCreated && _validBits.IsCreated; public Enumerator GetEnumerator() => new((UnsafeSlotMap*)UnsafeUtility.AddressOf(ref this)); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -239,7 +239,7 @@ public unsafe struct UnsafeSlotMap : IUnsafeCollection throw new ArgumentOutOfRangeException(nameof(slotIndex), "Slot index is out of range."); } - if (!_validBits.IsSet(slotIndex)|| _generations[slotIndex] != generation) + if (!_validBits.IsSet(slotIndex) || _generations[slotIndex] != generation) { throw new InvalidOperationException($"Slot {slotIndex} is not occupied."); } @@ -294,7 +294,7 @@ public unsafe struct UnsafeSlotMap : IUnsafeCollection _count = 0; } - public readonly unsafe void* GetUnsafePtr() + public readonly void* GetUnsafePtr() { return _data.GetUnsafePtr(); } @@ -303,6 +303,7 @@ public unsafe struct UnsafeSlotMap : IUnsafeCollection { _data.Dispose(); _freeSlots.Dispose(); + _validBits.Dispose(); _count = 0; _capacity = 0; diff --git a/Misaki.HighPerformance.LowLevel/MemoryLeakException.cs b/Misaki.HighPerformance.LowLevel/MemoryLeakException.cs index 69a002d..a07ec28 100644 --- a/Misaki.HighPerformance.LowLevel/MemoryLeakException.cs +++ b/Misaki.HighPerformance.LowLevel/MemoryLeakException.cs @@ -10,12 +10,21 @@ namespace Misaki.HighPerformance.LowLevel; /// An array of AllocationInfo containing details about the memory leaks. public class MemoryLeakException : Exception { - private readonly IEnumerable? _infos; - private readonly string _message = string.Empty; + private readonly string _message; - public MemoryLeakException(IEnumerable infos) + public override string Message => _message; + + public MemoryLeakException(ReadOnlySpan infos) { - _infos = infos; + var stringBuilder = new StringBuilder(); + stringBuilder.AppendLine($"Found {infos.Length} memory lakes!"); + + foreach (var info in infos) + { + stringBuilder.AppendLine(GetMessage(info.StackTrace)); + } + + _message = stringBuilder.ToString(); } public MemoryLeakException(string message) @@ -44,25 +53,4 @@ public class MemoryLeakException : Exception return stringBuilder.ToString(); } - - public override string Message - { - get - { - if (_infos == null) - { - return _message; - } - - var stringBuilder = new StringBuilder(); - stringBuilder.AppendLine($"Found {_infos.Count()} memory lakes!"); - - foreach (var info in _infos) - { - stringBuilder.AppendLine(GetMessage(info.StackTrace)); - } - - return stringBuilder.ToString(); - } - } } \ No newline at end of file diff --git a/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj b/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj index d83aca1..7cf9f5f 100644 --- a/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj +++ b/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable True @@ -21,10 +21,6 @@ True - - - - TextTemplatingFileGenerator diff --git a/Misaki.HighPerformance.Mathematics.CodeGen/Generators/VectorGenerator.cs b/Misaki.HighPerformance.Mathematics.CodeGen/Generators/VectorGenerator.cs index b429c16..91298df 100644 --- a/Misaki.HighPerformance.Mathematics.CodeGen/Generators/VectorGenerator.cs +++ b/Misaki.HighPerformance.Mathematics.CodeGen/Generators/VectorGenerator.cs @@ -422,7 +422,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators return new {typeName}({string.Join(", ", s_vectorComponents.Take(typeInfo.Row).Select(c => $"lhs + rhs.{c}"))}); }} -#if false //NET10_0_OR_GREATER +#if NET10_0_OR_GREATER {INLINE_METHOD_ATTRIBUTE} public void operator +=({typeName} other) {{"); diff --git a/Misaki.HighPerformance.Mathematics/Misaki.HighPerformance.Mathematics.csproj b/Misaki.HighPerformance.Mathematics/Misaki.HighPerformance.Mathematics.csproj index 13f0af6..8826c5a 100644 --- a/Misaki.HighPerformance.Mathematics/Misaki.HighPerformance.Mathematics.csproj +++ b/Misaki.HighPerformance.Mathematics/Misaki.HighPerformance.Mathematics.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable True @@ -13,6 +13,14 @@ https://git.personalnas.com/Misaki/Misaki.HighPerformance.git + + True + + + + True + + diff --git a/Misaki.HighPerformance.Test/Benchmark/MathematicsBenchmark.cs b/Misaki.HighPerformance.Test/Benchmark/MathematicsBenchmark.cs index 5d96feb..e730ed5 100644 --- a/Misaki.HighPerformance.Test/Benchmark/MathematicsBenchmark.cs +++ b/Misaki.HighPerformance.Test/Benchmark/MathematicsBenchmark.cs @@ -1,7 +1,6 @@ -using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; using Misaki.HighPerformance.Mathematics; using System.Numerics; -using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; namespace Misaki.HighPerformance.Test.Benchmark; @@ -25,7 +24,7 @@ public unsafe class MathematicsBenchmark public static f4 operator +(f4 a, f4 b) { var result = a._vec + b._vec; - return Unsafe.As, f4>(ref result); + return new f4(result); } } @@ -108,7 +107,7 @@ public unsafe class MathematicsBenchmark } [Benchmark] - public unsafe Vector128 v128Add() + public Vector128 v128Add() { var a = Vector128.Create(1f, 2f, 3f, 4f); var b = Vector128.Create(5f, 6f, 7f, 8f); diff --git a/Misaki.HighPerformance.Test/Misaki.HighPerformance.Test.csproj b/Misaki.HighPerformance.Test/Misaki.HighPerformance.Test.csproj index 2293916..38ec73f 100644 --- a/Misaki.HighPerformance.Test/Misaki.HighPerformance.Test.csproj +++ b/Misaki.HighPerformance.Test/Misaki.HighPerformance.Test.csproj @@ -2,7 +2,7 @@ Exe - net9.0 + net10.0 enable enable True diff --git a/Misaki.HighPerformance.Test/Program.cs b/Misaki.HighPerformance.Test/Program.cs index 42157eb..629061a 100644 --- a/Misaki.HighPerformance.Test/Program.cs +++ b/Misaki.HighPerformance.Test/Program.cs @@ -20,7 +20,7 @@ //using Misaki.HighPerformance.Test.Benchmark; -//BenchmarkDotNet.Running.BenchmarkRunner.Run(); +BenchmarkDotNet.Running.BenchmarkRunner.Run(); //using Misaki.HighPerformance.LowLevel.Buffer; //using Misaki.HighPerformance.LowLevel.Collections; @@ -40,17 +40,3 @@ // } //} -using Misaki.HighPerformance.LowLevel.Buffer; -using Misaki.HighPerformance.LowLevel.Collections; - -//AllocationManager.EnableDebugLayer(); -//var array = new UnsafeArray(10, Allocator.Persistent); -//var array2 = new UnsafeArray(10, Allocator.Persistent); -//array.Dispose(); -//array2.Dispose(); -//AllocationManager.Dispose(); - -using (AllocationManager.CreateStackScope()) -{ - var arr = new UnsafeArray(10, Allocator.Stack); -} \ No newline at end of file diff --git a/Misaki.HighPerformance.Test/UnitTest/Collections/TestUnsafeBitSet.cs b/Misaki.HighPerformance.Test/UnitTest/Collections/TestUnsafeBitSet.cs new file mode 100644 index 0000000..96053c9 --- /dev/null +++ b/Misaki.HighPerformance.Test/UnitTest/Collections/TestUnsafeBitSet.cs @@ -0,0 +1,78 @@ +using Misaki.HighPerformance.LowLevel.Buffer; +using Misaki.HighPerformance.LowLevel.Collections; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Serialization; + +namespace Misaki.HighPerformance.Test.UnitTest.Collections; + +[TestClass] +public class TestUnsafeBitSet +{ + private UnsafeBitSet _set1; + private UnsafeBitSet _set2; + + [TestInitialize] + public void Initialize() + { + _set1 = new UnsafeBitSet(16, Allocator.Persistent, AllocationOption.Clear); + _set2 = new UnsafeBitSet(16, Allocator.Persistent, AllocationOption.Clear); + } + + [TestCleanup] + public void Cleanup() + { + _set1.Dispose(); + _set2.Dispose(); + } + + [TestMethod] + public void TestBitCount() + { + Assert.AreEqual(256, _set1.BitCount); + } + + [TestMethod] + public void TestSetAndGet() + { + Assert.IsFalse(_set1.IsSet(0)); + _set1.SetBit(0); + Assert.IsTrue(_set1.IsSet(0)); + _set1.ClearBit(0); + Assert.IsFalse(_set1.IsSet(0)); + } + + [TestMethod] + public void TestClearAll() + { + for (int i = 0; i < _set1.BitCount; i++) + { + _set1.SetBit(i); + } + + _set1.ClearAll(); + for (int i = 0; i < _set1.BitCount; i++) + { + Assert.IsFalse(_set1.IsSet(i)); + } + } + + [TestMethod] + public void TestAndOperation() + { + _set1.SetBit(0); + _set1.SetBit(1); + + _set2.SetBit(1); + _set2.SetBit(2); + + _set1.And(_set2); + + Assert.IsFalse(_set1.IsSet(0)); + Assert.IsTrue(_set1.IsSet(1)); + Assert.IsFalse(_set1.IsSet(2)); + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Test/UnitTest/Collections/TestUnsafeSlotMap.cs b/Misaki.HighPerformance.Test/UnitTest/Collections/TestUnsafeSlotMap.cs index 626c73e..eb7741b 100644 --- a/Misaki.HighPerformance.Test/UnitTest/Collections/TestUnsafeSlotMap.cs +++ b/Misaki.HighPerformance.Test/UnitTest/Collections/TestUnsafeSlotMap.cs @@ -11,7 +11,7 @@ public class TestUnsafeSlotMap [TestInitialize] public void Initialize() { - _slotMap = new UnsafeSlotMap(16, Allocator.Persistent, AllocationOption.Clear); + _slotMap = new UnsafeSlotMap(16, Allocator.Persistent); } [TestCleanup] diff --git a/Misaki.HighPerformance/Collections/ConcureentSlotMap.cs b/Misaki.HighPerformance/Collections/ConcureentSlotMap.cs index 8498e64..1a6f1b8 100644 --- a/Misaki.HighPerformance/Collections/ConcureentSlotMap.cs +++ b/Misaki.HighPerformance/Collections/ConcureentSlotMap.cs @@ -1,4 +1,4 @@ -using System.Collections; +using System.Collections; using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -207,7 +207,7 @@ public class ConcurrentSlotMap : IEnumerable return false; // Another thread already removed it } - public bool Contain(int slotIndex, int generation) + public bool Contains(int slotIndex, int generation) { if (slotIndex < 0 || slotIndex >= Volatile.Read(ref _capacity)) { diff --git a/Misaki.HighPerformance/Misaki.HighPerformance.csproj b/Misaki.HighPerformance/Misaki.HighPerformance.csproj index cd3809e..966abd0 100644 --- a/Misaki.HighPerformance/Misaki.HighPerformance.csproj +++ b/Misaki.HighPerformance/Misaki.HighPerformance.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable True