using Misaki.HighPerformance.Jobs; using System.Numerics; using System.Runtime.CompilerServices; namespace Misaki.HighPerformance.Mathematics.SPMD; public interface IJobSPMD where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators { void Execute(int baseIndex, int threadIndex) where TLane : ISPMD; } internal struct SPMDJobWrapper : IJobParallelFor where T : unmanaged, IJobSPMD where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators { public T innerJob; public int totalCount; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Execute(int loopIndex, int threadIndex) { var baseIndex = loopIndex * WideLane.LaneWidth; var remaining = totalCount - baseIndex; if (remaining >= WideLane.LaneWidth) { innerJob.Execute>(baseIndex, threadIndex); } else { for (var i = 0; i < remaining; i++) { innerJob.Execute>(baseIndex + i, threadIndex); } } } } internal struct SPMDScalerJobWrapper : IJobParallelFor where T : unmanaged, IJobSPMD where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators { public T innerJob; public int totalCount; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Execute(int loopIndex, int threadIndex) { innerJob.Execute>(loopIndex, threadIndex); } } public static class IJobParallelForSPMDExtensions { public static void Run(this ref T job, int totalCount, int threadIndex) where T : unmanaged, IJobSPMD where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators { var iterations = (totalCount + WideLane.LaneWidth - 1) / WideLane.LaneWidth; for (var loopIndex = 0; loopIndex < iterations; loopIndex++) { var baseIndex = loopIndex * WideLane.LaneWidth; var remaining = totalCount - baseIndex; if (remaining >= WideLane.LaneWidth) { job.Execute>(baseIndex, threadIndex); } else { for (var i = 0; i < remaining; i++) { job.Execute>(baseIndex + i, threadIndex); } } } } public static JobHandle ScheduleParallelSPDM(this JobScheduler jobScheduler, ref T job, int totalCount, int batchSize, int threadIndex, JobHandle dependency) where T : unmanaged, IJobSPMD where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators { if (WideLane.IsSupported) { var warper = new SPMDJobWrapper { innerJob = job, totalCount = totalCount, }; var iterations = (totalCount + WideLane.LaneWidth - 1) / WideLane.LaneWidth; return jobScheduler.ScheduleParallel(ref warper, iterations, batchSize, threadIndex, dependency); } else { var warper = new SPMDScalerJobWrapper { innerJob = job, totalCount = totalCount, }; return jobScheduler.ScheduleParallel(ref warper, totalCount, batchSize, threadIndex, dependency); } } }