Compare commits
2 Commits
fef20f05b7
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 53bba54d43 | |||
| 7c9612ceb0 |
@@ -121,9 +121,6 @@ internal sealed class WaitAnyItem : IThreadPoolWorkItem
|
||||
/// </summary>
|
||||
public sealed unsafe partial class JobScheduler : IDisposable
|
||||
{
|
||||
// Don't sleep indefinitely because that causes our 1ms job to become 15ms.
|
||||
private const int SLEEP_THRESHOLD = -1;
|
||||
|
||||
private readonly ConcurrentSlotMap<JobInfo> _jobInfoPool;
|
||||
private readonly ConcurrentQueue<JobHandle>[] _jobQueues;
|
||||
private readonly WorkerThread[] _workerThreads;
|
||||
@@ -834,7 +831,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
|
||||
|
||||
if (!madeProgress)
|
||||
{
|
||||
spin.SpinOnce(SLEEP_THRESHOLD);
|
||||
spin.SpinOnce(-1); // Never sleep and yield to achieve lowest latency for single job completion.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -854,6 +851,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
|
||||
}
|
||||
|
||||
var spin = new SpinWait();
|
||||
var sleepThreshold = handles.Length * 20;
|
||||
var completedCount = 0;
|
||||
|
||||
while (true)
|
||||
@@ -877,7 +875,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
spin.SpinOnce(SLEEP_THRESHOLD);
|
||||
spin.SpinOnce(sleepThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -889,6 +887,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
|
||||
public JobHandle WaitAny(params ReadOnlySpan<JobHandle> handles)
|
||||
{
|
||||
var spin = new SpinWait();
|
||||
var sleepThreshold = handles.Length * 10;
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -900,7 +899,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
spin.SpinOnce(SLEEP_THRESHOLD);
|
||||
spin.SpinOnce(sleepThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,13 +6,20 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<AssemblyVersion>3.1.7</AssemblyVersion>
|
||||
<AssemblyVersion>3.1.8</AssemblyVersion>
|
||||
<Version>$(AssemblyVersion)</Version>
|
||||
<Authors>Misaki</Authors>
|
||||
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>
|
||||
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>
|
||||
<RepositoryUrl>https://github.com/misakieku/Misaki.HighPerformance.git</RepositoryUrl>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageTags>job-system;multithreading;concurrency;task-scheduler;work-stealing;zero-allocation;0gc;dag;dependency-graph;game-engine;ecs;high-performance;spmc</PackageTags>
|
||||
<Description>
|
||||
A high-performance, zero-allocation (0 GC), and zero-closure job system designed for custom game engines and data-oriented design (DOD).
|
||||
Features a lock-free Work-Stealing scheduler (SPMC) with DAG-based multi-dependency resolution.
|
||||
Includes a blazingly fast O(1) branchless priority queue (High/Normal/Low) using Cascade LUTs.
|
||||
Uniquely supports both unmanaged and managed jobs seamlessly via internal pooling, offering maximum flexibility without compromising C# GC performance.
|
||||
</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -7,16 +7,16 @@ namespace Misaki.HighPerformance.Jobs;
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class SPMCQueue<T>
|
||||
{
|
||||
private unsafe struct padding
|
||||
private struct __padding
|
||||
{
|
||||
private fixed byte _padding[64];
|
||||
private unsafe fixed byte _padding[64];
|
||||
}
|
||||
|
||||
private readonly T[] _queue;
|
||||
private readonly int _mask;
|
||||
|
||||
private int _head;
|
||||
private padding _padding; // Prevent false sharing between head and tail
|
||||
private __padding _padding;
|
||||
private int _tail;
|
||||
|
||||
public bool IsEmpty => Volatile.Read(ref _tail) - Volatile.Read(ref _head) <= 0;
|
||||
@@ -30,8 +30,9 @@ public class SPMCQueue<T>
|
||||
/// <param name="capacity">The capacity of the queue.</param>
|
||||
public SPMCQueue(int capacity)
|
||||
{
|
||||
_queue = new T[(int)BitOperations.RoundUpToPowerOf2((uint)capacity)];
|
||||
_mask = capacity - 1;
|
||||
var powerOfTwoCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)capacity);
|
||||
_queue = new T[powerOfTwoCapacity];
|
||||
_mask = powerOfTwoCapacity - 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -64,7 +65,7 @@ public class SPMCQueue<T>
|
||||
{
|
||||
var tail = _tail - 1;
|
||||
Volatile.Write(ref _tail, tail);
|
||||
|
||||
|
||||
Interlocked.MemoryBarrier();
|
||||
|
||||
var head = Volatile.Read(ref _head);
|
||||
|
||||
@@ -84,6 +84,11 @@ internal class WorkerThread : IDisposable
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (var offset = 0; offset < helperThreadCount; offset++)
|
||||
{
|
||||
var p = cascade[index + offset];
|
||||
|
||||
for (var i = 1; i < _scheduler.WorkerCount; i++)
|
||||
{
|
||||
|
||||
@@ -143,6 +143,8 @@ public readonly unsafe struct AllocationHandle
|
||||
private readonly ReallocFunc _realloc;
|
||||
private readonly FreeFunc _free;
|
||||
|
||||
public bool IsValid => _alloc != null && _realloc != null && _free != null;
|
||||
|
||||
public AllocationHandle(void* state, AllocFunc alloc, ReallocFunc realloc, FreeFunc free)
|
||||
{
|
||||
_state = state;
|
||||
|
||||
@@ -23,10 +23,12 @@ public unsafe struct VirtualMemoryBlock : IDisposable
|
||||
}
|
||||
|
||||
var addr = _baseAddress;
|
||||
var size = _size;
|
||||
|
||||
_baseAddress = null;
|
||||
_size = 0;
|
||||
_committed = 0;
|
||||
|
||||
MemoryUtility.Munmap(addr, _size);
|
||||
MemoryUtility.Munmap(addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
101
src/Misaki.HighPerformance.LowLevel/Collections/UnsafeString.cs
Normal file
101
src/Misaki.HighPerformance.LowLevel/Collections/UnsafeString.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using System.Text;
|
||||
|
||||
namespace Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
public unsafe struct UnsafeString : IDisposable
|
||||
{
|
||||
private UnsafeArray<char> _chars;
|
||||
|
||||
public readonly int Length => _chars.Length;
|
||||
|
||||
public readonly char this[int index] => _chars[index];
|
||||
|
||||
public UnsafeString(ReadOnlySpan<char> span, AllocationHandle handle)
|
||||
{
|
||||
_chars = new UnsafeArray<char>(span.Length, handle);
|
||||
span.CopyTo(_chars.AsSpan());
|
||||
}
|
||||
|
||||
public UnsafeString(UnsafeString other)
|
||||
{
|
||||
_chars = new UnsafeArray<char>(other.Length, other._chars.AllocationHandle);
|
||||
other.AsSpan().CopyTo(_chars.AsSpan());
|
||||
}
|
||||
|
||||
public readonly UnsafeString Copy(AllocationHandle handle = default)
|
||||
{
|
||||
handle = handle.IsValid ? handle : _chars.AllocationHandle;
|
||||
var clone = new UnsafeString(AsSpan(), handle);
|
||||
return clone;
|
||||
}
|
||||
|
||||
public readonly ReadOnlySpan<char> AsSpan()
|
||||
{
|
||||
return _chars.AsSpan();
|
||||
}
|
||||
|
||||
public readonly void* GetUnsafePtr()
|
||||
{
|
||||
return _chars.GetUnsafePtr();
|
||||
}
|
||||
|
||||
public readonly override string ToString()
|
||||
{
|
||||
return new string(_chars.AsSpan());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_chars.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public unsafe struct UnsafeText : IDisposable
|
||||
{
|
||||
private UnsafeArray<byte> _chars;
|
||||
|
||||
public readonly int Length => _chars.Length;
|
||||
|
||||
public readonly byte this[int index] => _chars[index];
|
||||
|
||||
public UnsafeText(ReadOnlySpan<byte> span, AllocationHandle handle)
|
||||
{
|
||||
_chars = new UnsafeArray<byte>(span.Length, handle);
|
||||
span.CopyTo(_chars.AsSpan());
|
||||
}
|
||||
|
||||
public UnsafeText(UnsafeText other)
|
||||
{
|
||||
_chars = new UnsafeArray<byte>(other.Length, other._chars.AllocationHandle);
|
||||
other.AsSpan().CopyTo(_chars.AsSpan());
|
||||
}
|
||||
|
||||
public readonly UnsafeText Copy(AllocationHandle handle = default)
|
||||
{
|
||||
handle = handle.IsValid ? handle : _chars.AllocationHandle;
|
||||
var clone = new UnsafeText(AsSpan(), handle);
|
||||
return clone;
|
||||
}
|
||||
|
||||
public readonly ReadOnlySpan<byte> AsSpan()
|
||||
{
|
||||
return _chars.AsSpan();
|
||||
}
|
||||
|
||||
public readonly void* GetUnsafePtr()
|
||||
{
|
||||
return _chars.GetUnsafePtr();
|
||||
}
|
||||
|
||||
public readonly override string ToString()
|
||||
{
|
||||
return Encoding.UTF8.GetString(_chars.AsSpan());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_chars.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,19 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Authors>Misaki</Authors>
|
||||
<AssemblyVersion>1.7.3</AssemblyVersion>
|
||||
<AssemblyVersion>1.7.4</AssemblyVersion>
|
||||
<Version>$(AssemblyVersion)</Version>
|
||||
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>
|
||||
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>
|
||||
<RepositoryUrl>https://github.com/misakieku/Misaki.HighPerformance.git</RepositoryUrl>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageTags>native;unsafe;memory;allocator;mmap;arena;tlsf;freelist;high-performance;dod;ecs;native-collections;native-array;game-engine</PackageTags>
|
||||
<Description>
|
||||
A true high-performance, low-level native memory and collection library for C#.
|
||||
Features a pluggable memory allocation architecture (AllocationHandle) with built-in allocators: Arena, FreeList, TLSF, and Malloc (mimalloc support).
|
||||
Includes fully unmanaged native collections (UnsafeArray, UnsafeList, UnsafeHashMap, etc.) and cross-platform mmap/munmap wrappers.
|
||||
Designed for Data-Oriented Design (DOD), custom game engines, and zero-allocation systems.
|
||||
</Description>
|
||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||
<ContentTargetFolders>contentFiles</ContentTargetFolders>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -248,7 +248,7 @@ internal unsafe struct GGXMipGenerationJobSPMD<TFloat, TInt> : IJobParallel
|
||||
return MathV.GatherVector3<TFloat, float>(img, idx.GetUnsafePtr(), 4);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
private readonly void ProcessPixel(int local_i, MipLevel* pLevel)
|
||||
{
|
||||
var w = (int)pLevel->width;
|
||||
@@ -350,7 +350,7 @@ internal unsafe struct GGXMipGenerationJobSPMD<TFloat, TInt> : IJobParallel
|
||||
pData[out_idx + 2] = prefilteredColor.z;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void Execute(int startIndex, int endIndex, ref readonly JobExecutionContext ctx)
|
||||
{
|
||||
var m = 0;
|
||||
|
||||
Reference in New Issue
Block a user