public ThreadRunner(Func<bool> executionCondition, ThreadResult result, TimingSettings settings, BenchmarkState sharedState, Action test, ITimerFactory timerFactory) { this.ExecutionCondition = executionCondition; this.Result = result; this.Settings = settings; this.SharedState = sharedState; this.Test = test; Timer = timerFactory.Create(); thread = new Thread(Run); thread.IsBackground = true; }
/// <summary> /// Runs the benchmark. Does not throw exceptions in case the test function throws one - if /// <see cref="TimingSettings.CatchTestExceptions"/> is false, the execution thread will rethrow the exception. /// This method sets <see cref="BenchmarkResult.HasErrors"/> to true in case of an exception, so if you have /// set <see cref="TimingSettings.CatchTestExceptions"/> to true, check <see cref="BenchmarkResult.HasErrors"/> /// afterwards. /// </summary> /// <param name="test"> /// Callback function that executes the test once. /// </param> /// <param name="executionCondition"> /// Optional callback function that defines whether the test is executed another time. You can use this if you /// have a shared state between test runs, e.g. a fixed-size list of inputs and each of them must be only used /// once. /// </param> /// <param name="resultName"> /// Optional name you want to assign to the result, e.g. "Run with 10 concurrent threads". /// </param> /// <returns> /// Benchmark results. /// </returns> public virtual BenchmarkResult RunBenchmark(Action test, Func<bool> executionCondition = null, string resultName = null) { if(test == null) throw new ArgumentNullException("test"); EnsureCorrectSettings(); var timer = TimerFactory.Create(); timer.Start(); var result = new BenchmarkResult { BenchmarkName = this.Name, ExceptionMode = Settings.ExceptionMode, Mode = Settings.Mode, ResultName = resultName }; var concurrency = Settings.Concurrency; var sharedState = new BenchmarkState(TimerFactory); var threads = new ThreadRunner[concurrency]; result.ThreadResults = new ThreadResult[concurrency]; for(int i = 0; i < concurrency; ++i) { result.ThreadResults[i] = new ThreadResult { ExceptionMode = Settings.ExceptionMode }; threads[i] = new ThreadRunner(executionCondition, result.ThreadResults[i], Settings, sharedState, test, TimerFactory); } // Start the actual benchmark in background thread(s) sharedState.TimerFromBeginning.Start(); for(int i = 0; i < concurrency; ++i) threads[i].Start(); // Wait for all threads to finish for(int i = 0; i < concurrency; ++i) threads[i].Join(); // Final value of this timer is unused sharedState.TimerFromBeginning.Stop(); timer.Stop(); // Propagate exceptions if any foreach(var threadResult in result.ThreadResults) if(threadResult.FirstUncountedException != null) { result.FirstUncountedException = threadResult.FirstUncountedException; break; } if(Settings.ExceptionMode == ExceptionMode.COUNT) foreach(var threadResult in result.ThreadResults) if(threadResult.FirstCountedException != null) { result.FirstCountedException = threadResult.FirstCountedException; break; } var elapsed = timer.Elapsed; result.CompleteElapsedTime = elapsed; result.CompletedExecutions = sharedState.CompletedExecutions; return result; }