Files
Misaki.HighPerformance/Misaki.HighPerformance.Jobs/JobExecutor.cs
Misaki fe8362e029 Add custom job scheduling and dependency combiners
- Introduce `CombinedDependenciesJob` for efficient dependency handling and memory management
- Add `ScheduleCustom<T>` for user-defined job execution/free logic
- Refactor `JobInfo` and `JobDataPool<T>` for safer resource management and custom function support
- Improve SPMD extension type constraint formatting
- Update SPMD project content path and increment assembly versions
- Add unit tests for combined dependencies and custom jobs
- Remove `[Timeout]` from tests to prevent spurious failures
- Add TODO for future `WideLane` optimizations
- Replace legacy .sln with .slnx for better solution structure
2026-05-03 15:17:19 +09:00

95 lines
3.3 KiB
C#

using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace Misaki.HighPerformance.Jobs;
internal static class JobExecutor
{
public static void Execute<T>(int dataID, int dataGeneration, ref JobRanges jobRanges, ref readonly JobExecutionContext ctx)
where T : IJob
{
ref var job = ref JobDataPool<T>.GetReference(dataID, dataGeneration, out var exists);
Debug.Assert(exists, "Job data not found in the pool.");
job.Execute(in ctx);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool GetWorkerStealingRange(ref JobRanges jobRanges, out int start, out int end)
{
start = Interlocked.Add(ref jobRanges.currentIndex, jobRanges.batchSize) - jobRanges.batchSize;
if (start >= jobRanges.totalIteration)
{
end = start;
return false;
}
end = Math.Min(start + jobRanges.batchSize, jobRanges.totalIteration);
return true;
}
public static void ExecuteParallelFor<T>(int dataID, int dataGeneration, ref JobRanges jobRanges, ref readonly JobExecutionContext ctx)
where T : IJobParallelFor
{
ref var job = ref JobDataPool<T>.GetReference(dataID, dataGeneration, out var exists);
Debug.Assert(exists, "Job data not found in the pool.");
while (true)
{
if (!GetWorkerStealingRange(ref jobRanges, out var start, out var end))
{
break;
}
for (var i = start; i < end; i++)
{
job.Execute(i, in ctx);
}
}
}
public static void ExecuteParallel<T>(int dataID, int dataGeneration, ref JobRanges jobRanges, ref readonly JobExecutionContext ctx)
where T : IJobParallel
{
ref var job = ref JobDataPool<T>.GetReference(dataID, dataGeneration, out var exists);
Debug.Assert(exists, "Job data not found in the pool.");
while (true)
{
if (!GetWorkerStealingRange(ref jobRanges, out var start, out var end))
{
break;
}
job.Execute(start, end, in ctx);
}
}
public unsafe static void ExecuteCustom<T>(int dataID, int dataGeneration, ref JobRanges jobRanges, ref readonly JobExecutionContext ctx)
{
ref var job = ref JobDataPool<T>.GetReference(dataID, dataGeneration, out var exists);
Debug.Assert(exists, "Job data not found in the pool.");
ref var jobInfo = ref ctx.JobScheduler.GetJobInfoReference(ctx.SelfHandle, out var exist);
Debug.Assert(exist, "Job info not found for the executing job.");
if (jobInfo.pCustomExecutionFunc != null)
{
((delegate*<ref T, ref JobRanges, ref readonly JobExecutionContext, void>)jobInfo.pCustomExecutionFunc)(ref job, ref jobRanges, in ctx);
}
}
public unsafe static void FreeCustom<T>(ref readonly JobInfo jobInfo)
{
ref var job = ref JobDataPool<T>.GetReference(jobInfo.dataID, jobInfo.dataGeneration, out var exists);
Debug.Assert(exists, "Job data not found in the pool.");
if (jobInfo.pCustomFreeFunc != null)
{
((delegate*<ref T, void>)jobInfo.pCustomFreeFunc)(ref job);
}
JobDataPool<T>.Free(in jobInfo);
}
}