Skip to content

Instantly share code, notes, and snippets.

@zaus
Last active February 20, 2021 15:42
Show Gist options
  • Save zaus/8055941 to your computer and use it in GitHub Desktop.
Save zaus/8055941 to your computer and use it in GitHub Desktop.
Linqpad Extensions - `.Title()`, `.DumpFormat(tokens)`, `.Perf(task)`, `.Vs(tasks)`
/// <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&lt;T&gt; { { "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>> {}
/// <summary>The actual extensions</summary>
public static class MyExtensions {
// Write custom extension methods here. They will be available to all queries.
#region ------------- pretty printing -----------------
private const char TITLE_UNDERLINE = '-';
public static void Title(this string msg, char underline = TITLE_UNDERLINE, int pad = 4, char padding = ' ') {
new string(underline, msg.Length + pad*2).Dump(new string(padding, pad) + msg + new string(padding, pad));
}
public static void TitleFormat(this string msg, params object[] tokens) {
var t = string.Format(msg, tokens);
new string(TITLE_UNDERLINE, t.Length).Dump(t);
}
public static void DumpFormat(this string msg, params object[] tokens) {
string.Format(msg, tokens).Dump();
}
public static void DumpTitle(string msg, char underline = TITLE_UNDERLINE, int pad = 4, char padding = ' ') {
msg.Title(underline, pad, padding);
}
// CSV readfile -- http://www.developertipoftheday.com/2012/10/read-csv-in-linqpad.html
#endregion ------------- pretty printing -----------------
/// <summary>Useful for enumerating all of the property-values of an object
/// where Dump instead gets the `ToString`; ex) new Uri(...).Props()
/// </summary>
public static Dictionary<string, object> Props(this object me) {
return me.GetType().GetProperties().Where(o => o.CanRead).ToDictionary(o => o.Name, o => o.GetValue(me));
}
/// <summary>Useful for enumerating all of the property-values of an object
/// where Dump instead gets the `ToString`; ex) new Uri(...).RawDump()
/// </summary>
public static void RawDump(this object me, string title = null) {
me.Props().Dump(title);
}
public static void Dump<T>(this IEnumerable<Dictionary<string, T>> ds) {
ds.ToDataTable().Dump();
}
public static DataTable ToDataTable<T>(this IEnumerable<Dictionary<string,T>> source)
{
// https://stackoverflow.com/questions/20530209/visualizing-lists-of-dictionaries-as-a-datagrid-in-linqpad
DataTable table = new DataTable();
foreach(var dict in source)
{
var dr = table.NewRow();
foreach(var entry in dict)
{
if (!table.Columns.Contains(entry.Key))
table.Columns.Add(entry.Key, typeof(T));
dr[entry.Key] = entry.Value;
}
table.Rows.Add(dr);
}
return table;
}
/// <summary>Dump all in a table, given as [ title1, object1, title2, object2, etc ]</summary>
public static void DumpAll(this string title, params object[] nameAndValues) {
nameAndValues.DumpAll(title);
}
/// <summary>Dump all in a table, given as [ title1, object1, title2, object2, etc ]</summary>
public static void DumpAll(this IEnumerable<object> nameAndValues, string title = null) {
var names = string.Join(",", nameAndValues.Where( (o,i) => i % 2 == 0 ));
var values = nameAndValues.Where( (o,i) => i % 2 == 1 ).ToArray();
Util.HorizontalRun(names, values).Dump(title);
}
/// <summary>Dump all in a table, given as [ title1, object1, title2, object2, etc ]</summary>
public static void DumpAll(this IEnumerable<KeyValuePair<string, object>> nameAndValues) {
( nameAndValues.SelectMany(kv => new object[] { kv.Key, kv.Value })).DumpAll();
}
#region ------------- benchmarking -----------------
/// <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, repetitions).Dump(string.IsNullOrEmpty(reportTitle) ? null : string.Format("{0} ({1}x)", reportTitle, repetitions));
return sw.Elapsed;
}
private static string perfElapsed(TimeSpan ts, int repetitions) {
return string.Format("{0} ticks elapsed ({1} ms) [in {2:0.#}K reps, {3} ms per]", ts.Ticks, ts.TotalMilliseconds, repetitions/1000D, ts.TotalMilliseconds / (double) repetitions);
}
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, repetitions)))
// .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))));
}
/// <summary>
/// Turn an anonymous object into a Dictionary; internally similar to System.Web.Mvc.HtmlHelper.AnonymousObjectToHtmlAttributes
/// </summary>
/// <remarks>http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/5ac8586b78b3#src%2fSystem.Web.Mvc%2fHtmlHelper.cs</remarks>
/// <param name="collection">the anonymous collection of property-values</param>
/// <param name="explicitChangeType">flag whether or not to assume type conversion can occur automatically (false), or if we have to explicitly change the type (true)</param>
/// <returns>A typecast dictionary from the object</returns>
public static IDictionary<string, Tvalue> AnonymousObjectToDictionary<Tvalue>(this object collection, bool explicitChangeType = false) {
if (collection != null) {
// using manual property getter via http://weblogs.asp.net/leftslipper/archive/2007/09/24/using-c-3-0-anonymous-types-as-dictionaries.aspx
/*
return GetProperties(collection).ToDictionary(p => p.Name, p =>
(Tvalue)(implicitConvert ? Convert.ChangeType(p.Value, typeof(Tvalue)) : p.Value));
*/
// the following seems to throw up on some dictionaries when it encounters EqualityComparator properties???
return collection.GetType()
.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public) // ignore "intrinsic" properties, like EqualityComparer, etc
.ToDictionary(p => p.Name, p => {
if (explicitChangeType) {
return (Tvalue)Convert.ChangeType(p.GetValue(collection, null), typeof(Tvalue));
}
else {
return (Tvalue)p.GetValue(collection, null);
}
});
/* */
}
return new Dictionary<string, Tvalue>();
}
#endregion ------------- benchmarking -----------------
#region ---------- JSON Extensions ----------
// http://geekswithblogs.net/EltonStoneman/archive/2012/05/11/extension-method-for-outputting-formatted-json-in-linqpad.aspx
public static object DumpJson(this object value, string description = null) {
return GetJsonDumpTarget(value).Dump(description);
}
public static object DumpJson(this object value, string description, int depth) {
return GetJsonDumpTarget(value).Dump(description, depth);
}
public static object DumpJson(this object value, string description, bool toDataGrid) {
return GetJsonDumpTarget(value).Dump(description, toDataGrid);
}
private static object GetJsonDumpTarget(object value) {
object dumpTarget = value;
//if this is a string that contains a JSON object, do a round-trip serialization to format it:
var stringValue = value as string;
if (stringValue != null) {
if (stringValue.Trim().StartsWith("{")) {
var obj = JsonConvert.DeserializeObject(stringValue);
dumpTarget = JsonConvert.SerializeObject(obj, Newtonsoft.Json.Formatting.Indented);
}
else {
dumpTarget = stringValue;
}
}
else {
dumpTarget = JsonConvert.SerializeObject(value, Newtonsoft.Json.Formatting.Indented);
}
return dumpTarget;
}
#endregion ---------- JSON Extensions ----------
/// <summary>Get 5 paragraphs of lorem ipsum</summary>
public static string Lipsum() {
return @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris sit amet tellus ut eros pretium ultricies. Praesent egestas eros vitae dui ultricies porta. Mauris at nisi urna. Sed quis congue libero. Quisque sollicitudin, orci non bibendum lacinia, sapien turpis ullamcorper nulla, sit amet rhoncus leo felis ut neque. Nulla nec auctor lacus. Integer purus sapien, volutpat sit amet est eget, pharetra luctus turpis. Curabitur id aliquam erat. Aenean tincidunt efficitur aliquet.
Duis non aliquam arcu, a viverra mauris. Sed non turpis eu mauris eleifend fringilla eget vitae purus. Suspendisse sit amet magna pellentesque, luctus augue lobortis, placerat dui. In ultrices ex at velit lacinia elementum. Quisque sed lacinia nisl. Pellentesque ante mi, tincidunt in ligula a, pharetra varius metus. Integer nibh neque, varius a maximus a, porta nec odio. Sed ac risus dictum nulla bibendum mattis quis id enim. Donec enim turpis, sodales non pellentesque non, pretium sit amet metus. Quisque velit leo, scelerisque non finibus et, gravida at tellus. Ut tristique consequat tortor, posuere malesuada metus consequat eu. Sed eu condimentum felis, sit amet aliquam nisi. Etiam commodo semper justo, quis ullamcorper nulla fringilla quis. Donec mattis ante id erat tempus, a mattis tortor viverra.
In dapibus enim a mi sodales sollicitudin. Praesent scelerisque orci neque, id aliquam enim egestas sit amet. In tincidunt egestas consequat. Pellentesque mattis elementum erat quis vestibulum. Mauris leo nibh, maximus maximus magna sit amet, malesuada congue dolor. Suspendisse nec luctus elit. Mauris porttitor at orci non pellentesque. Sed pellentesque facilisis tellus, quis fringilla velit porttitor a. Fusce vitae risus aliquet, viverra justo et, fermentum neque. Nullam vehicula libero id gravida auctor. Duis sed vestibulum augue, nec ornare est. Nullam mattis vehicula odio, ut ultricies quam accumsan ut. Quisque ac urna placerat, feugiat tellus eu, dapibus metus. Duis pharetra non augue ac sollicitudin. Etiam eget justo eu leo pellentesque mattis.
Quisque venenatis et purus quis mattis. Praesent rutrum diam nibh, quis consequat urna accumsan in. Mauris auctor dui ex, et dictum diam cursus sit amet. Phasellus nec scelerisque velit. Nullam risus odio, consequat in pellentesque eu, volutpat eget eros. Mauris tincidunt nulla at eleifend euismod. Fusce commodo quam a erat lacinia, ut rutrum tortor porta. Mauris eu sem quis erat fringilla aliquet. Etiam dolor arcu, viverra eu semper non, cursus at lorem. Fusce eleifend molestie eros, eu luctus est placerat non. Donec libero est, posuere fermentum tempor sit amet, tempus eget nibh. Donec sit amet ipsum sollicitudin, feugiat massa ut, consequat orci. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Mauris non ultrices sapien, sit amet finibus ligula. Vestibulum auctor lorem eu lectus auctor accumsan. Vestibulum condimentum, tortor id imperdiet elementum, dui sem aliquet turpis, nec aliquet sem quam eget mauris.
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Pellentesque et gravida nisl. Cras ac imperdiet sem, in ultricies turpis. Pellentesque placerat neque sed diam blandit lobortis. Quisque tempus volutpat augue. Aenean vulputate non sem eu malesuada. Praesent sed scelerisque purus, sit amet semper dolor. Maecenas semper imperdiet ex, id volutpat magna fermentum in. Aenean ultrices a nibh eget consectetur. Mauris justo ipsum, gravida eget dolor at, sagittis tincidunt ipsum. Praesent iaculis sagittis massa at placerat. Donec nec mauris bibendum, suscipit sapien eget, eleifend elit. Aliquam molestie felis mi, nec pretium nunc rutrum ut. Sed ac mattis ligula, a aliquam metus. Morbi venenatis augue enim, ac dignissim erat finibus in.";
}
}
#region ------------------ TESTING ----------------
public class AssertionException : Exception {
public AssertionException(string message, params object[] tokens) : base(string.Format(message, tokens)) {}
}
public static class Assert {
public static void IsTrue(bool expected, string reportFormat = null, params object[] reportTokens) {
if(!expected) throw new AssertionException(reportFormat ?? "Assertion failed", reportTokens);
}
public static void IsFalse(bool expected, string reportFormat = null, params object[] reportTokens) {
if(expected) throw new AssertionException(reportFormat ?? "Assertion failed", reportTokens);
}
public static void AreEqual(object first, object second, string reportFormat = null, params object[] reportTokens) {
IsTrue(first.Equals(second), string.Format("Assertion failed: {0} did not equal {1}; ", first, second) + Environment.NewLine + reportFormat, reportTokens);
}
public static void AreNotEqual(object first, object second, string reportFormat = null, params object[] reportTokens) {
IsFalse(first.Equals(second), string.Format("Assertion failed: {0} should not have equalled {1}; ", first, second) + Environment.NewLine + reportFormat, reportTokens);
}
}
/// <summary>Auto-run all public methods in this class, like a UnitTest harness</summary>
public abstract class UnitTestHarness {
public void Run() {
var _thisMethod = System.Reflection.MethodBase.GetCurrentMethod().Name;
var methods = this.GetType().GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public)
// ignore this one!
.Where(m => m.Name != _thisMethod)
.ToArray();
// methods.Select(m => m.Name).Dump("Will Execute:");
var timer = new Stopwatch();
foreach (var method in methods)
{
"Method: {0}".TitleFormat(method.Name);
timer.Reset();
timer.Start();
try {
method.Invoke(this, null);
timer.Stop();
} catch(Exception ex) {
timer.Stop();
ex.Dump();
}
var elapsed = timer.Elapsed.TotalMilliseconds;
">>> Ran in {0} ms ({1})".DumpFormat(elapsed, method.Name);
}
}//--- fn Run
protected UnitTestHarness(bool autorun = true) {
if(autorun) this.Run();
}
}
public class GenericEqualityComparer<T> : IEqualityComparer<T>
{
public Func<T, T, bool> Compare { get; set; }
public Func<T, int> HashCode { get; set; }
public GenericEqualityComparer(Func<T, int> hashcode, Func<T, T, bool> compare = null)
{
HashCode = hashcode;
if(compare == null) Compare = (a, b) => HashCode(a) == HashCode(b);
else Compare = compare ;
}
#region Implementation of IEqualityComparer<in T>
/// <summary>
/// Determines whether the specified objects are equal.
/// </summary>
/// <returns>
/// true if the specified objects are equal; otherwise, false.
/// </returns>
/// <param name="x">The first object of type <paramref name="T"/> to compare.</param><param name="y">The second object of type <paramref name="T"/> to compare.</param>
public bool Equals(T x, T y)
{
return Compare(x, y);
}
/// <summary>
/// Returns a hash code for the specified object.
/// </summary>
/// <returns>
/// A hash code for the specified object.
/// </returns>
/// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned.</param><exception cref="T:System.ArgumentNullException">The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.</exception>
public int GetHashCode(T obj)
{
return HashCode(obj);
}
#endregion
}
#endregion ------------------ TESTING ----------------
/** just the perf wrapper for benchmarking */
/// <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&lt;T&gt; { { "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>> {}
/// <summary>The actual extensions</summary>
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, repetitions).Dump(string.IsNullOrEmpty(reportTitle) ? null : string.Format("{0} ({1}x)", reportTitle, repetitions));
return sw.Elapsed;
}
private static string perfElapsed(TimeSpan ts, int repetitions) {
return string.Format("{0} ticks elapsed ({1} ms) [in {2:0.#}K reps, {3} ms per]", ts.Ticks, ts.TotalMilliseconds, repetitions/1000D, ts.TotalMilliseconds / (double) repetitions);
}
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, repetitions)))
// .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))));
}
}
Newtonsoft.Json.dll (only for the json dump extensions)
void Main()
{
// Write code to test your extensions here. Press F5 to compile and run.
"Test Default Underline".Title();
"Short".Title();
"Really Really Really Long Line that should probabbly never happen".Title();
"Alternate underline 1".Title('=');
"Alternate underline 2".Title('*');
"Alternate underline 3".Title('_');
MyExtensions.DumpTitle("stuff", '8');
"Testing Format Dump: {0} is the {1} in the {2}. {0}.".DumpFormat("A", "B", "C");
"Testing Format Dump: {0} is the {1} in the {2}. {0}.".TitleFormat("A", "B", "C");
var perfResult = "Perf test 1".Perf((i) => { i++; }, 100);
perfResult.Dump("Performance result");
#region ----------- testing VS usage styles ----------------
var s = "<?xml blah ?>things to live by";
var f = "?>";
/* bah!
var tests = new {
normal = n => s.IndexOf(f),
ordinal = n => s.IndexOf(f, StringComparison.Ordinal),
ordinalIgnore = n => s.IndexOf(f, StringComparison.OrdinalIgnoreCase)
};
*/
// this syntax sucks, needed `AnonymousTypeToDictionary` and then wasn't all that concise
// and i only got it to work once?
/*
var tasks = new {
normal = (Action<int>)( n => s.IndexOf(f) ),
ordinal = (Action<int>)( n => s.IndexOf(f, StringComparison.Ordinal) ),
ordinalIgnore = (Action<int>)( n => s.IndexOf(f, StringComparison.OrdinalIgnoreCase) )
}; tasks.Vs("comparisons");
*/
new Dictionary<string, Action<int>> {
{ "normal", n => s.IndexOf(f) },
{ "ordinal", n => s.IndexOf(f, StringComparison.Ordinal) },
{ "ordinalIgnore", n => s.IndexOf(f, StringComparison.OrdinalIgnoreCase) }
}.Vs("comparisons");
// equivalent to `new Dictionary<string, Action<int>> ...`
new Perf {
{ "normal", n => s.IndexOf(f) },
{ "ordinal", n => s.IndexOf(f, StringComparison.Ordinal) },
{ "ordinalIgnore", n => s.IndexOf(f, StringComparison.OrdinalIgnoreCase) }
}.Vs();
"comparisons".Vs(
n => s.IndexOf(f)
, n => s.IndexOf(f, StringComparison.Ordinal)
, n => s.IndexOf(f, StringComparison.OrdinalIgnoreCase)
);
"comparisons".Vs(new[] { "normal", "ordinal", "ordinalIgnore" }
, n => s.IndexOf(f)
, n => s.IndexOf(f, StringComparison.Ordinal)
, n => s.IndexOf(f, StringComparison.OrdinalIgnoreCase)
);
"comparisons".Vs(new[] { "normal" }, n => s.IndexOf(f), n => s.IndexOf(f, StringComparison.Ordinal), n => s.IndexOf(f, StringComparison.OrdinalIgnoreCase));
new Perf<DateTime> {
{ ".Now", n => DateTime.Now },
{ ".UtcNow", n => DateTime.UtcNow }
}.Vs("Datetime Fn");
#endregion ----------- testing VS usage styles ----------------
new Tests(); // run the 'unit tests'
}//--- fn Main
/// <summary>Just showing how to declare and use the unit test harness</summary>
class Tests : UnitTestHarness {
public Tests() : base(true) {}
public void Test_ModEquals() {
Assert.AreEqual( 2345234 % 2, 0 );
}
public void Test_ThisOneFails() {
Assert.AreEqual(2, 1);
}
public void YetAgainJustForPerformance() {
var l = Enumerable.Range(1, 1000).Select(i => i*23.523M);
var sum = 0M;
for(int i = 0; i < 1000; i++) sum = l.Sum();
sum.Dump("Final Sum");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment