Added UnsafeQueue;

This commit is contained in:
Misaki
2025-03-25 15:54:48 +09:00
parent 7bcd699eb9
commit cb69add265
5 changed files with 152 additions and 92 deletions

View File

@@ -1,22 +1,15 @@
namespace Misaki.HighPerformance.Unsafe.Collections.Contracts; namespace Misaki.HighPerformance.Unsafe.Collections.Contracts;
public unsafe interface IUnsafeCollection<T> : IDisposable where T : unmanaged public unsafe interface IUnsafeCollection<T> : IDisposable
where T : unmanaged
{ {
public T* Buffer public T* Buffer { get; }
{
get;
}
public int Size public int Size { get; }
{
get;
}
public ref T this[int index] public T this[int index] { get; }
{
get;
}
public void Clear(); public void Clear();
public void ReAlloc(int newSize); public void ReAlloc(int newSize);
} }

View File

@@ -1,14 +1,15 @@
using Misaki.HighPerformance.Unsafe.Collections.Contracts; using System.Collections;
using Misaki.HighPerformance.Unsafe.Helpers;
using System.Collections;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Misaki.HighPerformance.Unsafe.Collections.Contracts;
using Misaki.HighPerformance.Unsafe.Helpers;
namespace Misaki.HighPerformance.Unsafe.Collections; namespace Misaki.HighPerformance.Unsafe.Collections;
public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>, IEnumerable<T> where T : unmanaged public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>, IEnumerable<T>
where T : unmanaged
{ {
public struct Enumerator : IEnumerator<T> private struct Enumerator : IEnumerator<T>
{ {
private UnsafeArray<T> _collection; private UnsafeArray<T> _collection;
private int _index; private int _index;
@@ -44,24 +45,16 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>, IEnumerable<T> where
public T Current public T Current
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get get { return _value; }
{
return _value;
}
} }
object IEnumerator.Current object IEnumerator.Current
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get get { return Current; }
{
return Current;
}
} }
public void Dispose() public void Dispose() { }
{
}
} }
private T* _buffer; private T* _buffer;
@@ -70,9 +63,10 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>, IEnumerable<T> where
public readonly T* Buffer => _buffer; public readonly T* Buffer => _buffer;
public readonly int Size => _size; public readonly int Size => _size;
public readonly ref T this[int index] => ref UnsafeUtilities.AsRef<T>(_buffer + index); public readonly T this[int index] => UnsafeUtilities.ReadArrayElement<T>(_buffer, index);
public IEnumerator<T> GetEnumerator() => new Enumerator(ref this); public IEnumerator<T> GetEnumerator() => new Enumerator(ref this);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public UnsafeArray(int size, AllocationType allocationType) public UnsafeArray(int size, AllocationType allocationType)
@@ -93,7 +87,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>, IEnumerable<T> where
return; return;
} }
_buffer = (T*)NativeMemory.AlignedRealloc(_buffer, (nuint)(newSize * sizeof(T)), (nuint)AlignOf<T>()); _buffer = (T*)NativeMemory.AlignedRealloc(_buffer, (nuint)(newSize * sizeof(T)), AlignOf<T>());
_size = newSize; _size = newSize;
} }
@@ -111,3 +105,4 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>, IEnumerable<T> where
_size = 0; _size = 0;
} }
} }

View File

