using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
namespace Glass.PerformanceTests
{
///
/// Runs a benchmark once over a small text with its output thrown away, so the compiling and first-use setup of the
/// code it times is done before the run that counts. Without it the first slice or frame of every run carries tens
/// of milliseconds that the later ones don't.
///
public static class WarmStart
{
public const int Chars = 200000;
///
/// Gets whether a warm-up is running, when a row needs only enough calls to compile what it times.
///
public static bool IsWarming { get; private set; }
///
/// Returns the arguments with every text size, which starts with a digit, set to the warm-up's, adding one
/// after the benchmark's name when there is none.
///
public static string[] Shrunk(string[] args)
{
string size = Chars.ToString(CultureInfo.InvariantCulture);
var returnMe = new List();
bool sized = false;
foreach (string argument in args)
{
if (argument.Length > 0 && char.IsDigit(argument[0]))
{
returnMe.Add(size);
sized = true;
}
else
{
returnMe.Add(argument);
}
}
if (!sized)
{
returnMe.Insert(1, size);
}
return returnMe.ToArray();
}
///
/// Warms up, and then runs a benchmark for real. A warm-up that fails ends the run, since it has already said
/// why.
///
public static int Run(Func benchmark, string[] args)
{
TextWriter output = Console.Out;
int warmExit;
IsWarming = true;
Console.SetOut(TextWriter.Null);
try
{
warmExit = benchmark(Shrunk(args));
}
finally
{
Console.SetOut(output);
IsWarming = false;
}
if (warmExit != 0)
{
return warmExit;
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
return benchmark(args);
}
}
}