using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
public static void Main()
static Random r = new Random();
static string getRandomString(int size) {
var sb = new StringBuilder();
for(int i = 0; i < size; i++) sb.Append( (char)('a' + r.Next(0, 25)));
static ICollection<string> createTestList<T>(int setSize, int minWordLength = 5, int wordLengthVariance = 10, int previewSize = 0) where T : ICollection<string>, new() {
for(int i = 0; i < setSize; i++) set.Add(getRandomString(r.Next(minWordLength, minWordLength + wordLengthVariance)));
if(previewSize > 0) set.Take(previewSize).Dump(string.Format("Test size {0} sample {1}", setSize, previewSize));
static List<T> getSearchKeys<T>(ICollection<T> set) {
return _getSearchKeys(set).ToList();
static IEnumerable<T> _getSearchKeys<T>(ICollection<T> set) {
yield return set.First();
yield return set.ElementAt(set.Count / 10);
yield return set.ElementAt(set.Count / 2);
yield return set.ElementAt(set.Count / 10 * 9 - 1);
static void test(int setSize, int minWordLength = 5, int wordLengthVariance = 10, bool preview = false) {
var list = createTestList<List<string>>(setSize, minWordLength, wordLengthVariance);
var hashset = new HashSet<string>(list);
var keys = getSearchKeys(list);
{ "list, first", n => list.Contains(keys[0]) },
{ "hashset, first", n => hashset.Contains(keys[0]) },
{ "list, early", n => list.Contains(keys[1]) },
{ "hashset, early", n => hashset.Contains(keys[1]) },
{ "list, middle", n => list.Contains(keys[2]) },
{ "hashset, middle", n => hashset.Contains(keys[2]) },
{ "list, late", n => list.Contains(keys[3]) },
{ "hashset, late", n => hashset.Contains(keys[3]) },
{ "list, last", n => list.Contains(keys[4]) },
{ "hashset, last", n => hashset.Contains(keys[4]) },
}.Vs(string.Format("Comparing set size {0} min word size {1}-{2}", setSize, minWordLength, minWordLength+wordLengthVariance));
public static class ObjectExtensions {
public static void Dump<T>(this T o, string title = null) {
if(title != null) Console.WriteLine("====== {0} ======", title);
var e = o as IEnumerable;
if(o is String || e == null) Console.WriteLine(o.ToString());
else foreach(var i in e) Console.WriteLine(i);
public static TimeSpan[] Vs(this Dictionary<string, Action<int>> tasks, string reportTitle = null, int repetitions = 10000, bool noShow = false) {
var r = new Dictionary<string, TimeSpan>();
foreach (var task in tasks)
r.Add(task.Key, task.Key.Perf(task.Value, repetitions, true));
reportTitle = (reportTitle ?? "Vs");
KeyValuePair<string, TimeSpan> minPair;
var minIndex = indexOfMost(r, (a, b) => a.Value > b.Value, out minPair);
Compare = (kv.Value.TotalMilliseconds / min.TotalMilliseconds).ToString("0.00x"),
Elapsed = perfElapsed(kv.Value, repetitions),
(">> winner: " + tasks.Keys.ElementAt(minIndex)).Dump();
return r.Select(kv => kv.Value).ToArray();
public static TimeSpan Perf(this string reportTitle, Action<int> task, int repetitions = 10000, bool noShow = false) {
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < repetitions; i++) {
if( !noShow ) perfElapsed(sw.Elapsed, repetitions).Dump(string.IsNullOrEmpty(reportTitle) ? null : string.Format("{0} ({1}x)", reportTitle, repetitions));
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, out T min) {
public class Perf : Dictionary<string, Action<int>> {}
public class Perf<T> : Dictionary<string, Func<int, T>> {}