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:
@@ -3,7 +3,7 @@
|
|||||||
<TargetFrameworks></TargetFrameworks>
|
<TargetFrameworks></TargetFrameworks>
|
||||||
<PackageLicenseUrl>Public Domain</PackageLicenseUrl>
|
<PackageLicenseUrl>Public Domain</PackageLicenseUrl>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Authors>Misaki</Authors>
|
<Authors>Misaki</Authors>
|
||||||
<AssemblyVersion>1.0.0</AssemblyVersion>
|
<AssemblyVersion>1.0.0</AssemblyVersion>
|
||||||
<Version>$(AssemblyVersion)</Version>
|
<Version>$(AssemblyVersion)</Version>
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ public sealed unsafe class JobScheduler : IDisposable
|
|||||||
|
|
||||||
private bool _disposed = false;
|
private bool _disposed = false;
|
||||||
|
|
||||||
public int WorkerCount => _workerThreads.Length;
|
|
||||||
|
|
||||||
internal bool IsCancellationRequested => _cts.IsCancellationRequested;
|
internal bool IsCancellationRequested => _cts.IsCancellationRequested;
|
||||||
|
|
||||||
|
public int WorkerCount => _workerThreads.Length;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="JobScheduler"/> class with the specified number of worker threads.
|
/// Initializes a new instance of the <see cref="JobScheduler"/> class with the specified number of worker threads.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -481,7 +481,7 @@ public sealed unsafe class JobScheduler : IDisposable
|
|||||||
var completedCount = 0;
|
var completedCount = 0;
|
||||||
foreach (var handle in handles)
|
foreach (var handle in handles)
|
||||||
{
|
{
|
||||||
if (!_jobInfoPool.Contain(handle._id, handle._generation))
|
if (!_jobInfoPool.Contains(handle._id, handle._generation))
|
||||||
{
|
{
|
||||||
completedCount++;
|
completedCount++;
|
||||||
}
|
}
|
||||||
@@ -514,7 +514,7 @@ public sealed unsafe class JobScheduler : IDisposable
|
|||||||
{
|
{
|
||||||
foreach (var handle in handles)
|
foreach (var handle in handles)
|
||||||
{
|
{
|
||||||
if (!_jobInfoPool.Contain(handle._id, handle._generation))
|
if (!_jobInfoPool.Contains(handle._id, handle._generation))
|
||||||
{
|
{
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
@@ -542,6 +542,7 @@ public sealed unsafe class JobScheduler : IDisposable
|
|||||||
_jobQueue.Clear();
|
_jobQueue.Clear();
|
||||||
_jobDataAllocator.Dispose();
|
_jobDataAllocator.Dispose();
|
||||||
|
|
||||||
|
_workSignal.Dispose();
|
||||||
_cts.Dispose();
|
_cts.Dispose();
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
@@ -14,8 +14,17 @@
|
|||||||
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>
|
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<IsAotCompatible>True</IsAotCompatible>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<IsAotCompatible>True</IsAotCompatible>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Misaki.HighPerformance.LowLevel\Misaki.HighPerformance.LowLevel.csproj" />
|
<ProjectReference Include="..\Misaki.HighPerformance.LowLevel\Misaki.HighPerformance.LowLevel.csproj" />
|
||||||
|
<ProjectReference Include="..\Misaki.HighPerformance\Misaki.HighPerformance.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Misaki.HighPerformance.LowLevel.Buffer;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Holds information about a memory allocation.
|
/// Holds information about a memory allocation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly unsafe struct AllocationInfo
|
public readonly struct AllocationInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the size of the allocation in bytes.
|
/// Get the size of the allocation in bytes.
|
||||||
@@ -69,7 +69,7 @@ public static unsafe class AllocationManager
|
|||||||
{
|
{
|
||||||
var selfPtr = (ArenaAllocator*)instance;
|
var selfPtr = (ArenaAllocator*)instance;
|
||||||
var newPtr = selfPtr->_arena.Allocate(newSize, alignment, allocationOption);
|
var newPtr = selfPtr->_arena.Allocate(newSize, alignment, allocationOption);
|
||||||
MemCpy(newPtr, ptr, newSize);
|
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
||||||
|
|
||||||
if (allocationOption.HasFlag(AllocationOption.Clear))
|
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)
|
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);
|
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))
|
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 ArenaAllocator* s_pArenaAllocator;
|
||||||
private static readonly HeapAllocator* s_pHeapAllocator;
|
private static readonly HeapAllocator* s_pHeapAllocator;
|
||||||
@@ -438,12 +438,10 @@ public static unsafe class AllocationManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
var newPtr = AlignedRealloc(ptr, newSize, alignment);
|
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;
|
return newPtr;
|
||||||
@@ -535,7 +533,7 @@ public static unsafe class AllocationManager
|
|||||||
|
|
||||||
if (unfreeBytes > 0u)
|
if (unfreeBytes > 0u)
|
||||||
{
|
{
|
||||||
throw new MemoryLeakException(snapshot);
|
throw new MemoryLeakException(CollectionsMarshal.AsSpan(snapshot));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (s_activeHeapAllocations != 0)
|
else if (s_activeHeapAllocations != 0)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@@ -20,36 +20,21 @@ public readonly unsafe struct ReadOnlyUnsafeCollection<T> : IEnumerable<T>
|
|||||||
{
|
{
|
||||||
private readonly ReadOnlyUnsafeCollection<T> _collection;
|
private readonly ReadOnlyUnsafeCollection<T> _collection;
|
||||||
private int _index;
|
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;
|
readonly object IEnumerator.Current => Current;
|
||||||
|
|
||||||
public Enumerator(ref readonly ReadOnlyUnsafeCollection<T> array)
|
public Enumerator(ref readonly ReadOnlyUnsafeCollection<T> array)
|
||||||
{
|
{
|
||||||
_collection = array;
|
_collection = array;
|
||||||
_index = -1;
|
_index = -1;
|
||||||
_value = default;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool MoveNext()
|
public bool MoveNext()
|
||||||
{
|
{
|
||||||
_index++;
|
_index++;
|
||||||
|
return _index < _collection.Count;
|
||||||
if (_index < _collection.Count)
|
|
||||||
{
|
|
||||||
_value = UnsafeUtility.ReadArrayElement<T>(_collection._buffer, _index);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_value = default;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
@@ -79,7 +64,8 @@ public readonly unsafe struct ReadOnlyUnsafeCollection<T> : IEnumerable<T>
|
|||||||
get => UnsafeUtility.ReadArrayElement<T>(_buffer, index);
|
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();
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
public ReadOnlyUnsafeCollection(T* buffer, int count)
|
public ReadOnlyUnsafeCollection(T* buffer, int count)
|
||||||
|
|||||||
@@ -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 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
|
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 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>
|
/// <summary>
|
||||||
/// The highest bit set.
|
/// The highest bit set.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private int _highestBit;
|
public readonly int HighestBit => _highestBit;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum <see cref="_bits"/>-index current in use.
|
/// Returns the count of the bitset, how many uints it consists of.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
|
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public UnsafeBitSet()
|
public UnsafeBitSet()
|
||||||
{
|
{
|
||||||
_bits = new UnsafeArray<uint>(s_padding, Allocator.Persistent, AllocationOption.None);
|
_bits = new UnsafeArray<uint>(0, Allocator.Invalid, AllocationOption.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -94,33 +85,19 @@ public unsafe struct UnsafeBitSet : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private static int RoundToPadding(int length)
|
||||||
/// The highest uint index in use inside the <see cref="_bits"/>-array.
|
|
||||||
/// </summary>
|
|
||||||
public int HighestIndex
|
|
||||||
{
|
{
|
||||||
get => _max;
|
return (length + s_padding - 1) / s_padding * s_padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The highest bit set.
|
/// Determines the required length of an <see cref="UnsafeBitSet"/> to hold the passed id or bit.
|
||||||
/// </summary>
|
/// </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;
|
return (id >> _INDEX_SIZE) + int.Sign(id & _BIT_SIZE);
|
||||||
}
|
|
||||||
|
|
||||||
/// <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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -128,7 +105,7 @@ public unsafe struct UnsafeBitSet : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="index">The index.</param>
|
/// <param name="index">The index.</param>
|
||||||
/// <returns>True if it is, otherwise false</returns>
|
/// <returns>True if it is, otherwise false</returns>
|
||||||
public bool IsSet(int index)
|
public readonly bool IsSet(int index)
|
||||||
{
|
{
|
||||||
var b = index >> _INDEX_SIZE;
|
var b = index >> _INDEX_SIZE;
|
||||||
if (b >= _bits.Count)
|
if (b >= _bits.Count)
|
||||||
@@ -149,9 +126,10 @@ public unsafe struct UnsafeBitSet : IDisposable
|
|||||||
var b = index >> _INDEX_SIZE;
|
var b = index >> _INDEX_SIZE;
|
||||||
if (b >= _bits.Count)
|
if (b >= _bits.Count)
|
||||||
{
|
{
|
||||||
_bits.Resize(RoundToPadding(b));
|
_bits.Resize(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Track highest set bit
|
// Track highest set bit
|
||||||
_highestBit = Math.Max(_highestBit, index);
|
_highestBit = Math.Max(_highestBit, index);
|
||||||
_max = _highestBit / (_BIT_SIZE + 1) + 1;
|
_max = _highestBit / (_BIT_SIZE + 1) + 1;
|
||||||
@@ -228,9 +206,12 @@ public unsafe struct UnsafeBitSet : IDisposable
|
|||||||
|
|
||||||
public void Resize(int minimalLength, AllocationOption option = AllocationOption.None)
|
public void Resize(int minimalLength, AllocationOption option = AllocationOption.None)
|
||||||
{
|
{
|
||||||
|
var oldSize = _bits.Count;
|
||||||
var uints = (minimalLength >> _INDEX_SIZE) + int.Sign(minimalLength & _BIT_SIZE);
|
var uints = (minimalLength >> _INDEX_SIZE) + int.Sign(minimalLength & _BIT_SIZE);
|
||||||
var length = RoundToPadding(uints);
|
var length = RoundToPadding(uints);
|
||||||
|
|
||||||
_bits.Resize(length, option);
|
_bits.Resize(length, option);
|
||||||
|
_bits.AsSpan()[oldSize..].Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -238,10 +219,9 @@ public unsafe struct UnsafeBitSet : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="other">The other <see cref="UnsafeBitSet"/>.</param>
|
/// <param name="other">The other <see cref="UnsafeBitSet"/>.</param>
|
||||||
/// <returns>True if they match, false if not.</returns>
|
/// <returns>True if they match, false if not.</returns>
|
||||||
[SkipLocalsInit]
|
public readonly bool All(UnsafeBitSet other)
|
||||||
public 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)
|
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
||||||
{
|
{
|
||||||
var bits = _bits.AsSpan();
|
var bits = _bits.AsSpan();
|
||||||
@@ -300,9 +280,9 @@ public unsafe struct UnsafeBitSet : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="other">The other <see cref="UnsafeBitSet"/>.</param>
|
/// <param name="other">The other <see cref="UnsafeBitSet"/>.</param>
|
||||||
/// <returns>True if they match, false if not.</returns>
|
/// <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)
|
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
||||||
{
|
{
|
||||||
var bits = _bits.AsSpan();
|
var bits = _bits.AsSpan();
|
||||||
@@ -361,9 +341,9 @@ public unsafe struct UnsafeBitSet : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="other">The other <see cref="UnsafeBitSet"/>.</param>
|
/// <param name="other">The other <see cref="UnsafeBitSet"/>.</param>
|
||||||
/// <returns>True if none match, false if not.</returns>
|
/// <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)
|
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
||||||
{
|
{
|
||||||
var bits = _bits.AsSpan();
|
var bits = _bits.AsSpan();
|
||||||
@@ -403,9 +383,9 @@ public unsafe struct UnsafeBitSet : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="other">The other <see cref="UnsafeBitSet"/>.</param>
|
/// <param name="other">The other <see cref="UnsafeBitSet"/>.</param>
|
||||||
/// <returns>True if they match, false if not.</returns>
|
/// <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)
|
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
||||||
{
|
{
|
||||||
@@ -460,152 +440,85 @@ public unsafe struct UnsafeBitSet : IDisposable
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void AndOperation(UnsafeBitSet other)
|
public void And(UnsafeBitSet other)
|
||||||
{
|
{
|
||||||
var min = Math.Min(Length, other.Length);
|
if (Count != other.Count)
|
||||||
var temp = stackalloc uint[min];
|
|
||||||
|
|
||||||
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
|
||||||
{
|
{
|
||||||
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
|
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 vectorLeft = new Vector<uint>(_bits.AsSpan()[i..]);
|
||||||
var vectorRight = new Vector<uint>(other._bits.AsSpan()[i..]);
|
var vectorRight = new Vector<uint>(other._bits.AsSpan()[i..]);
|
||||||
var resultVector = Vector.BitwiseAnd(vectorLeft, vectorRight);
|
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 unsafe void OrOperation(UnsafeBitSet other)
|
public void Or(UnsafeBitSet other)
|
||||||
{
|
{
|
||||||
var min = Math.Min(Length, other.Length);
|
if (Count != other.Count)
|
||||||
var temp = stackalloc uint[min];
|
|
||||||
|
|
||||||
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
|
||||||
{
|
{
|
||||||
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
|
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 vectorLeft = new Vector<uint>(_bits.AsSpan()[i..]);
|
||||||
var vectorRight = new Vector<uint>(other._bits.AsSpan()[i..]);
|
var vectorRight = new Vector<uint>(other._bits.AsSpan()[i..]);
|
||||||
var resultVector = Vector.BitwiseOr(vectorLeft, vectorRight);
|
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 unsafe void XorOperation(UnsafeBitSet other)
|
public void Xor(UnsafeBitSet other)
|
||||||
{
|
{
|
||||||
var min = Math.Min(Length, other.Length);
|
if (Count != other.Count)
|
||||||
var temp = stackalloc uint[min];
|
|
||||||
|
|
||||||
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
|
||||||
{
|
{
|
||||||
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
|
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 vectorLeft = new Vector<uint>(_bits.AsSpan()[i..]);
|
||||||
var vectorRight = new Vector<uint>(other._bits.AsSpan()[i..]);
|
var vectorRight = new Vector<uint>(other._bits.AsSpan()[i..]);
|
||||||
var resultVector = Vector.Xor(vectorLeft, vectorRight);
|
var resultVector = Vector.Xor(vectorLeft, vectorRight);
|
||||||
resultVector.CopyTo(new Span<uint>(temp + i, s_padding));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_bits.CopyFrom(new Span<uint>(temp, min));
|
resultVector.CopyTo(_bits.AsSpan(i, s_padding));
|
||||||
}
|
|
||||||
|
|
||||||
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.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>
|
/// <summary>
|
||||||
@@ -624,10 +537,10 @@ public unsafe struct UnsafeBitSet : IDisposable
|
|||||||
/// <param name="span">The <see cref="Span{T}"/> to copy into.</param>
|
/// <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>
|
/// <param name="zero">If true, it will zero the unused space from the <see cref="span"/>.</param>
|
||||||
/// <returns>The <see cref="Span{T}"/>.</returns>
|
/// <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
|
// 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++)
|
for (var index = 0; index < length; index++)
|
||||||
{
|
{
|
||||||
span[index] = _bits[index];
|
span[index] = _bits[index];
|
||||||
@@ -639,10 +552,10 @@ public unsafe struct UnsafeBitSet : IDisposable
|
|||||||
span[index] = 0;
|
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
|
// Convert uint to binary form for pretty printing
|
||||||
var binaryBuilder = new StringBuilder();
|
var binaryBuilder = new StringBuilder();
|
||||||
@@ -652,7 +565,7 @@ public unsafe struct UnsafeBitSet : IDisposable
|
|||||||
}
|
}
|
||||||
binaryBuilder.Length--;
|
binaryBuilder.Length--;
|
||||||
|
|
||||||
return $"{nameof(_bits)}: {binaryBuilder}, {nameof(Length)}: {Length}";
|
return $"{nameof(_bits)}: {binaryBuilder}, {nameof(Count)}: {Count}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
|||||||
public readonly int Count => _count;
|
public readonly int Count => _count;
|
||||||
public readonly int Capacity => _capacity;
|
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));
|
public Enumerator GetEnumerator() => new((UnsafeSlotMap<T>*)UnsafeUtility.AddressOf(ref this));
|
||||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
|
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.");
|
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.");
|
throw new InvalidOperationException($"Slot {slotIndex} is not occupied.");
|
||||||
}
|
}
|
||||||
@@ -294,7 +294,7 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
|||||||
_count = 0;
|
_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly unsafe void* GetUnsafePtr()
|
public readonly void* GetUnsafePtr()
|
||||||
{
|
{
|
||||||
return _data.GetUnsafePtr();
|
return _data.GetUnsafePtr();
|
||||||
}
|
}
|
||||||
@@ -303,6 +303,7 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
|||||||
{
|
{
|
||||||
_data.Dispose();
|
_data.Dispose();
|
||||||
_freeSlots.Dispose();
|
_freeSlots.Dispose();
|
||||||
|
_validBits.Dispose();
|
||||||
|
|
||||||
_count = 0;
|
_count = 0;
|
||||||
_capacity = 0;
|
_capacity = 0;
|
||||||
|
|||||||
@@ -10,12 +10,21 @@ namespace Misaki.HighPerformance.LowLevel;
|
|||||||
/// <param name="Infos">An array of AllocationInfo containing details about the memory leaks.</param>
|
/// <param name="Infos">An array of AllocationInfo containing details about the memory leaks.</param>
|
||||||
public class MemoryLeakException : Exception
|
public class MemoryLeakException : Exception
|
||||||
{
|
{
|
||||||
private readonly IEnumerable<AllocationInfo>? _infos;
|
private readonly string _message;
|
||||||
private readonly string _message = string.Empty;
|
|
||||||
|
|
||||||
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)
|
public MemoryLeakException(string message)
|
||||||
@@ -44,25 +53,4 @@ public class MemoryLeakException : Exception
|
|||||||
|
|
||||||
return stringBuilder.ToString();
|
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
@@ -21,10 +21,6 @@
|
|||||||
<IsAotCompatible>True</IsAotCompatible>
|
<IsAotCompatible>True</IsAotCompatible>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Misaki.HighPerformance\Misaki.HighPerformance.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="Collections\FixedText.tt">
|
<None Update="Collections\FixedText.tt">
|
||||||
<Generator>TextTemplatingFileGenerator</Generator>
|
<Generator>TextTemplatingFileGenerator</Generator>
|
||||||
|
|||||||
@@ -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}"))});
|
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}
|
{INLINE_METHOD_ATTRIBUTE}
|
||||||
public void operator +=({typeName} other)
|
public void operator +=({typeName} other)
|
||||||
{{");
|
{{");
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
@@ -13,6 +13,14 @@
|
|||||||
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>
|
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<IsAotCompatible>True</IsAotCompatible>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<IsAotCompatible>True</IsAotCompatible>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Misaki.HighPerformance.Mathematics.CodeGen\Misaki.HighPerformance.Mathematics.CodeGen.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
<ProjectReference Include="..\Misaki.HighPerformance.Mathematics.CodeGen\Misaki.HighPerformance.Mathematics.CodeGen.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using Misaki.HighPerformance.Mathematics;
|
using Misaki.HighPerformance.Mathematics;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.Intrinsics;
|
using System.Runtime.Intrinsics;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.Test.Benchmark;
|
namespace Misaki.HighPerformance.Test.Benchmark;
|
||||||
@@ -25,7 +24,7 @@ public unsafe class MathematicsBenchmark
|
|||||||
public static f4 operator +(f4 a, f4 b)
|
public static f4 operator +(f4 a, f4 b)
|
||||||
{
|
{
|
||||||
var result = a._vec + b._vec;
|
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]
|
[Benchmark]
|
||||||
public unsafe Vector128<float> v128Add()
|
public Vector128<float> v128Add()
|
||||||
{
|
{
|
||||||
var a = Vector128.Create(1f, 2f, 3f, 4f);
|
var a = Vector128.Create(1f, 2f, 3f, 4f);
|
||||||
var b = Vector128.Create(5f, 6f, 7f, 8f);
|
var b = Vector128.Create(5f, 6f, 7f, 8f);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<PublishAot>True</PublishAot>
|
<PublishAot>True</PublishAot>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
//using Misaki.HighPerformance.Test.Benchmark;
|
//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.Buffer;
|
||||||
//using Misaki.HighPerformance.LowLevel.Collections;
|
//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);
|
|
||||||
}
|
|
||||||
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ public class TestUnsafeSlotMap
|
|||||||
[TestInitialize]
|
[TestInitialize]
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
_slotMap = new UnsafeSlotMap<int>(16, Allocator.Persistent, AllocationOption.Clear);
|
_slotMap = new UnsafeSlotMap<int>(16, Allocator.Persistent);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCleanup]
|
[TestCleanup]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
@@ -207,7 +207,7 @@ public class ConcurrentSlotMap<T> : IEnumerable<T>
|
|||||||
return false; // Another thread already removed it
|
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))
|
if (slotIndex < 0 || slotIndex >= Volatile.Read(ref _capacity))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
|
|||||||
Reference in New Issue
Block a user