Implement scheduling system with parallel execution support and dummy job systems

This commit is contained in:
max
2026-08-07 12:14:02 +02:00
parent a0f2713fdc
commit 0132c1326d
14 changed files with 844 additions and 5 deletions
@@ -0,0 +1,47 @@
using MoonTools.ECS;
using Nerfed.Runtime.Scheduling;
namespace Nerfed.Runtime.Systems.Synthetic;
public sealed class DummyLongJobConsumerSystem : MoonTools.ECS.System, IParallelSystemMetadata
{
private readonly JobSystem jobs;
private readonly DummyLongJobSharedState state;
private readonly bool requireCompletion;
public DummyLongJobConsumerSystem(
World world,
DummyLongJobSharedState state,
JobSystem jobs,
bool requireCompletion) : base(world)
{
this.state = state;
this.jobs = jobs;
this.requireCompletion = requireCompletion;
}
public string ScheduleName => nameof(DummyLongJobConsumerSystem);
// Consumer/apply stage is where structural world work should happen in real systems.
public SystemAccessDeclaration AccessDeclaration => SystemAccessDeclaration.Empty;
public override void Update(TimeSpan delta)
{
JobHandle handle = state.InFlight;
if (!handle.IsValid)
{
return;
}
if (!requireCompletion && !jobs.IsCompleted(handle))
{
return;
}
jobs.Wait(handle);
jobs.TryForget(handle);
state.InFlight = default;
state.ConsumeCount++;
}
}
@@ -0,0 +1,53 @@
using MoonTools.ECS;
using Nerfed.Runtime.Scheduling;
namespace Nerfed.Runtime.Systems.Synthetic;
public sealed class DummyLongJobProducerSystem : MoonTools.ECS.System, IParallelSystemMetadata
{
private readonly JobSystem jobs;
private readonly DummyLongJobSharedState state;
private readonly TimeSpan duration;
private readonly DummyWorkloadMode mode;
private readonly JobDeadlineMode deadline;
private int seed;
public DummyLongJobProducerSystem(
World world,
DummyLongJobSharedState state,
JobSystem jobs,
TimeSpan duration,
DummyWorkloadMode mode,
JobDeadlineMode deadline,
int initialSeed = 1) : base(world)
{
this.state = state;
this.jobs = jobs;
this.duration = duration;
this.mode = mode;
this.deadline = deadline;
seed = initialSeed;
}
public string ScheduleName => nameof(DummyLongJobProducerSystem);
// Producer pattern: reads current world state and emits immutable job payload.
public SystemAccessDeclaration AccessDeclaration => SystemAccessDeclaration.Empty;
public override void Update(TimeSpan delta)
{
if (state.InFlight.IsValid)
{
return;
}
int capturedSeed = seed;
state.InFlight = jobs.Submit(
() => state.LastResult = DummyWorkloadRunner.Run(duration, mode, capturedSeed),
deadline,
nameof(DummyLongJobProducerSystem));
seed = unchecked(capturedSeed + 1);
state.SubmitCount++;
}
}
@@ -0,0 +1,9 @@
namespace Nerfed.Runtime.Systems.Synthetic;
public sealed class DummyLongJobSharedState
{
public JobHandle InFlight;
public int LastResult;
public int SubmitCount;
public int ConsumeCount;
}
@@ -0,0 +1,34 @@
using MoonTools.ECS;
using Nerfed.Runtime.Scheduling;
namespace Nerfed.Runtime.Systems.Synthetic;
public sealed class DummyMainThreadWorkSystem : MoonTools.ECS.System, IParallelSystemMetadata
{
private readonly TimeSpan duration;
private readonly DummyWorkloadMode mode;
private readonly string scheduleName;
private int seed;
public DummyMainThreadWorkSystem(
World world,
TimeSpan duration,
DummyWorkloadMode mode,
string scheduleName,
int initialSeed = 1) : base(world)
{
this.duration = duration;
this.mode = mode;
this.scheduleName = scheduleName;
seed = initialSeed;
}
public string ScheduleName => scheduleName;
public SystemAccessDeclaration AccessDeclaration => SystemAccessDeclaration.Empty;
public override void Update(TimeSpan delta)
{
seed = DummyWorkloadRunner.Run(duration, mode, seed);
}
}
@@ -0,0 +1,8 @@
namespace Nerfed.Runtime.Systems.Synthetic;
public enum DummyWorkloadMode
{
Spin,
Sleep,
Compute,
}
@@ -0,0 +1,54 @@
using System.Diagnostics;
namespace Nerfed.Runtime.Systems.Synthetic;
internal static class DummyWorkloadRunner
{
public static int Run(TimeSpan duration, DummyWorkloadMode mode, int seed)
{
return mode switch
{
DummyWorkloadMode.Sleep => Sleep(duration, seed),
DummyWorkloadMode.Compute => Compute(duration, seed),
_ => Spin(duration, seed),
};
}
private static int Sleep(TimeSpan duration, int seed)
{
if (duration > TimeSpan.Zero)
{
Thread.Sleep(duration);
}
return seed;
}
private static int Spin(TimeSpan duration, int seed)
{
Stopwatch stopwatch = Stopwatch.StartNew();
int value = seed;
while (stopwatch.Elapsed < duration)
{
value = unchecked((value * 1664525) + 1013904223);
Thread.SpinWait(128);
}
return value;
}
private static int Compute(TimeSpan duration, int seed)
{
Stopwatch stopwatch = Stopwatch.StartNew();
int value = seed;
while (stopwatch.Elapsed < duration)
{
for (int i = 0; i < 2048; i++)
{
value = unchecked((value << 5) - value + i);
}
}
return value;
}
}