|
// helper extensions for testing/dumping performance results; |
|
// may not have all methods present |
|
|
|
|
|
/// <summary>Silly convenience initializer for creating Vs tasks with a dictionary; |
|
/// can use <code>new Perf { { "task1", n => whatever }, {"task2", n => bar}...}.Vs()</code> |
|
/// <para>see http://stackoverflow.com/a/14000325/1037948 </para> |
|
/// </summary> |
|
/// <remarks>Doesn't automatically call <see cref="Vs"/> because...</remarks> |
|
public class Perf : Dictionary<string, Action<int>> {} |
|
|
|
/// <summary>Silly convenience initializer for creating Vs tasks with a dictionary that can also provides some output; |
|
/// can use <code>new Perf<T> { { "task1", n => whatever }, {"task2", n => bar}...}.Vs()</code> |
|
/// <para>see http://stackoverflow.com/a/14000325/1037948 </para> |
|
/// <example><code> |
|
/// var p = new Perf{string} { |
|
/// { "int", n => i.GetType().Name }, |
|
/// { "double", n => d.GetType().Name }, |
|
/// { "string", n => s.GetType().Name }, |
|
/// { "object", n => o.GetType().Name }, |
|
/// { "struct", n => t.GetType().Name }, |
|
/// { "anon", n => a.GetType().Name }, |
|
/// }.Vs("gettype"); |
|
/// </code></example> |
|
/// </summary> |
|
/// <remarks>Doesn't automatically call <see cref="Vs"/> because...</remarks> |
|
public class Perf<T> : Dictionary<string, Func<int, T>> {} |
|
|
|
|
|
public static class PerfExtensions { |
|
/// <summary> Performance check -- how long do X repetitions of a task take?</summary> |
|
public static TimeSpan Perf(this string reportTitle, Action<int> task, int repetitions = 10000, bool noShow = false, bool showProgress = false) { |
|
Util.ProgressBar pb = null; |
|
double progress = 0; |
|
if(showProgress) { |
|
pb = new Util.ProgressBar("Perf Progress " + reportTitle); |
|
pb.Dump(); |
|
} |
|
|
|
// http://stackoverflow.com/questions/28637/is-datetime-now-the-best-way-to-measure-a-functions-performance |
|
Stopwatch sw = Stopwatch.StartNew(); |
|
|
|
for (int i = 0; i < repetitions; i++) { |
|
task(i); |
|
if(showProgress) pb.Percent = (int)(progress += 100D/(double)repetitions); |
|
} |
|
|
|
sw.Stop(); |
|
if( !noShow ) perfElapsed(sw.Elapsed).Dump(string.IsNullOrEmpty(reportTitle) ? null : string.Format("{0} ({1}x)", reportTitle, repetitions)); |
|
return sw.Elapsed; |
|
} |
|
|
|
private static string perfElapsed(TimeSpan ts) { |
|
return string.Format("{0} ticks elapsed ({1} ms)", ts.Ticks, ts.TotalMilliseconds); |
|
} |
|
private static int indexOfMost<T>(IEnumerable<T> list, Func<T, T, bool> compare) { |
|
int i = 0, j = 0; |
|
T min = list.First(); |
|
foreach(var s in list) { |
|
if(compare(min, s)) { |
|
min = s; |
|
j = i; |
|
} |
|
i++; |
|
} |
|
|
|
return j; |
|
} |
|
|
|
/// <summary> Performance check -- how long do 10K repetitions of the listed tasks take? Can also print the results (1 or more) of each task. |
|
/// <example><code> |
|
/// var p = new Perf{string} { |
|
/// { "int", n => i.GetType().Name }, |
|
/// { "double", n => d.GetType().Name }, |
|
/// { "string", n => s.GetType().Name }, |
|
/// { "object", n => o.GetType().Name }, |
|
/// { "struct", n => t.GetType().Name }, |
|
/// { "anon", n => a.GetType().Name }, |
|
/// }.Vs("gettype", outputTimes: 10); |
|
/// </code></example> |
|
/// </summary> |
|
public static TimeSpan[] Vs<T>(this Dictionary<string, Func<int,T>> tasks, string reportTitle = null, int repetitions = 10000, bool noShow = false, int outputTimes = 1, bool showProgress = false) { |
|
if(outputTimes > 0) { |
|
string outputTitle = "Output" + (reportTitle == null ? null : " for " + reportTitle); |
|
if(outputTimes == 1) tasks.Do().Dump(outputTitle); |
|
else tasks.Do(outputTimes).Dump(outputTitle); |
|
} |
|
|
|
return tasks.ToDictionary(k => k.Key, v => { Action<int> a = n => v.Value(n); return a; }).Vs(reportTitle, repetitions, noShow, showProgress); |
|
} |
|
|
|
|
|
/// <summary> Performance check -- how long do 10K repetitions of the listed tasks take?</summary> |
|
public static TimeSpan[] Vs(this Dictionary<string, Action<int>> tasks, string reportTitle = null, int repetitions = 10000, bool noShow = false, bool showProgress = false) { |
|
// tasks.Dump(); |
|
|
|
Util.ProgressBar pb = null; |
|
double progress = 0; |
|
if(showProgress) { |
|
pb = new Util.ProgressBar(reportTitle + " Progress"); |
|
pb.Dump("Note that performance calculation will be affected by this display"); |
|
} |
|
|
|
var n = tasks.Count(); |
|
var r = new Dictionary<string, TimeSpan>(); |
|
foreach (var task in tasks) |
|
{ |
|
r.Add(task.Key, task.Key.Perf(task.Value, repetitions, true, showProgress)); |
|
if(showProgress) pb.Percent = (int)(progress += 100D/(double)tasks.Count()); |
|
} |
|
|
|
if (!noShow) |
|
{ |
|
reportTitle = (reportTitle ?? "Vs");// + ": (" + string.Join(") vs (", tasks.Keys) + ")"; |
|
|
|
// get the best |
|
var minIndex = indexOfMost(r, (a, b) => a.Value > b.Value); |
|
|
|
r |
|
.OrderBy(kv => kv.Value) |
|
.Select(kv => new KeyValuePair<string, string>(kv.Key, perfElapsed(kv.Value))) |
|
|
|
// .ToDictionary(kv => kv.Key, kv => perfElapsed(kv.Value)) |
|
// r.Select(ts => perfElapsed(ts)).Concat(new[] { "winner: " + (hasSubs ? subReportTitles.ElementAt(minIndex) : minIndex + "th") }) |
|
/* |
|
Tuple.Create( |
|
r.Select(ts => perfElapsed(ts)) |
|
, "winner: " + (hasSubs ? subReportTitles.ElementAt(minIndex) : minIndex + "th") |
|
) |
|
*/ |
|
|
|
.Dump(reportTitle); |
|
|
|
(">> winner: " + tasks.Keys.ElementAt(minIndex)).Dump(); |
|
} |
|
|
|
return r.Select(kv => kv.Value).ToArray(); |
|
} |
|
/// <summary> Performance check -- how long do X repetitions of the listed tasks take?</summary> |
|
public static TimeSpan[] Vs(this string reportTitle, IEnumerable<string> subReportTitles, int repetitions, bool noShow, params Action<int>[] tasks) { |
|
return Vs(Enumerable.Range(0, tasks.Length).ToDictionary(i => null == subReportTitles || subReportTitles.Count() <= i ? i.ToString() : subReportTitles.ElementAt(i), i => tasks[i]) |
|
, reportTitle, repetitions, noShow); |
|
} |
|
|
|
/// <summary> Performance check -- how long do 10K repetitions of the listed tasks take?</summary> |
|
public static TimeSpan[] Vs(this string reportTitle, IEnumerable<string> reportTitles, params Action<int>[] tasks) { |
|
return reportTitle.Vs(reportTitles, 10000, false, tasks); |
|
} |
|
/// <summary> Performance check -- how long do 10K repetitions of the listed tasks take?</summary> |
|
public static TimeSpan[] Vs(this string reportTitle, params Action<int>[] tasks) { |
|
return reportTitle.Vs(null, tasks); |
|
} |
|
|
|
|
|
public static void Do(this Dictionary<string, Action<int>> tasks) { |
|
foreach(var kv in tasks) { |
|
kv.Value(1); |
|
} |
|
} |
|
/// <summary>Helpers for evaluating Perf</summary> |
|
public static Dictionary<string,T> Do<T>(this Dictionary<string, Func<int,T>> tasks) { |
|
return tasks.ToDictionary(kv => kv.Key, kv => kv.Value(1)); |
|
} |
|
/// <summary>Helpers for evaluating Perf</summary> |
|
public static Dictionary<string, object> Do<T>(this Dictionary<string, Func<int,T>> tasks, int repetitions) { |
|
// HorizontalRun lists things horizontally vs. vertically -- even objects! http://stackoverflow.com/questions/3555317/linqpad-extension-methods |
|
return tasks.ToDictionary(kv => kv.Key, kv => Util.HorizontalRun(true, Enumerable.Range(0,repetitions).Select(i => kv.Value(i)))); |
|
} |
|
} |