using System;
using Amphora;
namespace Ariadne.PerformanceTests
{
///
/// The context the benchmark arcs run in, which only the polling arcs look at.
///
public sealed class FrameWorld
{
///
/// How many times the event the polling arcs watch for has happened.
///
public int Raises;
}
///
/// An arc that counts how often it wakes, which is how two ways of ticking it are shown to do the same work.
///
public abstract class WakeCountingArc : Arc
{
public int Wakes;
protected override void Run(FrameWorld world)
{
}
}
///
/// Wakes on a fixed period, or runs every frame when the period is zero, and counts how often it runs.
///
public sealed class SleepyArc : WakeCountingArc
{
public readonly TimeSpan Period;
public Duration Deadline;
public SleepyArc(TimeSpan period, TimeSpan firstDeadline)
{
Period = period;
Deadline = firstDeadline;
}
protected override bool __Resume(FrameWorld world, TickTime time)
{
if (Period == TimeSpan.Zero)
{
++Wakes;
return false;
}
if (time.GameTime >= Deadline)
{
++Wakes;
Deadline = time.GameTime + Period;
}
__CurrentFrame.Sleep(Deadline);
return false;
}
}
///
/// The frame of a herd arc, which holds its trigger wait the way a generated frame does.
///
public sealed class HerdFrame : Frame
{
public readonly TriggerWait Waiting = new TriggerWait(1);
public override void AttachWaits(WakeSlot slot)
{
Waiting.Attach(slot);
}
}
///
/// Waits on a trigger over and over, and counts the times the trigger woke it.
///
public sealed class HerdArc : WakeCountingArc
{
private readonly Trigger trigger;
private bool started;
public HerdArc(Trigger trigger)
{
this.trigger = trigger;
}
protected override Frame __CreateFrame()
{
return new HerdFrame();
}
protected override bool __Resume(FrameWorld world, TickTime time)
{
var frame = (HerdFrame)__CurrentFrame;
if (started)
{
++Wakes;
}
started = true;
frame.Waiting.Begin(frame, requiresAll: false);
frame.Waiting.Add(trigger);
frame.Suspend();
return false;
}
}
///
/// Checks a flag every frame, the way a script that loops on a Yield() does without triggers.
///
public sealed class PollingArc : WakeCountingArc
{
private int seen;
protected override bool __Resume(FrameWorld world, TickTime time)
{
if (world.Raises != seen)
{
seen = world.Raises;
++Wakes;
}
return false;
}
}
///
/// Ticks a set of arcs one frame at a time, in whichever way is being timed.
///
public abstract class Host
{
public static readonly TimeSpan FrameLength = TimeSpan.FromMilliseconds(16);
private readonly ExceptionSandbox sandbox = new ExceptionSandbox(typeof(AriadneSites).Assembly);
private long frame;
public readonly FrameWorld World = new FrameWorld();
public readonly WakeCountingArc[] Arcs;
protected Host(WakeCountingArc[] arcs)
{
Arcs = arcs;
}
public int TotalWakes
{
get
{
int returnMe = 0;
foreach (WakeCountingArc arc in Arcs)
{
returnMe += arc.Wakes;
}
return returnMe;
}
}
protected abstract void TickAll(FrameWorld world, TickContext tick);
///
/// Runs one frame, with the game time one frame later than the last.
///
public void Step()
{
++frame;
TickAll(World, new TickContext(sandbox, FrameLength, TimeSpan.FromTicks(FrameLength.Ticks * frame)));
}
///
/// Runs frames.
///
public void Step(int frames)
{
for (int i = 0; i < frames; ++i)
{
Step();
}
}
}
///
/// Ticks every arc each frame, the way a host without a scheduler does.
///
public sealed class EveryArcHost : Host
{
public EveryArcHost(WakeCountingArc[] arcs)
: base(arcs)
{
}
protected override void TickAll(FrameWorld world, TickContext tick)
{
for (int i = 0; i < Arcs.Length; ++i)
{
Arcs[i].Tick(world, tick);
}
}
}
///
/// Hands the arcs to an ArcScheduler.
///
public sealed class SchedulerHost : Host
{
private readonly ArcScheduler scheduler = new ArcScheduler();
public SchedulerHost(WakeCountingArc[] arcs, int minimumSleepFrames)
: base(arcs)
{
scheduler.MinimumSleepFrames = minimumSleepFrames;
foreach (WakeCountingArc arc in arcs)
{
scheduler.Add(arc);
}
}
protected override void TickAll(FrameWorld world, TickContext tick)
{
scheduler.Tick(world, tick);
}
}
///
/// Ticks polling arcs every frame, with the event they watch for happening on a period.
///
public sealed class PollingHerdHost : Host
{
private readonly int periodFrames;
private int framesSinceRaise;
public PollingHerdHost(int arcCount, int periodFrames)
: base(MakeArcs(arcCount))
{
this.periodFrames = periodFrames;
}
private static WakeCountingArc[] MakeArcs(int arcCount)
{
var returnMe = new WakeCountingArc[arcCount];
for (int i = 0; i < returnMe.Length; ++i)
{
returnMe[i] = new PollingArc();
}
return returnMe;
}
protected override void TickAll(FrameWorld world, TickContext tick)
{
for (int i = 0; i < Arcs.Length; ++i)
{
Arcs[i].Tick(world, tick);
}
// The event comes after the ticks so that a herd which has yet to subscribe on frame one still sees it.
if (++framesSinceRaise == periodFrames)
{
framesSinceRaise = 0;
++world.Raises;
}
}
}
///
/// Hands arcs waiting on one trigger to an ArcScheduler, and raises the trigger on a period.
///
public sealed class TriggerHerdHost : Host
{
private readonly ArcScheduler scheduler = new ArcScheduler();
private readonly Trigger trigger;
private readonly int periodFrames;
private int framesSinceRaise;
public TriggerHerdHost(int arcCount, int periodFrames)
: this(arcCount, periodFrames, new Trigger())
{
}
private TriggerHerdHost(int arcCount, int periodFrames, Trigger trigger)
: base(MakeArcs(arcCount, trigger))
{
this.periodFrames = periodFrames;
this.trigger = trigger;
foreach (WakeCountingArc arc in Arcs)
{
scheduler.Add(arc);
}
}
private static WakeCountingArc[] MakeArcs(int arcCount, Trigger trigger)
{
var returnMe = new WakeCountingArc[arcCount];
for (int i = 0; i < returnMe.Length; ++i)
{
returnMe[i] = new HerdArc(trigger);
}
return returnMe;
}
protected override void TickAll(FrameWorld world, TickContext tick)
{
scheduler.Tick(world, tick);
if (++framesSinceRaise == periodFrames)
{
framesSinceRaise = 0;
trigger.Raise();
}
}
}
}