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`.
This commit is contained in:
2025-11-14 11:14:09 +09:00
parent bf4dd5670e
commit 24a7d49ae2
18 changed files with 223 additions and 260 deletions

View File

@@ -3,7 +3,7 @@
<TargetFrameworks></TargetFrameworks>
<PackageLicenseUrl>Public Domain</PackageLicenseUrl>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Authors>Misaki</Authors>
<AssemblyVersion>1.0.0</AssemblyVersion>
<Version>$(AssemblyVersion)</Version>

View File

@@ -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;
/// <summary>
/// Initializes a new instance of the <see cref="JobScheduler"/> class with the specified number of worker threads.
/// </summary>
@@ -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;

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
@@ -14,8 +14,17 @@
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<IsAotCompatible>True</IsAotCompatible>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<IsAotCompatible>True</IsAotCompatible>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Misaki.HighPerformance.LowLevel\Misaki.HighPerformance.LowLevel.csproj" />
<ProjectReference Include="..\Misaki.HighPerformance\Misaki.HighPerformance.csproj" />
</ItemGroup>
</Project>

View File

@@ -8,7 +8,7 @@ namespace Misaki.HighPerformance.LowLevel.Buffer;
/// <summary>
/// Holds information about a memory allocation.
/// </summary>
public readonly unsafe struct AllocationInfo
public readonly struct AllocationInfo
{
/// <summary>
/// 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,13 +438,11 @@ public static unsafe class AllocationManager
}
var newPtr = AlignedRealloc(ptr, newSize, alignment);
if (allocationOption.HasFlag(AllocationOption.Clear))
{
if (newSize > oldSize)
if (allocationOption.HasFlag(AllocationOption.Clear)
&& 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)

View File

@@ -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<T> : IEnumerable<T>
{
private readonly ReadOnlyUnsafeCollection<T> _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<T> array)
{
_collection = array;
_index = -1;
_value = default;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
_index++;
if (_index < _collection.Count)
{
_value = UnsafeUtility.ReadArrayElement<T>(_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<T> : IEnumerable<T>
get => UnsafeUtility.ReadArrayElement<T>(_buffer, index);
}
public IEnumerator<T> GetEnumerator() => new Enumerator(in this);
public Enumerator GetEnumerator() => new Enumerator(in this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public ReadOnlyUnsafeCollection(T* buffer, int count)

View File

@@ -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<uint>.Count; // The padding used for vectorization, the amount of uints required for being vectorized basically
/// <summary>
/// Determines the required length of an <see cref="UnsafeBitSet"/> to hold the passed id or bit.
/// </summary>
/// <param name="id">The id or bit.</param>
/// <returns>A size of required <see cref="uint"/>s for the bitset.</returns>
public static int RequiredLength(int id)
{
return (id >> 5) + int.Sign(id & _BIT_SIZE);
}
/// <summary>
/// Rounds the given length to the next padding size.
/// </summary>
/// <param name="length">The length to round.</param>
/// <returns>The rounded length.</returns>
public static int RoundToPadding(int length)
{
return (length + s_padding - 1) / s_padding * s_padding;
}
/// <summary>
/// The bits from the bitset.
/// </summary>
private UnsafeArray<uint> _bits;
private int _highestBit;
private int _max;
/// <summary>
/// The highest uint index in use inside the <see cref="_bits"/>-array.
/// </summary>
public readonly int HighestIndex => _max;
/// <summary>
/// The highest bit set.
/// </summary>
private int _highestBit;
public readonly int HighestBit => _highestBit;
/// <summary>
/// The maximum <see cref="_bits"/>-index current in use.
/// Returns the count of the bitset, how many uints it consists of.
/// </summary>
private int _max;
public readonly int Count => _bits.Count;
/// <summary>
/// Gets the total number of bits represented by the current instance.
/// </summary>
public readonly int BitCount => _bits.Count << _INDEX_SIZE;
public readonly bool IsCreated => _bits.IsCreated;
/// <summary>
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
/// </summary>
public UnsafeBitSet()
{
_bits = new UnsafeArray<uint>(s_padding, Allocator.Persistent, AllocationOption.None);
_bits = new UnsafeArray<uint>(0, Allocator.Invalid, AllocationOption.None);
}
/// <summary>
@@ -94,33 +85,19 @@ public unsafe struct UnsafeBitSet : IDisposable
}
}
/// <summary>
/// The highest uint index in use inside the <see cref="_bits"/>-array.
/// </summary>
public int HighestIndex
private static int RoundToPadding(int length)
{
get => _max;
return (length + s_padding - 1) / s_padding * s_padding;
}
/// <summary>
/// The highest bit set.
/// Determines the required length of an <see cref="UnsafeBitSet"/> to hold the passed id or bit.
/// </summary>
public int HighestBit
/// <param name="id">The id or bit.</param>
/// <returns>A size of required <see cref="uint"/>s for the bitset.</returns>
public static int RequiredLength(int id)
{
get => _highestBit;
}
/// <summary>
/// Returns the length of the bitset, how many ints it consists of.
/// </summary>
public int Length
{
get => _bits.Count;
}
public bool IsCreated
{
get => _bits.IsCreated;
return (id >> _INDEX_SIZE) + int.Sign(id & _BIT_SIZE);
}
/// <summary>
@@ -128,7 +105,7 @@ public unsafe struct UnsafeBitSet : IDisposable
/// </summary>
/// <param name="index">The index.</param>
/// <returns>True if it is, otherwise false</returns>
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();
}
/// <summary>
@@ -238,10 +219,9 @@ public unsafe struct UnsafeBitSet : IDisposable
/// </summary>
/// <param name="other">The other <see cref="UnsafeBitSet"/>.</param>
/// <returns>True if they match, false if not.</returns>
[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
/// </summary>
/// <param name="other">The other <see cref="UnsafeBitSet"/>.</param>
/// <returns>True if they match, false if not.</returns>
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
/// </summary>
/// <param name="other">The other <see cref="UnsafeBitSet"/>.</param>
/// <returns>True if none match, false if not.</returns>
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
/// </summary>
/// <param name="other">The other <see cref="UnsafeBitSet"/>.</param>
/// <returns>True if they match, false if not.</returns>
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 (Count != other.Count)
{
throw new ArgumentException("Bitsets must be of the same length for AND operation.");
}
if (!Vector.IsHardwareAccelerated || min < s_padding)
if (!Vector.IsHardwareAccelerated || Count < s_padding)
{
for (var i = 0; i < min; i++)
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<uint>(_bits.AsSpan()[i..]);
var vectorRight = new Vector<uint>(other._bits.AsSpan()[i..]);
var resultVector = Vector.BitwiseAnd(vectorLeft, vectorRight);
resultVector.CopyTo(new Span<uint>(temp + i, s_padding));
resultVector.CopyTo(_bits.AsSpan(i, s_padding));
}
}
}
_bits.CopyFrom(new Span<uint>(temp, min));
public void Or(UnsafeBitSet other)
{
if (Count != other.Count)
{
throw new ArgumentException("Bitsets must be of the same length for AND operation.");
}
public unsafe void OrOperation(UnsafeBitSet other)
if (!Vector.IsHardwareAccelerated || Count < s_padding)
{
var min = Math.Min(Length, other.Length);
var temp = stackalloc uint[min];
if (!Vector.IsHardwareAccelerated || min < s_padding)
for (var i = 0; i < Count; i++)
{
for (var i = 0; i < min; 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<uint>(_bits.AsSpan()[i..]);
var vectorRight = new Vector<uint>(other._bits.AsSpan()[i..]);
var resultVector = Vector.BitwiseOr(vectorLeft, vectorRight);
resultVector.CopyTo(new Span<uint>(temp + i, s_padding));
resultVector.CopyTo(_bits.AsSpan(i, s_padding));
}
}
}
_bits.CopyFrom(new Span<uint>(temp, min));
public void Xor(UnsafeBitSet other)
{
if (Count != other.Count)
{
throw new ArgumentException("Bitsets must be of the same length for AND operation.");
}
public unsafe void XorOperation(UnsafeBitSet other)
if (!Vector.IsHardwareAccelerated || Count < s_padding)
{
var min = Math.Min(Length, other.Length);
var temp = stackalloc uint[min];
if (!Vector.IsHardwareAccelerated || min < s_padding)
for (var i = 0; i < Count; i++)
{
for (var i = 0; i < min; 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<uint>(_bits.AsSpan()[i..]);
var vectorRight = new Vector<uint>(other._bits.AsSpan()[i..]);
var resultVector = Vector.Xor(vectorLeft, vectorRight);
resultVector.CopyTo(new Span<uint>(temp + i, s_padding));
}
}
_bits.CopyFrom(new Span<uint>(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<uint>(left._bits.AsSpan()[i..]);
var vectorRight = new Vector<uint>(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<uint>(left._bits.AsSpan()[i..]);
var vectorRight = new Vector<uint>(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<uint>(bitSet._bits.AsSpan()[i..]);
var resultVector = ~vector;
resultVector.CopyTo(bitSet._bits.AsSpan(i, s_padding));
}
}
return bitSet;
}
/// <summary>
@@ -624,10 +537,10 @@ public unsafe struct UnsafeBitSet : IDisposable
/// <param name="span">The <see cref="Span{T}"/> to copy into.</param>
/// <param name="zero">If true, it will zero the unused space from the <see cref="span"/>.</param>
/// <returns>The <see cref="Span{T}"/>.</returns>
public Span<uint> AsSpan(Span<uint> span, bool zero = true)
public readonly Span<uint> AsSpan(Span<uint> 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()

View File

@@ -57,7 +57,7 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
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<T>*)UnsafeUtility.AddressOf(ref this));
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
@@ -239,7 +239,7 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
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<T> : IUnsafeCollection<T>
_count = 0;
}
public readonly unsafe void* GetUnsafePtr()
public readonly void* GetUnsafePtr()
{
return _data.GetUnsafePtr();
}
@@ -303,6 +303,7 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
{
_data.Dispose();
_freeSlots.Dispose();
_validBits.Dispose();
_count = 0;
_capacity = 0;

View File

@@ -10,12 +10,21 @@ namespace Misaki.HighPerformance.LowLevel;
/// <param name="Infos">An array of AllocationInfo containing details about the memory leaks.</param>
public class MemoryLeakException : Exception
{
private readonly IEnumerable<AllocationInfo>? _infos;
private readonly string _message = string.Empty;
private readonly string _message;
public MemoryLeakException(IEnumerable<AllocationInfo> infos)
public override string Message => _message;
public MemoryLeakException(ReadOnlySpan<AllocationInfo> 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();
}
}
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
@@ -21,10 +21,6 @@
<IsAotCompatible>True</IsAotCompatible>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Misaki.HighPerformance\Misaki.HighPerformance.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Collections\FixedText.tt">
<Generator>TextTemplatingFileGenerator</Generator>

View File

@@ -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)
{{");

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
@@ -13,6 +13,14 @@
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<IsAotCompatible>True</IsAotCompatible>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<IsAotCompatible>True</IsAotCompatible>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Misaki.HighPerformance.Mathematics.CodeGen\Misaki.HighPerformance.Mathematics.CodeGen.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>

View File

@@ -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<Vector128<float>, f4>(ref result);
return new f4(result);
}
}
@@ -108,7 +107,7 @@ public unsafe class MathematicsBenchmark
}
[Benchmark]
public unsafe Vector128<float> v128Add()
public Vector128<float> v128Add()
{
var a = Vector128.Create(1f, 2f, 3f, 4f);
var b = Vector128.Create(5f, 6f, 7f, 8f);

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishAot>True</PublishAot>

View File

@@ -20,7 +20,7 @@
//using Misaki.HighPerformance.Test.Benchmark;
//BenchmarkDotNet.Running.BenchmarkRunner.Run<MathematicsBenchmark>();
BenchmarkDotNet.Running.BenchmarkRunner.Run<Misaki.HighPerformance.Test.Benchmark.MathematicsBenchmark>();
//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<int>(10, Allocator.Persistent);
//var array2 = new UnsafeArray<int>(10, Allocator.Persistent);
//array.Dispose();
//array2.Dispose();
//AllocationManager.Dispose();
using (AllocationManager.CreateStackScope())
{
var arr = new UnsafeArray<int>(10, Allocator.Stack);
}

View File

@@ -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));
}
}

View File

@@ -11,7 +11,7 @@ public class TestUnsafeSlotMap
[TestInitialize]
public void Initialize()
{
_slotMap = new UnsafeSlotMap<int>(16, Allocator.Persistent, AllocationOption.Clear);
_slotMap = new UnsafeSlotMap<int>(16, Allocator.Persistent);
}
[TestCleanup]

View File

@@ -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<T> : IEnumerable<T>
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))
{

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>