@@ -1,14 +1,14 @@
using Misaki.HighPerformance.Unsafe.Collections.Contracts; using System.Collections;
using Misaki.HighPerformance.Unsafe.Helpers;
using System.Collections;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using Misaki.HighPerformance.Unsafe.Collections.Contracts;
using Misaki.HighPerformance.Unsafe.Helpers;
namespace Misaki.HighPerformance.Unsafe.Collections; namespace Misaki.HighPerformance.Unsafe.Collections;
public unsafe struct UnsafeList<T> : IUnsafeCollection<T>, IEnumerable<T> where T : unmanaged public unsafe struct UnsafeList<T> : IUnsafeCollection<T>, IEnumerable<T>
where T : unmanaged
{ {
public struct Enumerator : IEnumerator<T> private struct Enumerator : IEnumerator<T>
{ {
private UnsafeList<T> _collection; private UnsafeList<T> _collection;
private int _index; private int _index;
@@ -44,24 +44,16 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>, IEnumerable<T> where
public readonly T Current public readonly T Current
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get get { return _value; }
{
return _value;
}
} }
readonly object IEnumerator.Current readonly object IEnumerator.Current
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get get { return Current; }
{
return Current;
}
} }
public readonly void Dispose() public readonly void Dispose() { }
{
}
} }
/// <summary> /// <summary>
@@ -90,7 +82,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>, IEnumerable<T> where
{ {
var idx = Interlocked.Increment(ref listData->_size) - 1; var idx = Interlocked.Increment(ref listData->_size) - 1;
listData->CheckNoResizeCapacity(idx, 1); listData->CheckNoResizeCapacity(idx, 1);
UnsafeUtilities.WriteArrayElement(listData->_buffer, idx, value); UnsafeUtilities.WriteArrayElement(listData->Buffer, idx, value);
} }
/// <summary> /// <summary>
@@ -102,31 +94,31 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>, IEnumerable<T> where
{ {
var idx = Interlocked.Add(ref listData->_size, count) - count; var idx = Interlocked.Add(ref listData->_size, count) - count;
listData->CheckNoResizeCapacity(idx, count); listData->CheckNoResizeCapacity(idx, count);
MemCpy(listData->_buffer + idx, ptr, (uint)(count * sizeof(T))); MemCpy(listData->Buffer + idx, ptr, (uint)(count * sizeof(T)));
} }
} }
private T* _buffer; private UnsafeArray<T> _array;
private int _size; private int _size;
private int _capacity;
public readonly T* Buffer => _buffer; public readonly T* Buffer => _array.Buffer;
public readonly int Size => _size; public readonly int Size => _size;
public readonly int Capacity => _capacity; public readonly int Capacity => _array.Size;
public readonly ref T this[int index] => ref UnsafeUtilities.ReadArrayElementRef<T>(_buffer, index); public readonly T this[int index] => _array[index];
public IEnumerator<T> GetEnumerator() => new Enumerator(ref this); public IEnumerator<T> GetEnumerator() => new Enumerator(ref this);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public ParallelWriter AsParallelWriter() => new((UnsafeList<T>*)UnsafeUtilities.AddressOf(ref this)); public ParallelWriter AsParallelWriter() =>
new((UnsafeList<T>*)UnsafeUtilities.AddressOf(ref this));
public UnsafeList(int capacity, AllocationType allocationType) public UnsafeList(int capacity, AllocationType allocationType)
{ {
_buffer = (T*)NativeMemory.AlignedAlloc((nuint)(capacity * sizeof(T)), (nuint)AlignOf<T>()); _array = new UnsafeArray<T>(capacity, allocationType);
_size = 0; _size = 0;
_capacity = capacity;
if (allocationType == AllocationType.Clear) if (allocationType == AllocationType.Clear)
{ {
@@ -141,9 +133,11 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>, IEnumerable<T> where
private readonly void CheckNoResizeCapacity(int index, int count) private readonly void CheckNoResizeCapacity(int index, int count)
{ {
if (index + count > _capacity) if (index + count > Capacity)
{ {
throw new Exception($"AddNoResize assumes that list capacity is sufficient (Capacity {Capacity}, Size {Size}), requested count {count}!"); throw new Exception(
$"AddNoResize assumes that list capacity is sufficient (Capacity {Capacity}, Size {Size}), requested count {count}!"
);
} }
} }
@@ -172,12 +166,12 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>, IEnumerable<T> where
public void Add(T value) public void Add(T value)
{ {
if (_size >= _capacity) if (_size >= Capacity)
{ {
ReAlloc(_capacity + (int)(_capacity * 0.5f)); ReAlloc(Capacity + (int)(Capacity * 0.5f));
} }
UnsafeUtilities.WriteArrayElement(_buffer, _size, value); UnsafeUtilities.WriteArrayElement(Buffer, _size, value);
_size++; _size++;
} }
@@ -185,21 +179,21 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>, IEnumerable<T> where
{ {
CheckNoResizeCapacity(1); CheckNoResizeCapacity(1);
UnsafeUtilities.WriteArrayElement(_buffer, _size, value); UnsafeUtilities.WriteArrayElement(Buffer, _size, value);
_size++; _size++;
} }
public void AddRange(Span<T> values, int count) public void AddRange(Span<T> values, int count)
{ {
var newSize = _size + count; var newSize = _size + count;
if (newSize > _capacity) if (newSize > Capacity)
{ {
ReAlloc(_capacity + count); ReAlloc(Capacity + count);
} }
fixed (T* ptr = values) fixed (T* ptr = values)
{ {
MemCpy(_buffer + _size, ptr, (uint)(count * sizeof(T))); MemCpy(_array.Buffer + _size, ptr, (uint)(count * sizeof(T)));
} }
_size += count; _size += count;
@@ -211,7 +205,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>, IEnumerable<T> where
fixed (T* ptr = values) fixed (T* ptr = values)
{ {
MemCpy(_buffer + _size, ptr, (uint)(values.Length * sizeof(T))); MemCpy(_array.Buffer + _size, ptr, (uint)(values.Length * sizeof(T)));
} }
_size += values.Length; _size += values.Length;
@@ -221,7 +215,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>, IEnumerable<T> where
{ {
CheckNoResizeCapacity(count); CheckNoResizeCapacity(count);
MemCpy(_buffer + _size, ptr, (uint)(count * sizeof(T))); MemCpy(_array.Buffer + _size, ptr, (uint)(count * sizeof(T)));
_size += count; _size += count;
} }
@@ -235,7 +229,11 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>, IEnumerable<T> where
} }
var copyFrom = Math.Min(start + length, _size); var copyFrom = Math.Min(start + length, _size);
MemCpy(_buffer + start, _buffer + copyFrom, (uint)((_size - copyFrom) * sizeof(T))); MemCpy(
_array.Buffer + start,
_array.Buffer + copyFrom,
(uint)((_size - copyFrom) * sizeof(T))
);
_size -= length; _size -= length;
} }
@@ -254,7 +252,11 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>, IEnumerable<T> where
} }
var copyFrom = Math.Min(_size - length, start + length); var copyFrom = Math.Min(_size - length, start + length);
MemCpy(_buffer + start, _buffer + copyFrom, (uint)((_size - copyFrom) * sizeof(T))); MemCpy(
_array.Buffer + start,
_array.Buffer + copyFrom,
(uint)((_size - copyFrom) * sizeof(T))
);
_size -= length; _size -= length;
} }
@@ -265,26 +267,19 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>, IEnumerable<T> where
public void ReAlloc(int newSize) public void ReAlloc(int newSize)
{ {
if (newSize == _size) _array.ReAlloc(newSize);
{
return;
}
_buffer = (T*)NativeMemory.AlignedRealloc(_buffer, (nuint)(newSize * sizeof(T)), (nuint)AlignOf<T>());
_size = newSize;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void Clear() public readonly void Clear()
{ {
MemClear(_buffer, (uint)(_size * sizeof(T))); _array.Clear();
} }
public void Dispose() public void Dispose()
{ {
NativeMemory.AlignedFree(_buffer); _array.Dispose();
_buffer = null;
_size = 0; _size = 0;
} }
} }

