I needed a better way to benchmark code, because, well, rewriting the same benchmarking code every time I need it is just...well...unpleasant.

So, here's a class which does just that, it runs an `Action` over a specific number of rounds, and calculates certain stats on it.

The nice thing about this is that it **doesn't** store the run times as it calculates the stats. So you can literally supply any value for `rounds` and it *should* work. (Not tested for `rounds` values greater than 10,000,000.)

The latest version is [on GitHub](https://github.com/EBrown8534/Framework/blob/master/Evbpc.Framework/Utilities/BenchmarkResult.cs).

It's pretty simple. Two `static` methods on a simple class that run the benchmark.

The nice thing about this class is it includes a version for `Func<T>` as well, which will also **verify the output** of the function. This means you can benchmark and verify your code at the same time, to make sure that nothing weird happens.

    /// <summary>
    /// Represents the result of a benchmarking session.
    /// </summary>
    public class BenchmarkResult
    {
        /// <summary>
        /// The total number of rounds ran.
        /// </summary>
        public ulong RoundsRun { get; set; }

        /// <summary>
        /// The average time for all the rounds.
        /// </summary>
        public TimeSpan AverageTime { get; set; }

        /// <summary>
        /// The maximum time taken for a single round.
        /// </summary>
        public TimeSpan MaxTime { get; set; }

        /// <summary>
        /// The minimum time taken for a single round.
        /// </summary>
        public TimeSpan MinTime { get; set; }

        /// <summary>
        /// The variance (standard deviation) of all the rounds.
        /// </summary>
        public TimeSpan Variance { get; set; }

        /// <summary>
        /// The number of rounds that passed testing. (Always equivalent to <see cref="RoundsRun"/> for <see cref="Benchmark(ulong, Action)"/>.)
        /// </summary>
        public ulong RoundsPassed { get; set; }
        
        /// <summary>
        /// Runs a benchmark of a method.
        /// </summary>
        /// <param name="rounds">The number of rounds to run.</param>
        /// <param name="method">The code to run.</param>
        /// <returns>A <see cref="BenchmarkResult"/> representing the result of the session.</returns>
        public static BenchmarkResult Benchmark(ulong rounds, Action method)
        {
            var sw = new Stopwatch();

            double m2 = 0;
            double averageTicks = 0;
            double totalValues = 0;
            long maxTicks = 0;
            long minTicks = 0;

            for (ulong i = 0; i < rounds; i++)
            {
                sw.Start();
                method.Invoke();
                sw.Stop();

                if (totalValues == 0)
                {
                    maxTicks = sw.ElapsedTicks;
                    minTicks = sw.ElapsedTicks;
                }

                totalValues++;

                maxTicks = Math.Max(sw.ElapsedTicks, maxTicks);
                minTicks = Math.Min(sw.ElapsedTicks, minTicks);

                // We need to store `delta` here as the `averageTicks` will change on the next calculation, and we need this previous `delta` for the calculation after it.
                double delta = sw.ElapsedTicks - averageTicks;
                averageTicks = averageTicks + delta / totalValues;
                m2 += delta * (sw.ElapsedTicks - averageTicks);

                sw.Reset();
            }

            double variance = m2 / (totalValues - 1);

            return new BenchmarkResult
            {
                AverageTime = new TimeSpan(Convert.ToInt64(averageTicks)),
                MaxTime = new TimeSpan(maxTicks),
                MinTime = new TimeSpan(minTicks),
                RoundsPassed = rounds,
                RoundsRun = rounds,
                Variance = new TimeSpan(Convert.ToInt64(variance))
            };
        }

        /// <summary>
        /// Runs a benchmark of a function and returns the results of the session.
        /// </summary>
        /// <typeparam name="T">The type of the output of the function.</typeparam>
        /// <param name="rounds">The number of rounds to run.</param>
        /// <param name="method">The code to run.</param>
        /// <param name="expectedResult">The expected result of the function. This will be compared to the actual result and used for <see cref="BenchmarkResult.RoundsPassed"/>. This uses the default <code>object.Equals(object)</code> method.</param>
        /// <returns>A <see cref="BenchmarkResult"/> representing the result of the session.</returns>
        public static BenchmarkResult Benchmark<T>(ulong rounds, Func<T> method, T expectedResult)
        {
            var sw = new Stopwatch();

            double m2 = 0;
            double averageTicks = 0;
            double totalValues = 0;
            long maxTicks = 0;
            long minTicks = 0;
            ulong roundsPassed = 0;

            for (ulong i = 0; i < rounds; i++)
            {
                sw.Start();
                var result = method.Invoke();
                sw.Stop();

                if (expectedResult.Equals(result))
                {
                    roundsPassed++;
                }

                if (totalValues == 0)
                {
                    maxTicks = sw.ElapsedTicks;
                    minTicks = sw.ElapsedTicks;
                }

                totalValues++;

                maxTicks = Math.Max(sw.ElapsedTicks, maxTicks);
                minTicks = Math.Min(sw.ElapsedTicks, minTicks);

                // We need to store `delta` here as the `averageTicks` will change on the next calculation, and we need this previous `delta` for the calculation after it.
                double delta = sw.ElapsedTicks - averageTicks;
                averageTicks = averageTicks + delta / totalValues;
                m2 += delta * (sw.ElapsedTicks - averageTicks);

                sw.Reset();
            }

            double variance = m2 / (totalValues - 1);

            return new BenchmarkResult
            {
                AverageTime = new TimeSpan(Convert.ToInt64(averageTicks)),
                MaxTime = new TimeSpan(maxTicks),
                MinTime = new TimeSpan(minTicks),
                RoundsPassed = roundsPassed,
                RoundsRun = rounds,
                Variance = new TimeSpan(Convert.ToInt64(variance))
            };
        }
    }

It's *literally* a one-liner:

>     var result = BenchmarkResult.Benchmark(10000000, () => Thread.Sleep(0));
    result = BenchmarkResult.Benchmark(10000000, () => true, true);

That will benchmark the `Thread.Sleep(0)` method 10,000,000 times, and the `return true` method 10,000,000 times while verifying it always returns `true`.