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;
}
}
}