View File

@@ -0,0 +1,71 @@
using System.Diagnostics.CodeAnalysis;
using Misaki.HighPerformance.Unsafe.Collections.Contracts;
using Misaki.HighPerformance.Unsafe.Helpers;
namespace Misaki.HighPerformance.Unsafe.Collections;
public unsafe struct UnsafeQueue<T> : IUnsafeCollection<T>
where T : unmanaged
{
private UnsafeArray<T> _array;
private int _size;
private int _offset;
public T* Buffer => _array.Buffer;
public int Size => _size;
public int Capacity => _array.Size;
public T this[int index] => _array[index];
public void Enqueue(T value)
{
if (_size >= Capacity)
{
ReAlloc(Capacity + (int)(Capacity * 0.5f));
}
UnsafeUtilities.WriteArrayElement(Buffer, (_offset + _size) % Capacity, value);
_size++;
}
public T Dequeue()
{
if (_size == 0)
{
throw new InvalidOperationException("Queue is empty.");
}
var value = UnsafeUtilities.ReadArrayElement<T>(Buffer, _offset);
_offset = (_offset + 1) % Capacity;
_size--;
return value;
}
public bool TryDequeue([MaybeNullWhen(false)] out T? value)
{
if (_offset > _size)
{
value = null;
return false;
}
value = Dequeue();
return true;
}
public void ReAlloc(int newSize)
{
_array.ReAlloc(newSize);
}
public void Clear()
{
_array.Clear();
}
public void Dispose()
{
_array.Dispose();
}
}

