Refactor allocation types and improve memory management

This commit renames `AllocationType` to `AllocationOption` across multiple files, enhancing clarity in memory allocation practices. Key updates include:
- Modifications to the `UnsafeArray`, `ParallelNoiseBenchmark`, `Arena`, and `DynamicArena` classes to utilize the new `AllocationOption`.
- Refactoring of the `AllocationManager` and `HashMapHelper` classes to support the new allocation strategy.
- Removal of the `Misaki.HighPerformance.Mathematics` project reference, indicating a restructuring of dependencies.
- Introduction of a new `MathUtilities` class for calculating the smallest power of two, aiding memory allocation strategies.

These changes collectively improve code maintainability and clarity in memory management.
This commit is contained in:
Misaki
2025-04-03 12:06:25 +09:00
parent 48f2dce778
commit da64e07c6f
17 changed files with 111 additions and 71 deletions

View File

@@ -1,6 +1,6 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Misaki.HighPerformance.Unsafe.Collections; using Misaki.HighPerformance.Unsafe.Collections;
using Misaki.HighPerformance.Unsafe.Collections.Services; using Misaki.HighPerformance.Unsafe.Services;
namespace Misaki.HighPerformance.Test; namespace Misaki.HighPerformance.Test;

View File

@@ -61,7 +61,7 @@ public class ParallelNoiseBenchmark
[Benchmark] [Benchmark]
public static void JobSystem() public static void JobSystem()
{ {
using var buffers = new UnsafeArray<float>(_LENGTH, Allocator.Persistent, AllocationType.UnInitialized); using var buffers = new UnsafeArray<float>(_LENGTH, Allocator.Persistent, AllocationOption.UnInitialized);
var job = new NoiseJob() var job = new NoiseJob()
{ {
buffers = buffers, buffers = buffers,
@@ -76,7 +76,7 @@ public class ParallelNoiseBenchmark
[Benchmark] [Benchmark]
public static void ParallelFor() public static void ParallelFor()
{ {
using var buffers = new UnsafeArray<float>(_LENGTH, Allocator.Persistent, AllocationType.UnInitialized); using var buffers = new UnsafeArray<float>(_LENGTH, Allocator.Persistent, AllocationOption.UnInitialized);
Parallel.For(0, _LENGTH, i => Parallel.For(0, _LENGTH, i =>
{ {
@@ -90,7 +90,7 @@ public class ParallelNoiseBenchmark
[Benchmark] [Benchmark]
public static void For() public static void For()
{ {
using var buffers = new UnsafeArray<float>(_LENGTH, Allocator.Persistent, AllocationType.UnInitialized); using var buffers = new UnsafeArray<float>(_LENGTH, Allocator.Persistent, AllocationOption.UnInitialized);
for (var i = 0; i < _LENGTH; i++) for (var i = 0; i < _LENGTH; i++)
{ {
var x = i % _WIDTH; var x = i % _WIDTH;

View File

@@ -1,5 +1,5 @@
using Misaki.HighPerformance.Test; using Misaki.HighPerformance.Test;
using Misaki.HighPerformance.Unsafe.Collections.Services; using Misaki.HighPerformance.Unsafe.Services;
AllocationManager.Initialize(512_000); AllocationManager.Initialize(512_000);
var test = new CollectionBenchmark(); var test = new CollectionBenchmark();

View File

@@ -30,7 +30,7 @@ public unsafe struct Arena : IDisposable
/// <param name="alignSize">Defines the alignment requirement for the allocated memory.</param> /// <param name="alignSize">Defines the alignment requirement for the allocated memory.</param>
/// <returns>A pointer to the allocated memory block or null if the allocation cannot be fulfilled.</returns> /// <returns>A pointer to the allocated memory block or null if the allocation cannot be fulfilled.</returns>
/// <exception cref="ObjectDisposedException">Thrown if the arena has been disposed.</exception> /// <exception cref="ObjectDisposedException">Thrown if the arena has been disposed.</exception>
public void* Allocate(uint size, uint alignSize, AllocationType allocationType) public void* Allocate(uint size, uint alignSize, AllocationOption allocationType)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
@@ -41,9 +41,9 @@ public unsafe struct Arena : IDisposable
} }
_offset = offset + size; _offset = offset + size;
var ptr = (byte*)_buffer + offset; var ptr = _buffer + offset;
if (allocationType == AllocationType.Clear) if (allocationType == AllocationOption.Clear)
{ {
MemClear(ptr, size); MemClear(ptr, size);
} }

View File

@@ -57,7 +57,7 @@ public unsafe struct DynamicArena : IDisposable
/// <param name="alignSize">Alignment requirement for the memory block.</param> /// <param name="alignSize">Alignment requirement for the memory block.</param>
/// <returns>Pointer to the allocated memory block.</returns> /// <returns>Pointer to the allocated memory block.</returns>
/// <exception cref="ObjectDisposedException">Thrown if the arena has been disposed.</exception> /// <exception cref="ObjectDisposedException">Thrown if the arena has been disposed.</exception>
public void* Allocate(uint size, uint alignSize, AllocationType allocationType) public void* Allocate(uint size, uint alignSize, AllocationOption allocationType)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);

View File

@@ -0,0 +1,15 @@
namespace Misaki.HighPerformance.Unsafe.Collections;
public enum AllocationOption : byte
{
UnInitialized,
Clear
}
public enum Allocator: byte
{
// Make the first allocator as invalid because we don't want to user create a defualt collection without passing any parameters
Invalid = 0,
Temp = 1,
Persistent = 2,
}

View File

@@ -1,13 +0,0 @@
namespace Misaki.HighPerformance.Unsafe.Collections;
public enum AllocationType
{
UnInitialized,
Clear
}
public enum Allocator
{
Temp,
Persistent
}

View File

@@ -1,6 +1,6 @@
using Misaki.HighPerformance.Unsafe.Collections.Contracts; using Misaki.HighPerformance.Unsafe.Collections.Contracts;
using Misaki.HighPerformance.Unsafe.Collections.Services;
using Misaki.HighPerformance.Unsafe.Helpers; using Misaki.HighPerformance.Unsafe.Helpers;
using Misaki.HighPerformance.Unsafe.Services;
using System.Collections; using System.Collections;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -66,6 +66,8 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
private T* _buffer; private T* _buffer;
private int _count; private int _count;
private readonly Allocator _allocator;
public readonly int Count => _count; public readonly int Count => _count;
public readonly ref T this[int index] public readonly ref T this[int index]
@@ -90,7 +92,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
/// <param name="count">Specifies the number of elements to allocate in the array, which must be greater than zero.</param> /// <param name="count">Specifies the number of elements to allocate in the array, which must be greater than zero.</param>
/// <param name="allocationType">Determines how the allocated memory should be initialized, either uninitialized or cleared.</param> /// <param name="allocationType">Determines how the allocated memory should be initialized, either uninitialized or cleared.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the specified number of elements is less than or equal to zero.</exception> /// <exception cref="ArgumentOutOfRangeException">Thrown when the specified number of elements is less than or equal to zero.</exception>
public UnsafeArray(int count, Allocator allocator, AllocationType allocationType = AllocationType.UnInitialized) public UnsafeArray(int count, Allocator allocator, AllocationOption allocationType = AllocationOption.UnInitialized)
{ {
if (count <= 0) if (count <= 0)
{ {
@@ -100,7 +102,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
_buffer = AllocationManager.Allocate<T>((uint)count, (uint)AlignOf<T>(), allocator, allocationType); _buffer = AllocationManager.Allocate<T>((uint)count, (uint)AlignOf<T>(), allocator, allocationType);
_count = count; _count = count;
if (allocationType == AllocationType.Clear) if (allocationType == AllocationOption.Clear)
{ {
Clear(); Clear();
} }
@@ -132,7 +134,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void Clear() public readonly void Clear()
{ {
MemClear(_buffer, (uint)(_count * sizeof(T))); MemClear(_buffer, (nuint)(_count * sizeof(T)));
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -143,7 +145,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
public void Dispose() public void Dispose()
{ {
AlignedFree(_buffer); AllocationManager.Free(_buffer, _allocator);
_buffer = null; _buffer = null;
_count = 0; _count = 0;

View File

@@ -92,9 +92,9 @@ public unsafe struct UnsafeHashMap<TKey, TValue> : IUnsafeCollection<KeyValuePai
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => new Enumerator((HashMapHelper<TKey>*)UnsafeUtilities.AddressOf(ref _hashMap)); public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => new Enumerator((HashMapHelper<TKey>*)UnsafeUtilities.AddressOf(ref _hashMap));
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public UnsafeHashMap(int capacity) public UnsafeHashMap(int capacity, Allocator allocator)
{ {
_hashMap = new HashMapHelper<TKey>(capacity, sizeof(TValue), HashMapHelper<TKey>.MINIMAL_CAPACITY); _hashMap = new HashMapHelper<TKey>(capacity, sizeof(TValue), HashMapHelper<TKey>.MINIMAL_CAPACITY, allocator);
} }
/// <summary> /// <summary>

View File

@@ -49,9 +49,9 @@ public unsafe struct UnsafeHashSet<T> : IUnsafeCollection<T>, IEnumerable<T>
public IEnumerator<T> GetEnumerator() => new Enumerator((HashMapHelper<T>*)UnsafeUtilities.AddressOf(ref _hashMap)); public IEnumerator<T> GetEnumerator() => new Enumerator((HashMapHelper<T>*)UnsafeUtilities.AddressOf(ref _hashMap));
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public UnsafeHashSet(int capacity) public UnsafeHashSet(int capacity, Allocator allocator)
{ {
_hashMap = new HashMapHelper<T>(capacity, 0, HashMapHelper<T>.MINIMAL_CAPACITY); _hashMap = new HashMapHelper<T>(capacity, 0, HashMapHelper<T>.MINIMAL_CAPACITY, allocator);
} }
/// <summary> /// <summary>

View File

@@ -129,12 +129,12 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
public ParallelWriter AsParallelWriter() => new((UnsafeList<T>*)UnsafeUtilities.AddressOf(ref this)); public ParallelWriter AsParallelWriter() => new((UnsafeList<T>*)UnsafeUtilities.AddressOf(ref this));
public UnsafeList(int capacity, Allocator allocator, AllocationType allocationType = AllocationType.UnInitialized) public UnsafeList(int capacity, Allocator allocator, AllocationOption allocationType = AllocationOption.UnInitialized)
{ {
_array = new UnsafeArray<T>(capacity, allocator, allocationType); _array = new UnsafeArray<T>(capacity, allocator, allocationType);
_count = 0; _count = 0;
if (allocationType == AllocationType.Clear) if (allocationType == AllocationOption.Clear)
{ {
Clear(); Clear();
} }

View File

@@ -80,13 +80,13 @@ public unsafe struct UnsafeQueue<T> : IUnsafeCollection<T>
public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeQueue<T>*)UnsafeUtilities.AddressOf(ref this)); public IEnumerator<T> GetEnumerator() => new Enumerator((UnsafeQueue<T>*)UnsafeUtilities.AddressOf(ref this));
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public UnsafeQueue(int capacity, Allocator allocator, AllocationType allocationType = AllocationType.UnInitialized) public UnsafeQueue(int capacity, Allocator allocator, AllocationOption allocationType = AllocationOption.UnInitialized)
{ {
_array = new UnsafeArray<T>(capacity, allocator, allocationType); _array = new UnsafeArray<T>(capacity, allocator, allocationType);
_count = 0; _count = 0;
_offset = 0; _offset = 0;
if (allocationType == AllocationType.Clear) if (allocationType == AllocationOption.Clear)
{ {
Clear(); Clear();
} }

View File

@@ -1,5 +1,5 @@
using Misaki.HighPerformance.Mathematics;
using Misaki.HighPerformance.Unsafe.Collections; using Misaki.HighPerformance.Unsafe.Collections;
using Misaki.HighPerformance.Unsafe.Services;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -74,6 +74,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
private readonly int _sizeOfTValue; private readonly int _sizeOfTValue;
private readonly int _log2MinGrowth; private readonly int _log2MinGrowth;
private readonly Allocator _allocator;
public const int MINIMAL_CAPACITY = 64; public const int MINIMAL_CAPACITY = 64;
@@ -107,16 +108,6 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
get => !IsCreated || _count == 0; get => !IsCreated || _count == 0;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private readonly int CalcCapacityCeilPow2(int capacity)
{
capacity = Math.Max(Math.Max(1, _count), capacity);
var newCapacity = Math.Max(capacity, 1 << _log2MinGrowth);
var result = MathUtilities.CeilPow2(newCapacity);
return result;
}
private static int CalculateDataSize(int capacity, int bucketCapacity, int sizeOfTValue, out int outKeyOffset, out int outNextOffset, out int outBucketOffset) private static int CalculateDataSize(int capacity, int bucketCapacity, int sizeOfTValue, out int outKeyOffset, out int outNextOffset, out int outBucketOffset)
{ {
var sizeOfTKey = sizeof(TKey); var sizeOfTKey = sizeof(TKey);
@@ -135,7 +126,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
return totalSize; return totalSize;
} }
public HashMapHelper(int capacity, int sizeOfTValue, uint minGrowth) public HashMapHelper(int capacity, int sizeOfTValue, uint minGrowth, Allocator allocator)
{ {
if (capacity <= 0) if (capacity <= 0)
{ {
@@ -150,18 +141,25 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
_capacity = CalcCapacityCeilPow2(capacity); _capacity = CalcCapacityCeilPow2(capacity);
_bucketCapacity = _capacity * 2; _bucketCapacity = _capacity * 2;
_sizeOfTValue = sizeOfTValue;
_log2MinGrowth = BitOperations.Log2(minGrowth);
_allocator = allocator;
var totalSize = CalculateDataSize(_capacity, _bucketCapacity, sizeOfTValue, var totalSize = CalculateDataSize(_capacity, _bucketCapacity, sizeOfTValue,
out var keyOffset, out var nextOffset, out var bucketOffset); out var keyOffset, out var nextOffset, out var bucketOffset);
_buffer = (byte*)Malloc((nuint)totalSize); AllocateBuffer(totalSize, keyOffset, nextOffset, bucketOffset);
_keys = (TKey*)(_buffer + keyOffset);
_next = (int*)(_buffer + nextOffset);
_buckets = (int*)(_buffer + bucketOffset);
Clear(); Clear();
}
_sizeOfTValue = sizeOfTValue; [MethodImpl(MethodImplOptions.AggressiveInlining)]
_log2MinGrowth = BitOperations.Log2(minGrowth); private readonly int CalcCapacityCeilPow2(int capacity)
{
capacity = Math.Max(Math.Max(1, _count), capacity);
var newCapacity = Math.Max(capacity, 1 << _log2MinGrowth);
var result = MathUtilities.CeilPow2(newCapacity);
return result;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -179,6 +177,17 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AllocateBuffer(int totalSize, int keyOffset, int nextOffset, int bucketOffset)
{
var alignSize = sizeof(TKey) > sizeof(int) ? AlignOf<TKey>() : AlignOf<int>();
_buffer = AllocationManager.Allocate<byte>((uint)totalSize, (uint)alignSize, _allocator, AllocationOption.UnInitialized);
_keys = (TKey*)(_buffer + keyOffset);
_next = (int*)(_buffer + nextOffset);
_buckets = (int*)(_buffer + bucketOffset);
}
internal void ResizeExact(int newCapacity, int newBucketCapacity) internal void ResizeExact(int newCapacity, int newBucketCapacity)
{ {
var totalSize = CalculateDataSize(newCapacity, newBucketCapacity, _sizeOfTValue, var totalSize = CalculateDataSize(newCapacity, newBucketCapacity, _sizeOfTValue,
@@ -190,10 +199,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
var oldBuckets = _buckets; var oldBuckets = _buckets;
var oldBucketCapacity = _bucketCapacity; var oldBucketCapacity = _bucketCapacity;
_buffer = (byte*)Malloc((nuint)totalSize); AllocateBuffer(totalSize, keyOffset, nextOffset, bucketOffset);
_keys = (TKey*)(_buffer + keyOffset);
_next = (int*)(_buffer + nextOffset);
_buckets = (int*)(_buffer + bucketOffset);
_capacity = newCapacity; _capacity = newCapacity;
_bucketCapacity = newBucketCapacity; _bucketCapacity = newBucketCapacity;
@@ -405,7 +411,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
internal UnsafeArray<TKey> GetKeyArray(Allocator allocator) internal UnsafeArray<TKey> GetKeyArray(Allocator allocator)
{ {
var result = new UnsafeArray<TKey>(_count, allocator, AllocationType.UnInitialized); var result = new UnsafeArray<TKey>(_count, allocator, AllocationOption.UnInitialized);
for (int i = 0, count = 0, max = result.Count, capacity = _bucketCapacity; i < capacity && count < max; i++) for (int i = 0, count = 0, max = result.Count, capacity = _bucketCapacity; i < capacity && count < max; i++)
{ {
@@ -424,7 +430,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
internal UnsafeArray<TValue> GetValueArray<TValue>(Allocator allocator) internal UnsafeArray<TValue> GetValueArray<TValue>(Allocator allocator)
where TValue : unmanaged where TValue : unmanaged
{ {
var result = new UnsafeArray<TValue>(_count, allocator, AllocationType.UnInitialized); var result = new UnsafeArray<TValue>(_count, allocator, AllocationOption.UnInitialized);
for (int i = 0, count = 0, max = result.Count, capacity = _bucketCapacity; i < capacity && count < max; ++i) for (int i = 0, count = 0, max = result.Count, capacity = _bucketCapacity; i < capacity && count < max; ++i)
{ {
@@ -443,7 +449,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
public UnsafeArray<KeyValuePair<TKey, TValue>> GetKeyValueArrays<TValue>(Allocator allocator) public UnsafeArray<KeyValuePair<TKey, TValue>> GetKeyValueArrays<TValue>(Allocator allocator)
where TValue : unmanaged where TValue : unmanaged
{ {
var result = new UnsafeArray<KeyValuePair<TKey, TValue>>(_count, allocator, AllocationType.UnInitialized); var result = new UnsafeArray<KeyValuePair<TKey, TValue>>(_count, allocator, AllocationOption.UnInitialized);
for (int i = 0, count = 0, max = result.Count, capacity = _bucketCapacity; i < capacity && count < max; i++) for (int i = 0, count = 0, max = result.Count, capacity = _bucketCapacity; i < capacity && count < max; i++)
{ {
@@ -476,7 +482,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
{ {
if (IsCreated) if (IsCreated)
{ {
Free(_buffer); AllocationManager.Free(_buffer, _allocator);
_buffer = null; _buffer = null;
_keys = null; _keys = null;

View File

@@ -16,7 +16,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Misaki.HighPerformance.Math\Misaki.HighPerformance.Mathematics.csproj" /> <ProjectReference Include="..\Misaki.HighPerformance\Misaki.HighPerformance.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,6 +1,7 @@
using Misaki.HighPerformance.Unsafe.Buffer; using Misaki.HighPerformance.Unsafe.Buffer;
using Misaki.HighPerformance.Unsafe.Collections;
namespace Misaki.HighPerformance.Unsafe.Collections.Services; namespace Misaki.HighPerformance.Unsafe.Services;
public static unsafe class AllocationManager public static unsafe class AllocationManager
{ {
@@ -15,7 +16,7 @@ public static unsafe class AllocationManager
_initialized = true; _initialized = true;
} }
public static T* Allocate<T>(uint size, uint alignSize, Allocator allocator, AllocationType allocationType) internal static T* Allocate<T>(uint size, uint alignSize, Allocator allocator, AllocationOption allocationType)
where T : unmanaged where T : unmanaged
{ {
if (!_initialized) if (!_initialized)
@@ -34,6 +35,22 @@ public static unsafe class AllocationManager
} }
} }
internal static void Free(void* ptr, Allocator allocator)
{
if (!_initialized)
{
throw new InvalidOperationException("The AllocationManager has not been initialized.");
}
lock (_lock)
{
if (allocator == Allocator.Persistent)
{
AlignedFree(ptr);
}
}
}
public static void Reset(bool clear = false) public static void Reset(bool clear = false)
{ {
if (!_initialized) if (!_initialized)

View File

@@ -9,8 +9,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Misaki.HighPerformance.Unsa
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Misaki.HighPerformance.Test", "Misaki.HighPerformance.Test\Misaki.HighPerformance.Test.csproj", "{90EFF5B8-22CD-4B6A-83AB-48E0E97610EA}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Misaki.HighPerformance.Test", "Misaki.HighPerformance.Test\Misaki.HighPerformance.Test.csproj", "{90EFF5B8-22CD-4B6A-83AB-48E0E97610EA}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Misaki.HighPerformance.Mathematics", "Misaki.HighPerformance.Math\Misaki.HighPerformance.Mathematics.csproj", "{861F9574-2063-4B54-8D6A-AA32B26B6583}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -29,10 +27,6 @@ Global
{90EFF5B8-22CD-4B6A-83AB-48E0E97610EA}.Debug|Any CPU.Build.0 = Debug|Any CPU {90EFF5B8-22CD-4B6A-83AB-48E0E97610EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{90EFF5B8-22CD-4B6A-83AB-48E0E97610EA}.Release|Any CPU.ActiveCfg = Release|Any CPU {90EFF5B8-22CD-4B6A-83AB-48E0E97610EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{90EFF5B8-22CD-4B6A-83AB-48E0E97610EA}.Release|Any CPU.Build.0 = Release|Any CPU {90EFF5B8-22CD-4B6A-83AB-48E0E97610EA}.Release|Any CPU.Build.0 = Release|Any CPU
{861F9574-2063-4B54-8D6A-AA32B26B6583}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{861F9574-2063-4B54-8D6A-AA32B26B6583}.Debug|Any CPU.Build.0 = Debug|Any CPU
{861F9574-2063-4B54-8D6A-AA32B26B6583}.Release|Any CPU.ActiveCfg = Release|Any CPU
{861F9574-2063-4B54-8D6A-AA32B26B6583}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -0,0 +1,19 @@
using System.Runtime.CompilerServices;
namespace Misaki.HighPerformance;
public static class MathUtilities
{
/// <summary>Returns the smallest power of two that is greater than or equal to the specified number.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CeilPow2(int x)
{
x -= 1;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return x + 1;
}
}