using System; using System.Collections.Generic; namespace Ariadne.PerformanceTests { /// /// A set of arcs that wake on a period, run for a stretch of frames by each way of ticking them. /// public sealed class SchedulerScenario { /// /// Stands for arcs that never wake during a run, and for arcs that run every frame. /// public const int Never = -1; public const int EveryFrame = 0; public const int FramesChecked = 300; public readonly string Name; public readonly int ArcCount; public readonly int PeriodFrames; public readonly int SpreadFrames; public SchedulerScenario(string name, int arcCount, int periodFrames, int spreadFrames = 1) { Name = name; ArcCount = arcCount; PeriodFrames = periodFrames; SpreadFrames = spreadFrames; } /// /// Makes the arcs, with their first wake times spread over the period so that they don't all wake together. /// The arcs take periods from PeriodFrames up to PeriodFrames + SpreadFrames - 1 in turn. /// public SleepyArc[] MakeArcs() { var returnMe = new SleepyArc[ArcCount]; for (int i = 0; i < returnMe.Length; ++i) { TimeSpan period = TimeSpan.Zero; TimeSpan first = TimeSpan.Zero; if (PeriodFrames == Never) { period = TimeSpan.FromDays(10000); first = period; } else if (PeriodFrames > 0) { int periodFrames = PeriodFrames + i % SpreadFrames; period = TimeSpan.FromTicks(Host.FrameLength.Ticks * periodFrames); first = TimeSpan.FromTicks(Host.FrameLength.Ticks * (1 + i % periodFrames)); } returnMe[i] = new SleepyArc(period, first); } return returnMe; } /// /// Throws unless both ways of ticking run every arc the same number of times, since timings of different work /// mean nothing. /// public void CheckSameWork(int minimumSleepFrames) { Host everyArc = new EveryArcHost(MakeArcs()); Host scheduled = new SchedulerHost(MakeArcs(), minimumSleepFrames); for (int frame = 0; frame < FramesChecked; ++frame) { everyArc.Step(); scheduled.Step(); } for (int i = 0; i < ArcCount; ++i) { if (everyArc.Arcs[i].Wakes != scheduled.Arcs[i].Wakes) { throw new InvalidOperationException(Name + ": arc " + i + " woke " + everyArc.Arcs[i].Wakes + " times when every arc was ticked, and " + scheduled.Arcs[i].Wakes + " with the scheduler."); } } } } /// /// A herd of arcs waiting on one event that happens on a period, run once by polling and once by a trigger. /// public sealed class HerdScenario { public const int Never = SchedulerScenario.Never; public readonly string Name; public readonly int ArcCount; public readonly int PeriodFrames; public HerdScenario(string name, int arcCount, int periodFrames) { Name = name; ArcCount = arcCount; PeriodFrames = periodFrames; } /// /// Gets how many arcs wake in an average frame. /// public double WakesPerFrame { get { if (PeriodFrames == Never) { return 0; } return (double)ArcCount / PeriodFrames; } } /// /// Throws unless polling and the trigger wake every arc the same number of times, since timings of different /// work mean nothing. /// public void CheckSameWork() { var polling = new PollingHerdHost(ArcCount, PeriodFrames); var triggered = new TriggerHerdHost(ArcCount, PeriodFrames); for (int frame = 0; frame < SchedulerScenario.FramesChecked; ++frame) { polling.Step(); triggered.Step(); } for (int i = 0; i < ArcCount; ++i) { if (polling.Arcs[i].Wakes != triggered.Arcs[i].Wakes) { throw new InvalidOperationException(Name + ": arc " + i + " woke " + polling.Arcs[i].Wakes + " times when polling, and " + triggered.Arcs[i].Wakes + " with the trigger."); } } } } public static class Scenarios { public static readonly int[] ArcCounts = { 1000, 10000, 100000 }; public static List BuildHerd() { var returnMe = new List(); foreach (int arcCount in ArcCounts) { returnMe.Add(new HerdScenario(arcCount + " herd, raised never", arcCount, HerdScenario.Never)); returnMe.Add(new HerdScenario(arcCount + " herd, raised every 100 frames", arcCount, 100)); returnMe.Add(new HerdScenario(arcCount + " herd, raised every 10 frames", arcCount, 10)); returnMe.Add(new HerdScenario(arcCount + " herd, raised every 3 frames", arcCount, 3)); returnMe.Add(new HerdScenario(arcCount + " herd, raised every 2 frames", arcCount, 2)); returnMe.Add(new HerdScenario(arcCount + " herd, raised every frame", arcCount, 1)); } return returnMe; } public static List BuildScheduler() { var returnMe = new List(); foreach (int arcCount in ArcCounts) { returnMe.Add(new SchedulerScenario(arcCount + " arcs, none wake", arcCount, SchedulerScenario.Never)); returnMe.Add(new SchedulerScenario(arcCount + " arcs, wake every 100 frames", arcCount, 100)); returnMe.Add(new SchedulerScenario(arcCount + " arcs, wake every 20 frames", arcCount, 20)); returnMe.Add(new SchedulerScenario(arcCount + " arcs, wake every 10 frames", arcCount, 10)); returnMe.Add(new SchedulerScenario(arcCount + " arcs, wake every 12 to 20 frames", arcCount, 12, spreadFrames: 9)); returnMe.Add(new SchedulerScenario(arcCount + " arcs, wake every 5 frames", arcCount, 5)); returnMe.Add(new SchedulerScenario(arcCount + " arcs, wake every frame", arcCount, 1)); returnMe.Add(new SchedulerScenario(arcCount + " arcs, never wait", arcCount, SchedulerScenario.EveryFrame)); } return returnMe; } } }