View File

@@ -2,10 +2,12 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Misaki.HighPerformance.Unsafe.Helpers; namespace Misaki.HighPerformance.Unsafe.Helpers;
public unsafe static class MemoryUtilities
public static unsafe class MemoryUtilities
{ {
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
private struct AlignOfHelper<T> where T : struct private struct AlignOfHelper<T>
where T : struct
{ {
public byte dummy; public byte dummy;
public T data; public T data;
@@ -53,7 +55,8 @@ public unsafe static class MemoryUtilities
/// <typeparam name="T">Represents an unmanaged type for which the size is being calculated.</typeparam> /// <typeparam name="T">Represents an unmanaged type for which the size is being calculated.</typeparam>
/// <returns>Returns the size of the specified type as an unsigned integer.</returns> /// <returns>Returns the size of the specified type as an unsigned integer.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static nuint SizeOf<T>() where T : unmanaged public static nuint SizeOf<T>()
where T : unmanaged
{ {
return (nuint)sizeof(T); return (nuint)sizeof(T);
} }
@@ -63,9 +66,10 @@ public unsafe static class MemoryUtilities
/// </summary> /// </summary>
/// <typeparam name="T">Represents an unmanaged type for which the alignment size is being calculated.</typeparam> /// <typeparam name="T">Represents an unmanaged type for which the alignment size is being calculated.</typeparam>
/// <returns>Returns the difference in size between a helper structure and the specified type.</returns> /// <returns>Returns the difference in size between a helper structure and the specified type.</returns>
public static int AlignOf<T>() where T : unmanaged public static nuint AlignOf<T>()
where T : unmanaged
{ {
return sizeof(AlignOfHelper<T>) - sizeof(T); return (nuint)(sizeof(AlignOfHelper<T>) - sizeof(T));
} }
/// <summary> /// <summary>
@@ -73,8 +77,10 @@ public unsafe static class MemoryUtilities
/// </summary> /// </summary>
/// <typeparam name="T">Represents a value type that is used to determine the alignment size.</typeparam> /// <typeparam name="T">Represents a value type that is used to determine the alignment size.</typeparam>
/// <returns>Returns the size difference in bytes as an integer.</returns> /// <returns>Returns the size difference in bytes as an integer.</returns>
public static int MarshalAlignOf<T>() where T : struct public static int MarshalAlignOf<T>()
where T : struct
{ {
return Marshal.SizeOf<AlignOfHelper<T>>() - Marshal.SizeOf<T>(); return Marshal.SizeOf<AlignOfHelper<T>>() - Marshal.SizeOf<T>();
} }
} }