private void PrintRunSummary(ThreadedRunResult runResult)
        {
            var sb = new StringBuilder();

            sb.Append(runResult.TestName);
            if (runResult.Successful)
            {
                sb.AppendLine(" (Success) ");
                sb.Append(runResult.RequestsPerSecond);
                sb.Append(" RPS total (");
                var samplePoints = runResult.IterationCounters.Count;
                Debug.Assert(samplePoints > 0);
                sb.Append(samplePoints.ToString(CultureInfo.InvariantCulture));
                if (samplePoints > 1)
                {
                    sb.AppendLine(" samples)");
                    foreach (var i in new[] { 0.95, 0.99, 0.999 })
                    {
                        var percentile       = (i * 100).ToString(CultureInfo.InvariantCulture);
                        var resultPercentile = GetPercentile(runResult, i, c => ((ThreadedIterationCounter)c).RequestsPerSecond, false);
                        var resultName       = string.Format("{0} - {1}th percentile", runResult.TestName, percentile);

                        sb.Append(resultPercentile);
                        sb.Append(" RPS ");
                        sb.Append(percentile);
                        sb.AppendLine("th percentile");
                    }
                }
                else
                {
                    sb.AppendLine(" iteration)");
                }
            }
            else
            {
                sb.Append(" (Fail) ");
                sb.Append(runResult.ReportedException.Message);
            }
            sb.AppendLine();
            Console.WriteLine(sb.ToString());
        }
        protected ThreadedRunResult Run(ThreadedTestDefinition test)
        {
            //localize test settings
            var threadCount = 8;

            if (test.ThreadCount.HasValue)
            {
                threadCount = test.ThreadCount.Value;
            }
            var warmupDuration = 10000;

            if (test.WarmupDuration.HasValue)
            {
                warmupDuration = test.WarmupDuration.Value;
            }
            var testDuration = 60000;

            if (test.TestDuration.HasValue)
            {
                testDuration = test.TestDuration.Value;
            }
            var testName = test.TestName ?? test.GetType() + "#" + test.GetHashCode();

            var setup              = test.Setup;
            var run                = test.Run;
            var cleanup            = test.Cleanup;
            var threadStateFactory = test.ThreadStateFactory;

            //validate
            if (run == null)
            {
                throw new ArgumentNullException(string.Format("Verify that test {0} has a run action.", testName));
            }

            //setup
            try
            {
                if (setup != null)
                {
                    setup();
                }
            }
            catch (Exception e)
            {
                return(new ThreadedRunResult(testName, e));
            }

            var iterationSnaps = new List <Tuple <long, long> >();

            var iterationCounter = 0L;
            var testStopSignal   = false;

            Action decoratedRunAction = () =>
            {
                var state = threadStateFactory();
                do
                {
                    run(state);
                    Interlocked.Increment(ref iterationCounter);
                }while (!testStopSignal);
            };

            Action rpsCounterAction = () =>
            {
                do
                {
                    var current = Interlocked.Read(ref iterationCounter);
                    iterationSnaps.Add(new Tuple <long, long>(current, GC.GetTotalMemory(false)));
                    Thread.Sleep(1000);
                }while (!testStopSignal);
            };

            var workers = new List <Thread>();

            //add worker tasks
            for (var i = 0; i < threadCount; ++i)
            {
                workers.Add(new Thread(new ThreadStart(decoratedRunAction)));
            }

            long totalExecutionTime;

            //run
            try
            {
                var runStopWatch = new Stopwatch();
                runStopWatch.Start();
                foreach (var worker in workers)
                {
                    worker.Start();
                }
                // warmup
                runStopWatch.Restart();
                do
                {
                    Thread.Sleep(1000);
                }while (runStopWatch.ElapsedMilliseconds < warmupDuration);
                //add rps counter thread
                Thread counter = null;
                workers.Add(counter = new Thread(new ThreadStart(rpsCounterAction)));
                counter.Start();
                //actual run
                runStopWatch.Restart();
                do
                {
                    Thread.Sleep(1000);
                }while (runStopWatch.ElapsedMilliseconds < testDuration);
                testStopSignal = true;
                runStopWatch.Stop();
                totalExecutionTime = runStopWatch.ElapsedMilliseconds / 1000;
            }
            catch (Exception e)
            {
                return(new ThreadedRunResult(testName, e));
            }

            //summarize iteration counters
            var prevCummulativeValue = 0L;
            var iterationCounters    = new List <ThreadedIterationCounter>();

            foreach (var snap in iterationSnaps)
            {
                if (snap.Item1 == 0)
                {
                    continue;
                }

                var iterationRps = snap.Item1 - prevCummulativeValue;
                iterationCounters.Add(new ThreadedIterationCounter
                {
                    RequestsPerSecond = iterationRps,
                    WorkingSet        = snap.Item2
                });
                prevCummulativeValue += iterationRps;
            }

            var result = new ThreadedRunResult(testName, iterationCounter / totalExecutionTime, GC.GetTotalMemory(false), iterationCounters);

            //cleanup
            try
            {
                if (cleanup != null)
                {
                    cleanup();
                }
            }
            catch (Exception e)
            {
                result.ReportedException = e;
            }

            //report
            return(result);
        }