diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index daf0699..6c35219 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -7,7 +7,7 @@ "type": "object", "properties": { "BuildNo": { - "type": "string", + "type": "integer", "description": "The Buildnumber provided by the CI" }, "Configuration": { @@ -74,6 +74,10 @@ "type": "string", "description": "Root directory during build execution" }, + "RunTests": { + "type": "boolean", + "description": "Run UnitTests on build" + }, "Skip": { "type": "array", "description": "List of targets to be skipped. Empty list skips all dependencies", diff --git a/CHANGELOG.md b/CHANGELOG.md index 31e05d4..92599bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,14 +6,23 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## vNext ### Added -- +- ThreadBehaviour to define how a thread is created +- Allow benchmarks to be run on the MainThread ### Changed -- +- Added IDisposable to IThreadSessionHandler ### Fixed - +## v2.1.0 +### Added +- ThreadBehaviour to define how a thread is created +- Allow benchmarks to be run on the MainThread + +### Changed +- Added IDisposable to IThreadSessionHandler + ## v2.0.2 ### Added - Added Benchmarks and samples oh how to use the BenchmarkRunner diff --git a/appveyor.yml b/appveyor.yml index 016e9b3..25c8471 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,7 @@ # http://www.appveyor.com/docs/appveyor-yml environment: - base_version: 2.0.2 + base_version: 2.0.3 # version format version: $(base_version).{build} diff --git a/build/Build.cs b/build/Build.cs index 8ef055d..7e8c8c2 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -38,14 +38,17 @@ class Build : NukeBuild [GitRepository] readonly GitRepository GitRepository; [Parameter("Version to be injected in the Build")] - public string Version { get; set; } = $"2.0.2"; + public string Version { get; set; } = $"2.1.0"; [Parameter("The Buildnumber provided by the CI")] - public string BuildNo = "1"; + public int BuildNo = 6; [Parameter("Is RC Version")] public bool IsRc = false; + [Parameter("Run UnitTests on build")] + public bool RunTests = true; + AbsolutePath SourceDirectory => RootDirectory / "src"; AbsolutePath TestsDirectory => RootDirectory / "src" / "tests"; @@ -89,6 +92,11 @@ class Build : NukeBuild .DependsOn(Compile) .Executes(() => { + if (!RunTests) + { + return; + } + DotNetTest(s => s .SetProjectFile(Solution) .SetConfiguration(Configuration) @@ -131,6 +139,6 @@ class Build : NukeBuild }); string PackageVersion - => IsRc ? int.Parse(BuildNo) < 10 ? $"{Version}-RC0{BuildNo}" : $"{Version}-RC{BuildNo}" : Version; + => IsRc ? BuildNo < 10 ? $"{Version}-RC0{BuildNo}" : $"{Version}-RC{BuildNo}" : Version; } diff --git a/build/_build.csproj b/build/_build.csproj index fdb9396..9e5a2e8 100644 --- a/build/_build.csproj +++ b/build/_build.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 CS0649;CS0169 .. diff --git a/src/MeasureMap/BenchmarkRunnerExtensions.cs b/src/MeasureMap/BenchmarkRunnerExtensions.cs index eb0a84a..a20964e 100644 --- a/src/MeasureMap/BenchmarkRunnerExtensions.cs +++ b/src/MeasureMap/BenchmarkRunnerExtensions.cs @@ -76,5 +76,18 @@ public static BenchmarkRunner SetMinLogLevel(this BenchmarkRunner runner, LogLev runner.Settings.Logger.MinLogLevel = level; return runner; } + + /// + /// Set the to the settings of the .

+ /// Using overwrites the ThreadingHandler defined in each ProfilerSession that is run in the Benchmark. + ///
+ /// + /// + /// + public static BenchmarkRunner SetThreadBehaviour(this BenchmarkRunner runner, ThreadBehaviour behaviour) + { + runner.Settings.ThreadBehaviour = behaviour; + return runner; + } } } diff --git a/src/MeasureMap/ExecutionContext.cs b/src/MeasureMap/ExecutionContext.cs index 66d858c..63349a9 100644 --- a/src/MeasureMap/ExecutionContext.cs +++ b/src/MeasureMap/ExecutionContext.cs @@ -22,7 +22,6 @@ public ExecutionContext() /// public ExecutionContext(IProfilerSettings settings) { - Threads = new ThreadList(); Settings = settings; } @@ -30,12 +29,7 @@ public ExecutionContext(IProfilerSettings settings) /// The data store for the context /// public IDictionary SessionData { get; } = new Dictionary(); - - /// - /// Gets a list containing all threads that are associated with this run - /// - public IThreadList Threads { get; set; } - + /// /// Gets the settings associated with the session /// @@ -140,10 +134,7 @@ public static IExecutionContext Clear(this IExecutionContext context) /// public static IExecutionContext Clone(this IExecutionContext context) { - var child = new ExecutionContext(context.Settings) - { - Threads = context.Threads - }; + var child = new ExecutionContext(context.Settings); foreach(var item in context.SessionData) { diff --git a/src/MeasureMap/IExecutionContext.cs b/src/MeasureMap/IExecutionContext.cs index 895cd20..d2c612c 100644 --- a/src/MeasureMap/IExecutionContext.cs +++ b/src/MeasureMap/IExecutionContext.cs @@ -1,5 +1,4 @@ using MeasureMap.Diagnostics; -using MeasureMap.Threading; using System.Collections.Generic; namespace MeasureMap @@ -13,12 +12,7 @@ public interface IExecutionContext /// The data store for the context /// IDictionary SessionData { get; } - - /// - /// Gets a list containing all threads that are associated with this run - /// - IThreadList Threads { get; } - + /// /// Gets the settings associated with the session /// diff --git a/src/MeasureMap/ProfilerSession.cs b/src/MeasureMap/ProfilerSession.cs index ba123a6..7e8464c 100644 --- a/src/MeasureMap/ProfilerSession.cs +++ b/src/MeasureMap/ProfilerSession.cs @@ -73,11 +73,10 @@ public static ProfilerSession StartSession() /// Sets the amount of threads that the profiling sessions should run in. /// All iterations are run on every thread. /// - /// The amount of threads that the task is run on /// The current profiling session - public ProfilerSession SetThreads(int thredCount) + public ProfilerSession SetExecutionHandler(IThreadSessionHandler handler) { - _executor = new MultyThreadSessionHandler(thredCount); + _executor = handler; return this; } @@ -172,10 +171,7 @@ public void Dispose() /// protected virtual void Dispose(bool disposing) { - if (_executor is MultyThreadSessionHandler handler) - { - handler.DisposeThreads(); - } + _executor.Dispose(); } } } diff --git a/src/MeasureMap/ProfilerSessionExtensions.cs b/src/MeasureMap/ProfilerSessionExtensions.cs index d564a49..01cd946 100644 --- a/src/MeasureMap/ProfilerSessionExtensions.cs +++ b/src/MeasureMap/ProfilerSessionExtensions.cs @@ -78,6 +78,38 @@ public static ProfilerSession Task(this ProfilerSession session, Func + /// Sets the amount of threads that the profiling sessions should run in. + /// All iterations are run on every thread. + /// + /// + /// The amount of threads that the task is run on + /// The current profiling session + public static ProfilerSession SetThreads(this ProfilerSession session, int thredCount) + { + session.SetExecutionHandler(new MultyThreadSessionHandler(thredCount)); + + return session; + } + + /// + /// Set the to the settings of the + /// + /// + /// + /// + public static ProfilerSession SetThreadBehaviour(this ProfilerSession session, ThreadBehaviour behaviour) + { + session.Settings.ThreadBehaviour = behaviour; + + if (behaviour == ThreadBehaviour.RunOnMainThread) + { + session.SetExecutionHandler(new MainThreadSessionHandler()); + } + + return session; + } + /// /// Sets the amount of iterations that the profileing session should run the task /// @@ -140,6 +172,13 @@ public static ProfilerSession RunWarmup(this ProfilerSession session, bool run) public static ProfilerSession SetSettings(this ProfilerSession session, ProfilerSettings settings) { settings.MergeChangesTo(session.Settings); + + if(session.Settings.ThreadBehaviour != ThreadBehaviour.Thread) + { + // ensure the ThreadBehaviour is set when using the new settings + // this is only set when using the default ThreadBehaviour in a ProfilerSession + session.SetThreadBehaviour(session.Settings.ThreadBehaviour); + } return session; } diff --git a/src/MeasureMap/ProfilerSettings.cs b/src/MeasureMap/ProfilerSettings.cs index 94aff8f..5f85fa1 100644 --- a/src/MeasureMap/ProfilerSettings.cs +++ b/src/MeasureMap/ProfilerSettings.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using MeasureMap.Diagnostics; using MeasureMap.Runners; +using MeasureMap.Threading; namespace MeasureMap { @@ -15,6 +16,7 @@ public class ProfilerSettings : IProfilerSettings private int _iterations = 1; private bool _runWarmup = true; private TimeSpan _duration = TimeSpan.Zero; + private ThreadBehaviour _threadBehaviour = ThreadBehaviour.Thread; /// /// @@ -70,6 +72,19 @@ public bool RunWarmup } } + /// + /// Defines the way threads are created + /// + public ThreadBehaviour ThreadBehaviour + { + get => _threadBehaviour; + set + { + _threadBehaviour = value; + AddChange("threadBehaviour", s => s.ThreadBehaviour, (s, v) => s.ThreadBehaviour = v); + } + } + /// /// Gets the that is used to run the tasks /// @@ -82,13 +97,15 @@ public bool RunWarmup private void AddChange(string property, Func func, Action action) { - _changes[property] = (origSet, newSet) => action(origSet, func(newSet)); + _changes[property] = (toSet, fromSet) => action(toSet, func(fromSet)); } internal void MergeChangesTo(ProfilerSettings settings) { foreach (var action in _changes.Values) { + // settings = to + // this = from action(settings, this); } } diff --git a/src/MeasureMap/ProfilerSettingsExtensions.cs b/src/MeasureMap/ProfilerSettingsExtensions.cs new file mode 100644 index 0000000..aa4f334 --- /dev/null +++ b/src/MeasureMap/ProfilerSettingsExtensions.cs @@ -0,0 +1,26 @@ +using MeasureMap.Threading; +using System; + +namespace MeasureMap +{ + /// + /// Extensions for + /// + public static class ProfilerSettingsExtensions + { + /// + /// Get the ThreadFactory based on the setting in + /// + /// + /// + public static Func, IWorkerThread> GetThreadFactory(this ProfilerSettings settings) + { + return settings.ThreadBehaviour switch + { + ThreadBehaviour.Thread => WorkerThread.Factory, + ThreadBehaviour.Task => WorkerTask.Factory, + _ => WorkerThread.Factory + }; + } + } +} diff --git a/src/MeasureMap/SessionHandlers/BasicSessionHandler.cs b/src/MeasureMap/SessionHandlers/BasicSessionHandler.cs index 546d47e..8036f57 100644 --- a/src/MeasureMap/SessionHandlers/BasicSessionHandler.cs +++ b/src/MeasureMap/SessionHandlers/BasicSessionHandler.cs @@ -1,12 +1,11 @@ - +using System; using MeasureMap.Threading; -using System; using MeasureMap.Diagnostics; namespace MeasureMap { /// - /// A single threaded task session handler + /// A single threaded task session handler. Runs all tasks in a separate thread. /// public class BasicSessionHandler : SessionHandler, IThreadSessionHandler { @@ -19,23 +18,39 @@ public class BasicSessionHandler : SessionHandler, IThreadSessionHandler public override IProfilerResult Execute(ITask task, ProfilerSettings settings) { var runnerThreads = new WorkerThreadList(); - var threads = new ThreadList(); var thread = runnerThreads.StartNew(0, () => { - var worker = new Worker(threads); + var worker = new Worker(); return worker.Run(task, settings); - }); + }, settings.GetThreadFactory()); settings.Logger.Write($"Start thread {thread.Id}", LogLevel.Debug, nameof(BasicSessionHandler)); runnerThreads.WaitAll(); - threads.WaitAll(); return new ProfilerResult { thread.Result }; } + + /// + /// Dispose + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Dispose + /// + /// + protected virtual void Dispose(bool disposing) + { + // nothing to dispose + } } } diff --git a/src/MeasureMap/SessionHandlers/IThreadSessionHandler.cs b/src/MeasureMap/SessionHandlers/IThreadSessionHandler.cs index a0390a1..0cdb50d 100644 --- a/src/MeasureMap/SessionHandlers/IThreadSessionHandler.cs +++ b/src/MeasureMap/SessionHandlers/IThreadSessionHandler.cs @@ -1,10 +1,11 @@ - +using System; + namespace MeasureMap { /// /// defines a mechanism to execute the task /// - public interface IThreadSessionHandler : ISessionHandler + public interface IThreadSessionHandler : ISessionHandler, IDisposable { } } diff --git a/src/MeasureMap/SessionHandlers/MainThreadSessionHandler.cs b/src/MeasureMap/SessionHandlers/MainThreadSessionHandler.cs new file mode 100644 index 0000000..cc12fc2 --- /dev/null +++ b/src/MeasureMap/SessionHandlers/MainThreadSessionHandler.cs @@ -0,0 +1,48 @@ +using System; +using MeasureMap.Diagnostics; + +namespace MeasureMap +{ + /// + /// Runs all tasks in the main thread. This can only be used when running on one thread. Multithreaded handling cannot be run on the main thread + /// + public class MainThreadSessionHandler : SessionHandler, IThreadSessionHandler + { + /// + /// Executes the task + /// + /// The task to run + /// The settings for the profiler + /// The resulting collection of the executions + public override IProfilerResult Execute(ITask task, ProfilerSettings settings) + { + settings.Logger.Write($"Start on mainthread", LogLevel.Debug, nameof(BasicSessionHandler)); + + var worker = new Worker(); + var result = worker.Run(task, settings); + + return new ProfilerResult + { + result + }; + } + + /// + /// Dispose + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Dispose + /// + /// + protected virtual void Dispose(bool disposing) + { + // nothing to dispose + } + } +} diff --git a/src/MeasureMap/SessionHandlers/MultyThreadSessionHandler.cs b/src/MeasureMap/SessionHandlers/MultyThreadSessionHandler.cs index 1105fc4..13d0cd8 100644 --- a/src/MeasureMap/SessionHandlers/MultyThreadSessionHandler.cs +++ b/src/MeasureMap/SessionHandlers/MultyThreadSessionHandler.cs @@ -1,7 +1,6 @@ using System; using System.Diagnostics; using System.Linq; -using System.Threading; using MeasureMap.Diagnostics; using MeasureMap.Threading; @@ -10,7 +9,7 @@ namespace MeasureMap /// /// A multy threaded task session handler /// - public class MultyThreadSessionHandler : SessionHandler, IThreadSessionHandler, IDisposable + public class MultyThreadSessionHandler : SessionHandler, IThreadSessionHandler { private readonly int _threadCount; private readonly WorkerThreadList _threads; @@ -40,19 +39,18 @@ public MultyThreadSessionHandler(int threadCount) public override IProfilerResult Execute(ITask task, ProfilerSettings settings) { var sw = Stopwatch.StartNew(); - var threads = new ThreadList(); _logger = settings.Logger; lock (_threads) { for (int i = 0; i < _threadCount; i++) - { - var thread = _threads.StartNew(i, () => - { - var worker = new Worker(threads); - var p = worker.Run(task, settings); - return p; - }); + { + var thread = _threads.StartNew(i, () => + { + var worker = new Worker(); + var p = worker.Run(task, settings); + return p; + }, settings.GetThreadFactory()); settings.Logger.Write($"Start thread {thread.Id}", LogLevel.Debug, nameof(MultyThreadSessionHandler)); } @@ -67,8 +65,6 @@ public override IProfilerResult Execute(ITask task, ProfilerSettings settings) var results = _threads.Select(s => s.Result); - threads.WaitAll(); - var collectîon = new ProfilerResult(); collectîon.ResultValues.Add(ResultValueType.Threads, _threadCount); diff --git a/src/MeasureMap/Threading/IThreadList.cs b/src/MeasureMap/Threading/IThreadList.cs deleted file mode 100644 index d7e5816..0000000 --- a/src/MeasureMap/Threading/IThreadList.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; - -namespace MeasureMap.Threading -{ - /// - /// Defines a list of threads - /// - public interface IThreadList - { - /// - /// Start the action in a new thread - /// - /// - void StartNew(Action execution); - - /// - /// Wait for all threads to end - /// - void WaitAll(); - } -} diff --git a/src/MeasureMap/Threading/IWorkerThread.cs b/src/MeasureMap/Threading/IWorkerThread.cs new file mode 100644 index 0000000..214339a --- /dev/null +++ b/src/MeasureMap/Threading/IWorkerThread.cs @@ -0,0 +1,39 @@ +using System; +using System.Threading; + +namespace MeasureMap.Threading +{ + /// + /// Thread to run the Worker in + /// + public interface IWorkerThread : IDisposable + { + /// + /// Get the final result of the thread + /// + Result Result { get; } + + /// + /// Get the Id of the thread + /// + int Id { get; } + + /// + /// Gets if the thread is still working + /// + bool IsAlive { get; } + + /// + /// Gets a that is used to wait for the event to be set. + /// + /// + /// should only be used if it's needed for integration with code bases that rely on having a WaitHandle. + /// + public WaitHandle WaitHandle { get; } + + /// + /// Start the thread + /// + void Start(); + } +} \ No newline at end of file diff --git a/src/MeasureMap/Threading/ThreadBehaviour.cs b/src/MeasureMap/Threading/ThreadBehaviour.cs new file mode 100644 index 0000000..5fe1d4a --- /dev/null +++ b/src/MeasureMap/Threading/ThreadBehaviour.cs @@ -0,0 +1,24 @@ + +namespace MeasureMap +{ + /// + /// Define the behaviour of threading to use. This can influence the way the benchmarks run with dependencies outside the benchmark. + /// + public enum ThreadBehaviour + { + /// + /// Use System.Threading.Thread to run benchmarks + /// + Thread, + + /// + /// Use System.Threading.Tasks.Task to run benchmarks + /// + Task, + + /// + /// All tasks will be run on the main thread. This option is ignored when using multiple threads. + /// + RunOnMainThread + } +} diff --git a/src/MeasureMap/Threading/ThreadList.cs b/src/MeasureMap/Threading/ThreadList.cs deleted file mode 100644 index 9c0cadb..0000000 --- a/src/MeasureMap/Threading/ThreadList.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; -using Thread = System.Threading.Tasks.Task; - -namespace MeasureMap.Threading -{ - /// - /// List of threads - /// - public class ThreadList : IThreadList - { - private readonly List _threads = new List(); - - /// - /// Start the action in a new thread - /// - /// - public void StartNew(Action execution) - { - var thread = Thread.Factory.StartNew(execution); - Add(thread); - } - - /// - /// Add a task to the threadlist - /// - /// - public void Add(Thread task) - { - lock (_threads) - { - _threads.Add(task); - - task.ContinueWith(t => - { - // make sure the thread is completed - t.ConfigureAwait(false).GetAwaiter().GetResult(); - Remove(t); - }); - } - } - - /// - /// Remove a task from the threadlist - /// - /// - public void Remove(Thread task) - { - lock (_threads) - { - if (_threads.Contains(task)) - { - _threads.Remove(task); - } - } - } - - /// - /// Wait for all threads to end - /// - public void WaitAll() - { - while (Count() > 0) - { - Debug.WriteLine($"[{DateTime.Now:o}] [MeasureMap] [Debug] [{nameof(ThreadList)}] Task count before waitall: {Count()}"); - - Thread.WaitAll(GetTaskArray(), -1, CancellationToken.None); - - Debug.WriteLine($"[{DateTime.Now:o}] [MeasureMap] [Debug] [{nameof(ThreadList)}] Task count after waitall: {Count()}"); - } - } - - private Thread[] GetTaskArray() - { - lock (_threads) - { - return _threads.ToArray(); - } - } - - /// - /// Gets the count of threads in the list - /// - /// - private int Count() - { - lock (_threads) - { - return _threads.Count; - } - } - } -} diff --git a/src/MeasureMap/Threading/WorkerTask.cs b/src/MeasureMap/Threading/WorkerTask.cs new file mode 100644 index 0000000..a5dd299 --- /dev/null +++ b/src/MeasureMap/Threading/WorkerTask.cs @@ -0,0 +1,110 @@ +using System; +using System.Reflection; +using System.Threading; + +namespace MeasureMap.Threading +{ + /// + /// Worker that uses to run Benchmarks + /// + public class WorkerTask : IWorkerThread + { + private System.Threading.Tasks.Task _task; + private readonly ManualResetEventSlim _event; + private bool _disposed; + private readonly Func _action; + private readonly int _index; + + private readonly CancellationTokenSource _cancellationToken; + + /// + /// Gets the factory for creating a new WorkerTask + /// + public static Func, IWorkerThread> Factory => (i, e) => new WorkerTask(i, e); + + /// + /// Create a new Thread + /// + /// + /// + public WorkerTask(int index, Func action) + { + _event = new ManualResetEventSlim(); + _cancellationToken = new CancellationTokenSource(); + _index = index; + _action = action; + + _event.Reset(); + } + + /// + /// Get the final result of the thread + /// + public Result Result { get; private set; } + + /// + /// Get the Id of the thread + /// + public int Id => _task?.Id ?? 0; + + /// + /// Gets if the thread is still working + /// + public bool IsAlive => _task is { Status: <= System.Threading.Tasks.TaskStatus.Running }; + + /// + /// Gets a that is used to wait for the event to be set. + /// + /// + /// should only be used if it's needed for integration with code bases that rely on having a WaitHandle. + /// + public WaitHandle WaitHandle => _event.WaitHandle; + + /// + /// Start the thread + /// + public void Start() + { + _task = System.Threading.Tasks.Task.Factory.StartNew(() => + { + try + { + Result = _action(); + } + catch + { + // do nothing. just move to finally + } + finally + { + // reset the event to be signaled + _event.Set(); + } + }, _cancellationToken.Token, System.Threading.Tasks.TaskCreationOptions.None, System.Threading.Tasks.TaskScheduler.Current); + } + + /// + /// Dispose the thread + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Dispose the class + /// + /// + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + _disposed = true; + _cancellationToken.Cancel(); + } + } +} diff --git a/src/MeasureMap/Threading/WorkerThread.cs b/src/MeasureMap/Threading/WorkerThread.cs index 17fb9ca..ceedf4f 100644 --- a/src/MeasureMap/Threading/WorkerThread.cs +++ b/src/MeasureMap/Threading/WorkerThread.cs @@ -4,14 +4,19 @@ namespace MeasureMap.Threading { /// - /// + /// Worker that uses to run Benchmarks /// - public class WorkerThread : IDisposable + public class WorkerThread : IWorkerThread { private readonly Thread _thread; private readonly ManualResetEventSlim _event; private bool _disposed; + /// + /// Gets the factory for creating a new WorkerThread + /// + public static Func, IWorkerThread> Factory => (i, e) => new WorkerThread(i, e); + /// /// Create a new Thread /// diff --git a/src/MeasureMap/Threading/WorkerThreadList.cs b/src/MeasureMap/Threading/WorkerThreadList.cs index 5479d9b..39287ca 100644 --- a/src/MeasureMap/Threading/WorkerThreadList.cs +++ b/src/MeasureMap/Threading/WorkerThreadList.cs @@ -9,24 +9,20 @@ namespace MeasureMap.Threading /// /// Represents a list of WorkerThreads /// - public class WorkerThreadList : IEnumerable + public class WorkerThreadList : IEnumerable { - private readonly List _threads = new List(); + private readonly List _threads = new List(); /// /// Start a new thread and add it to the list /// /// /// + /// /// - public WorkerThread StartNew(int index, Func execution) + public IWorkerThread StartNew(int index, Func execution, Func, IWorkerThread> threadFactory) { - var thread = new WorkerThread(index, () => - { - var result = execution.Invoke(); - - return result; - }); + var thread = threadFactory.Invoke(index, execution); Add(thread); @@ -39,7 +35,7 @@ public WorkerThread StartNew(int index, Func execution) /// Add a task to the threadlist /// /// - public void Add(WorkerThread thread) + public void Add(IWorkerThread thread) { lock (_threads) { @@ -51,7 +47,7 @@ public void Add(WorkerThread thread) /// Remove a task from the threadlist /// /// - public void Remove(WorkerThread thread) + public void Remove(IWorkerThread thread) { lock (_threads) { @@ -84,7 +80,7 @@ public void WaitAll() } } - private WorkerThread[] GetTaskArray() + private IWorkerThread[] GetTaskArray() { lock (_threads) { @@ -97,7 +93,7 @@ private WorkerThread[] GetTaskArray() /// /// /// - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { return _threads.GetEnumerator(); } diff --git a/src/MeasureMap/Worker.cs b/src/MeasureMap/Worker.cs index 7ca0edb..b338c1d 100644 --- a/src/MeasureMap/Worker.cs +++ b/src/MeasureMap/Worker.cs @@ -8,22 +8,11 @@ namespace MeasureMap /// public class Worker { - private readonly IThreadList _threads; - - /// - /// Creates a new instance of the worker - /// - public Worker() : this(new ThreadList()) - { - } - /// /// Creates a new instance of the worker /// - /// - public Worker(IThreadList threads) + public Worker() { - _threads = threads ?? throw new ArgumentNullException(nameof(threads)); } /// @@ -39,10 +28,7 @@ public Result Run(ITask task, ProfilerSettings settings) ForceGarbageCollector(); result.InitialSize = GC.GetTotalMemory(true); - var context = new ExecutionContext(settings) - { - Threads = _threads - }; + var context = new ExecutionContext(settings); var runner = settings.Runner; runner.Run(settings, context, c => diff --git a/src/Tests/MeasureMap.Benchmark/Program.cs b/src/Tests/MeasureMap.Benchmark/Program.cs index 6dec965..a7f9fcc 100644 --- a/src/Tests/MeasureMap.Benchmark/Program.cs +++ b/src/Tests/MeasureMap.Benchmark/Program.cs @@ -1,6 +1,7 @@ using MeasureMap.Benchmark; -var bm = "BenchmarkUsingMultipleThreads"; +var bm = "ThreadBehaviourBenchmarks"; +//var bm = "BenchmarkUsingMultipleThreads"; //var bm = "BenchmarkSample"; //var bm = "WorkerThreadListBenchmarks"; //var bm = "ThroughputBenchmarks"; @@ -23,6 +24,9 @@ case nameof(ThroughputBenchmarks): new ThroughputBenchmarks().RunBenchmarks(); break; + case nameof(ThreadBehaviourBenchmarks): + new ThreadBehaviourBenchmarks().RunBenchmarks(); + break; } diff --git a/src/Tests/MeasureMap.Benchmark/ThreadBehaviourBenchmarks.cs b/src/Tests/MeasureMap.Benchmark/ThreadBehaviourBenchmarks.cs new file mode 100644 index 0000000..6e3e7f3 --- /dev/null +++ b/src/Tests/MeasureMap.Benchmark/ThreadBehaviourBenchmarks.cs @@ -0,0 +1,35 @@ + +using MeasureMap.Tracers.Metrics; + +namespace MeasureMap.Benchmark +{ + public class ThreadBehaviourBenchmarks + { + public void RunBenchmarks() + { + // check the throughput + // from 0 to 1000ms + // + + var benchmark = new BenchmarkRunner(); + benchmark.SetIterations(10); + + benchmark.Task("Thread", () => { }).SetThreadBehaviour(ThreadBehaviour.Thread); + benchmark.Task("Task", () => { }).SetThreadBehaviour(ThreadBehaviour.Task); + benchmark.Task("MainThread", () => { }).SetThreadBehaviour(ThreadBehaviour.RunOnMainThread); + + benchmark.RunSessions() + .Trace(new TraceMetrics()); + } + + public class TraceMetrics : Tracers.TraceMetrics + { + public TraceMetrics() + { + Add(ProfilerMetric.Duration); + Add(ProfilerMetric.AverageMilliseconds); + Add(ProfilerMetric.Throughput); + } + } + } +} diff --git a/src/Tests/MeasureMap.Benchmark/WorkerThreadListBenchmarks.cs b/src/Tests/MeasureMap.Benchmark/WorkerThreadListBenchmarks.cs index 60ba9f9..b63630c 100644 --- a/src/Tests/MeasureMap.Benchmark/WorkerThreadListBenchmarks.cs +++ b/src/Tests/MeasureMap.Benchmark/WorkerThreadListBenchmarks.cs @@ -16,7 +16,8 @@ public void RunBenchmarks() var benchmark = new BenchmarkRunner(); benchmark.SetIterations(10); - benchmark.Task("StartNew Thread", ctx => threads.StartNew(ctx.Get(ContextKeys.Iteration), () => new Result())); + benchmark.Task("StartNew Thread", ctx => threads.StartNew(ctx.Get(ContextKeys.Iteration), () => new Result(), WorkerThread.Factory)); + benchmark.Task("StartNew Task", ctx => threads.StartNew(ctx.Get(ContextKeys.Iteration), () => new Result(), WorkerTask.Factory)); benchmark.RunSessions() .Trace(new TraceMetrics()); diff --git a/src/Tests/MeasureMap.UnitTest/BenchmarkRunnerTests.cs b/src/Tests/MeasureMap.UnitTest/BenchmarkRunnerTests.cs index d8530a4..9081650 100644 --- a/src/Tests/MeasureMap.UnitTest/BenchmarkRunnerTests.cs +++ b/src/Tests/MeasureMap.UnitTest/BenchmarkRunnerTests.cs @@ -38,5 +38,26 @@ public void BenchmarkRunner_SetIterations_Runner() .SetIterations(10); runner.Settings.Runner.Should().BeOfType(); } + + [TestCase(ThreadBehaviour.Task)] + [TestCase(ThreadBehaviour.Thread)] + public void BenchmarkRunner_SetThreadBehaviour(ThreadBehaviour behaviour) + { + new BenchmarkRunner() + .SetThreadBehaviour(behaviour).Settings.ThreadBehaviour.Should().Be(behaviour); + } + + [TestCase(ThreadBehaviour.Task)] + [TestCase(ThreadBehaviour.Thread)] + public void BenchmarkRunner_Run_ThreadBehaviour(ThreadBehaviour behaviour) + { + var runner = new BenchmarkRunner() + .SetThreadBehaviour(behaviour); + + var session = runner.Task("test", () => { }); + runner.RunSessions(); + + session.Settings.ThreadBehaviour.Should().Be(behaviour); + } } } diff --git a/src/Tests/MeasureMap.UnitTest/ExecutionContextTests.cs b/src/Tests/MeasureMap.UnitTest/ExecutionContextTests.cs index 62dee97..62c67ce 100644 --- a/src/Tests/MeasureMap.UnitTest/ExecutionContextTests.cs +++ b/src/Tests/MeasureMap.UnitTest/ExecutionContextTests.cs @@ -48,12 +48,5 @@ public void ExecutionContext_Get_Default() Assert.That(value == 0); } - - [Test] - public void ExecutionContext_ThreadList_Default() - { - var context = new ExecutionContext(); - context.Threads.Should().NotBeNull(); - } } } diff --git a/src/Tests/MeasureMap.UnitTest/ProfileSessionTests.cs b/src/Tests/MeasureMap.UnitTest/ProfileSessionTests.cs index f246fc6..6f947c6 100644 --- a/src/Tests/MeasureMap.UnitTest/ProfileSessionTests.cs +++ b/src/Tests/MeasureMap.UnitTest/ProfileSessionTests.cs @@ -9,6 +9,7 @@ using Polaroider; using MeasureMap.UnitTest.Tracers; using MeasureMap.Tracers; +using Moq; namespace MeasureMap.UnitTest { @@ -632,6 +633,29 @@ public void ProfileSession_SetMinLogLevel(LogLevel level) .SetMinLogLevel(level).Settings.Logger.MinLogLevel.Should().Be(level); } + [TestCase(ThreadBehaviour.Task)] + [TestCase(ThreadBehaviour.Thread)] + [TestCase(ThreadBehaviour.RunOnMainThread)] + public void ProfileSession_SetThreadBehaviour(ThreadBehaviour behaviour) + { + ProfilerSession.StartSession() + .SetThreadBehaviour(behaviour).Settings.ThreadBehaviour.Should().Be(behaviour); + } + + [Test] + public void ProfileSession_SetExecutionHandler() + { + var mock = new Mock(); + mock.Setup(x => x.Execute(It.IsAny(), It.IsAny())).Returns(() => new ProfilerResult()); + + ProfilerSession.StartSession() + .SetExecutionHandler(mock.Object) + .Task(() => { }) + .RunSession(); + + mock.Verify(x => x.Execute(It.IsAny(), It.IsAny())); + } + private void Task() { System.Threading.Thread.Sleep(TimeSpan.FromSeconds(0.002)); diff --git a/src/Tests/MeasureMap.UnitTest/ProfilerSettingTests.cs b/src/Tests/MeasureMap.UnitTest/ProfilerSettingTests.cs index 2da9c05..9b838c1 100644 --- a/src/Tests/MeasureMap.UnitTest/ProfilerSettingTests.cs +++ b/src/Tests/MeasureMap.UnitTest/ProfilerSettingTests.cs @@ -3,6 +3,7 @@ using System.Text; using FluentAssertions; using MeasureMap.Runners; +using MeasureMap.Threading; using NUnit.Framework; namespace MeasureMap.UnitTest @@ -96,5 +97,35 @@ public void ProfilerSettings_Runner_SetDuration_CheckNew() settings.Runner.Should().NotBeSameAs(runner); } + + [Test] + public void ProfilerSettings_GetThreadFactory_Default() + { + var settings = new ProfilerSettings(); + + settings.GetThreadFactory().Invoke(1, () => new Result()).Should().BeOfType(); + } + + [Test] + public void ProfilerSettings_GetThreadFactory_Thread() + { + var settings = new ProfilerSettings + { + ThreadBehaviour = ThreadBehaviour.Thread + }; + + settings.GetThreadFactory().Invoke(1, () => new Result()).Should().BeOfType(); + } + + [Test] + public void ProfilerSettings_GetThreadFactory_Task() + { + var settings = new ProfilerSettings + { + ThreadBehaviour = ThreadBehaviour.Task + }; + + settings.GetThreadFactory().Invoke(1, () => new Result()).Should().BeOfType(); + } } } diff --git a/src/Tests/MeasureMap.UnitTest/Runners/TimedTaskExecutionTests.cs b/src/Tests/MeasureMap.UnitTest/Runners/TimedTaskExecutionTests.cs index 5792a5d..45f268d 100644 --- a/src/Tests/MeasureMap.UnitTest/Runners/TimedTaskExecutionTests.cs +++ b/src/Tests/MeasureMap.UnitTest/Runners/TimedTaskExecutionTests.cs @@ -26,8 +26,6 @@ public void TimedTaskExecution_Execute_Initial() var exec = new TimedTaskExecution(TimeSpan.FromSeconds(1)); exec.Execute(context, c => called = true); - context.Threads.WaitAll(); - called.Should().BeTrue(); } @@ -42,8 +40,6 @@ public void TimedTaskExecution_Execute_Repeat() exec.Execute(context, c => called++); exec.Execute(context, c => called++); - context.Threads.WaitAll(); - called.Should().Be(2); } diff --git a/src/Tests/MeasureMap.UnitTest/SessionHandlers/MainThreadSessionHandlerTests.cs b/src/Tests/MeasureMap.UnitTest/SessionHandlers/MainThreadSessionHandlerTests.cs new file mode 100644 index 0000000..0a4edab --- /dev/null +++ b/src/Tests/MeasureMap.UnitTest/SessionHandlers/MainThreadSessionHandlerTests.cs @@ -0,0 +1,30 @@ +using FluentAssertions; +using Moq; +using NUnit.Framework; + +namespace MeasureMap.UnitTest.SessionHandlers +{ + public class MainThreadSessionHandlerTests + { + [Test] + public void MainThreadSessionHandler_Execute() + { + var handler = new MainThreadSessionHandler(); + + var result = handler.Execute(new Mock().Object, new ProfilerSettings()); + + result.Should().NotBeNull(); + } + + [Test] + public void MainThreadSessionHandler_Execute_TaskRun() + { + var handler = new MainThreadSessionHandler(); + var task = new Mock(); + + handler.Execute(task.Object, new ProfilerSettings()); + + task.Verify(x => x.Run(It.IsAny())); + } + } +} diff --git a/src/Tests/MeasureMap.UnitTest/ThreadedProfilerTests.cs b/src/Tests/MeasureMap.UnitTest/ThreadedProfilerTests.cs index 7094ebe..6681d6b 100644 --- a/src/Tests/MeasureMap.UnitTest/ThreadedProfilerTests.cs +++ b/src/Tests/MeasureMap.UnitTest/ThreadedProfilerTests.cs @@ -9,8 +9,9 @@ namespace MeasureMap.UnitTest [TestFixture] public class ThreadedProfilerTests { - [Test] - public void ThreadedProfiler_NoThread() + [TestCase(ThreadBehaviour.Thread)] + [TestCase(ThreadBehaviour.Task)] + public void ThreadedProfiler_NoThread(ThreadBehaviour behaviour) { var result = ProfilerSession.StartSession() .Task(c => @@ -19,6 +20,7 @@ public void ThreadedProfiler_NoThread() Trace.WriteLine($"Iteration {(int)i}"); }) .SetIterations(10) + .SetThreadBehaviour(behaviour) .RunSession(); Assert.IsInstanceOf(result); @@ -27,8 +29,9 @@ public void ThreadedProfiler_NoThread() Assert.That(result.Iterations.Count() == 10); } - [Test] - public void ThreadedProfiler_OneThread() + [TestCase(ThreadBehaviour.Thread)] + [TestCase(ThreadBehaviour.Task)] + public void ThreadedProfiler_OneThread(ThreadBehaviour behaviour) { var result = ProfilerSession.StartSession() .Task(c => @@ -37,6 +40,7 @@ public void ThreadedProfiler_OneThread() Trace.WriteLine($"Iteration {(int)i}"); }) .SetIterations(10) + .SetThreadBehaviour(behaviour) .SetThreads(1) .RunSession(); @@ -44,8 +48,9 @@ public void ThreadedProfiler_OneThread() Assert.That(result.Iterations.Count() == 10); } - [Test] - public void ThreadedProfiler_MultipleThreads() + [TestCase(ThreadBehaviour.Thread)] + [TestCase(ThreadBehaviour.Task)] + public void ThreadedProfiler_MultipleThreads(ThreadBehaviour behaviour) { var result = ProfilerSession.StartSession() .Task(c => @@ -54,6 +59,7 @@ public void ThreadedProfiler_MultipleThreads() Trace.WriteLine($"Iteration {(int)i}"); }) .SetIterations(10) + .SetThreadBehaviour(behaviour) .SetThreads(10) .RunSession(); @@ -61,8 +67,9 @@ public void ThreadedProfiler_MultipleThreads() Assert.That(result.Iterations.Count() == 100); } - [Test] - public void ThreadedProfiler_MultipleThreads_ReturnValues() + [TestCase(ThreadBehaviour.Thread)] + [TestCase(ThreadBehaviour.Task)] + public void ThreadedProfiler_MultipleThreads_ReturnValues(ThreadBehaviour behaviour) { var result = ProfilerSession.StartSession() .Task(c => @@ -71,14 +78,13 @@ public void ThreadedProfiler_MultipleThreads_ReturnValues() Trace.WriteLine($"Iteration {(int)i}"); }) .SetIterations(10) + .SetThreadBehaviour(behaviour) .SetThreads(10) .RunSession(); Assert.That(((ProfilerResult)result).All(r => r.AverageTime.Ticks > 0), () => "AverageTime"); Assert.That(((ProfilerResult)result).All(r => r.EndSize > 0), () => "EndSize"); Assert.That(((ProfilerResult)result).All(r => r.AverageTicks > 0), () => "AverageTicks"); - // milliseconds can be 0 when ticks are too low - //Assert.That(((BenchmarkResult)result).Any(r => r.AverageMilliseconds != 0), () => "AverageMilliseconds"); Assert.That(((ProfilerResult)result).All(r => r.Fastest != null), () => "Fastest"); Assert.That(((ProfilerResult)result).All(r => r.Increase != 0), () => "Increase"); Assert.That(((ProfilerResult)result).All(r => r.InitialSize > 0), () => "InitialSize"); @@ -86,8 +92,9 @@ public void ThreadedProfiler_MultipleThreads_ReturnValues() } - [Test] - public void ThreadedProfiler_MultipleThreads_Interval_Duration() + [TestCase(ThreadBehaviour.Thread)] + [TestCase(ThreadBehaviour.Task)] + public void ThreadedProfiler_MultipleThreads_Interval_Duration(ThreadBehaviour behaviour) { var sw = new Stopwatch(); sw.Start(); @@ -99,6 +106,7 @@ public void ThreadedProfiler_MultipleThreads_Interval_Duration() Trace.WriteLine($"Iteration {i}"); }) .SetDuration(TimeSpan.FromSeconds(20)) + .SetThreadBehaviour(behaviour) .SetInterval(TimeSpan.FromMilliseconds(50)) .SetThreads(10) .RunSession(); @@ -109,46 +117,11 @@ public void ThreadedProfiler_MultipleThreads_Interval_Duration() result.Trace(); result.Elapsed().Should().BeGreaterThan(TimeSpan.FromSeconds(20)).And.BeLessThan(sw.Elapsed); - - /* - ### MeasureMap - Profiler result for Profilesession - #### Summary - Warmup ======================================== - Duration Warmup: 00:00:00.0183942 - Setup ======================================== - Threads: 10 - Iterations: 1129 - Duration ======================================== - Duration: 00:00:25.1914746 - Total Time: 00:01:01.4940694 - Average Time: 00:00:00.0544677 - Average Milliseconds: 53 - Average Ticks: 544677 - Fastest: 00:00:00.0022682 - Slowest: 00:00:05.2171468 - Memory ========================================== - Memory Initial size: 1585784 - Memory End size: 1684312 - Memory Increase: 98528 - - #### Details per Thread - | ThreadId | Iterations | Average time | Slowest | Fastest | - | --- | --- | ---: | ---: | ---: | - | 10 | 194 | 00:00:00.0138437 | 00:00:00.0604912 | 00:00:00.0029758 | - | 11 | 173 | 00:00:00.0191987 | 00:00:00.0870298 | 00:00:00.0032143 | - | 17 | 138 | 00:00:00.0300923 | 00:00:00.2621003 | 00:00:00.0058133 | - | 18 | 119 | 00:00:00.0597062 | 00:00:00.3090188 | 00:00:00.0032996 | - | 16 | 127 | 00:00:00.0422762 | 00:00:00.3011997 | 00:00:00.0045766 | - | 20 | 85 | 00:00:00.1109262 | 00:00:00.3315440 | 00:00:00.0038052 | - | 19 | 99 | 00:00:00.0923007 | 00:00:00.3204989 | 00:00:00.0028416 | - | 21 | 68 | 00:00:00.1224101 | 00:00:00.3372373 | 00:00:00.0029042 | - | 22 | 58 | 00:00:00.1047994 | 00:00:00.4461727 | 00:00:00.0026986 | - | 23 | 68 | 00:00:00.0866374 | 00:00:05.2171468 | 00:00:00.0022682 | - */ } - [Test] - public void ThreadedProfiler_MultipleThreads_Interval_Iterations() + [TestCase(ThreadBehaviour.Thread)] + [TestCase(ThreadBehaviour.Task)] + public void ThreadedProfiler_MultipleThreads_Interval_Iterations(ThreadBehaviour behaviour) { var sw = new Stopwatch(); sw.Start(); @@ -160,6 +133,7 @@ public void ThreadedProfiler_MultipleThreads_Interval_Iterations() Trace.WriteLine($"Iteration {i}"); }) .SetIterations(50) + .SetThreadBehaviour(behaviour) .SetInterval(TimeSpan.FromMilliseconds(50)) .SetThreads(10) .RunSession(); @@ -169,51 +143,11 @@ public void ThreadedProfiler_MultipleThreads_Interval_Iterations() result.Trace(); result.Elapsed().Should().BeLessThan(sw.Elapsed); - - /* - ### MeasureMap - Profiler result for Profilesession - #### Summary - Warmup ======================================== - Duration Warmup: 00:00:00.0170398 - Setup ======================================== - Threads: 10 - Iterations: 500 - Duration ======================================== - Duration: 00:00:07.6054371 - Total Time: 00:00:12.1014659 - Average Time: 00:00:00.0242029 - Average Milliseconds: 23 - Average Ticks: 242029 - Fastest: 00:00:00.0022626 - Slowest: 00:00:00.2318987 - Memory ========================================== - Memory Initial size: 1592056 - Memory End size: 1629092 - Memory Increase: 37036 - - #### Details per Thread - | ThreadId | Iterations | Average time | Slowest | Fastest | - | --- | --- | ---: | ---: | ---: | - | 10 | 50 | 00:00:00.0107312 | 00:00:00.0408709 | 00:00:00.0029143 | - | 11 | 50 | 00:00:00.0127949 | 00:00:00.0277547 | 00:00:00.0033066 | - | 16 | 50 | 00:00:00.0177724 | 00:00:00.0464626 | 00:00:00.0025592 | - | 20 | 50 | 00:00:00.0505545 | 00:00:00.1462360 | 00:00:00.0035389 | - | 17 | 50 | 00:00:00.0364123 | 00:00:00.1176959 | 00:00:00.0029823 | - | 19 | 50 | 00:00:00.0452512 | 00:00:00.1173062 | 00:00:00.0040458 | - | 18 | 50 | 00:00:00.0280446 | 00:00:00.0750011 | 00:00:00.0037804 | - | 10 | 50 | 00:00:00.0139704 | 00:00:00.2229528 | 00:00:00.0026401 | - | 16 | 50 | 00:00:00.0162519 | 00:00:00.2318987 | 00:00:00.0022626 | - | 11 | 50 | 00:00:00.0102454 | 00:00:00.0660816 | 00:00:00.0025892 | - */ } - - - - - - [Test] - public void ThreadedProfiler_MultipleThreads_Simple_Duration() + [TestCase(ThreadBehaviour.Thread)] + [TestCase(ThreadBehaviour.Task)] + public void ThreadedProfiler_MultipleThreads_Simple_Duration(ThreadBehaviour behaviour) { var sw = new Stopwatch(); sw.Start(); @@ -225,6 +159,7 @@ public void ThreadedProfiler_MultipleThreads_Simple_Duration() Trace.WriteLine($"Iteration {i}"); }) .SetDuration(TimeSpan.FromSeconds(20)) + .SetThreadBehaviour(behaviour) .SetThreads(10) .RunSession(); @@ -234,46 +169,11 @@ public void ThreadedProfiler_MultipleThreads_Simple_Duration() result.Trace(); result.Elapsed().Should().BeGreaterThan(TimeSpan.FromSeconds(20)).And.BeLessThan(sw.Elapsed); - - /* - ### MeasureMap - Profiler result for Profilesession - #### Summary - Warmup ======================================== - Duration Warmup: 00:00:00.0189054 - Setup ======================================== - Threads: 10 - Iterations: 2268 - Duration ======================================== - Duration: 00:00:41.3471002 - Total Time: 00:00:55.2762125 - Average Time: 00:00:00.0243722 - Average Milliseconds: 23 - Average Ticks: 243722 - Fastest: 00:00:00.0024162 - Slowest: 00:00:06.3143534 - Memory ========================================== - Memory Initial size: 1596528 - Memory End size: 1785172 - Memory Increase: 188644 - - #### Details per Thread - | ThreadId | Iterations | Average time | Slowest | Fastest | - | --- | --- | ---: | ---: | ---: | - | 11 | 350 | 00:00:00.0130281 | 00:00:00.0821559 | 00:00:00.0024162 | - | 9 | 412 | 00:00:00.0105151 | 00:00:00.0238863 | 00:00:00.0034361 | - | 17 | 50 | 00:00:00.1314544 | 00:00:01.4934925 | 00:00:00.0052489 | - | 19 | 1 | 00:00:06.3143534 | 00:00:06.3143534 | 00:00:06.3143534 | - | 16 | 262 | 00:00:00.0178478 | 00:00:00.0435406 | 00:00:00.0034318 | - | 20 | 405 | 00:00:00.0115145 | 00:00:00.2712846 | 00:00:00.0028629 | - | 18 | 93 | 00:00:00.0721457 | 00:00:00.4774589 | 00:00:00.0037850 | - | 21 | 345 | 00:00:00.0137611 | 00:00:00.0518304 | 00:00:00.0039876 | - | 22 | 239 | 00:00:00.0252535 | 00:00:00.2743825 | 00:00:00.0026940 | - | 23 | 111 | 00:00:00.0600429 | 00:00:01.1319821 | 00:00:00.0024369 | - */ } - [Test] - public void ThreadedProfiler_MultipleThreads_Simple_Iterations() + [TestCase(ThreadBehaviour.Thread)] + [TestCase(ThreadBehaviour.Task)] + public void ThreadedProfiler_MultipleThreads_Simple_Iterations(ThreadBehaviour behaviour) { var sw = new Stopwatch(); sw.Start(); @@ -285,6 +185,7 @@ public void ThreadedProfiler_MultipleThreads_Simple_Iterations() Trace.WriteLine($"Iteration {i}"); }) .SetIterations(50) + .SetThreadBehaviour(behaviour) .SetThreads(10) .RunSession(); @@ -293,42 +194,6 @@ public void ThreadedProfiler_MultipleThreads_Simple_Iterations() result.Trace(); result.Elapsed().Should().BeLessThan(sw.Elapsed); - - /* - ### MeasureMap - Profiler result for Profilesession - #### Summary - Warmup ======================================== - Duration Warmup: 00:00:00.0191410 - Setup ======================================== - Threads: 10 - Iterations: 500 - Duration ======================================== - Duration: 00:00:08.4436926 - Total Time: 00:00:09.2145654 - Average Time: 00:00:00.0184291 - Average Milliseconds: 17 - Average Ticks: 184291 - Fastest: 00:00:00.0021651 - Slowest: 00:00:00.6672119 - Memory ========================================== - Memory Initial size: 1596900 - Memory End size: 1639636 - Memory Increase: 42736 - - #### Details per Thread - | ThreadId | Iterations | Average time | Slowest | Fastest | - | --- | --- | ---: | ---: | ---: | - | 10 | 50 | 00:00:00.0106889 | 00:00:00.1027457 | 00:00:00.0026854 | - | 11 | 50 | 00:00:00.0122346 | 00:00:00.0341723 | 00:00:00.0054592 | - | 16 | 50 | 00:00:00.0171771 | 00:00:00.0393153 | 00:00:00.0067055 | - | 19 | 50 | 00:00:00.0153481 | 00:00:00.1648672 | 00:00:00.0024235 | - | 20 | 50 | 00:00:00.0187932 | 00:00:00.3237942 | 00:00:00.0035926 | - | 17 | 50 | 00:00:00.0410966 | 00:00:00.6672119 | 00:00:00.0061538 | - | 18 | 50 | 00:00:00.0309728 | 00:00:00.3929003 | 00:00:00.0031255 | - | 21 | 50 | 00:00:00.0171989 | 00:00:00.1665635 | 00:00:00.0021651 | - | 10 | 50 | 00:00:00.0101438 | 00:00:00.0211847 | 00:00:00.0046102 | - | 11 | 50 | 00:00:00.0106368 | 00:00:00.0214394 | 00:00:00.0028004 | - */ } } } diff --git a/src/Tests/MeasureMap.UnitTest/Threading/WorkerThreadListTests.cs b/src/Tests/MeasureMap.UnitTest/Threading/WorkerThreadListTests.cs index a03fecc..266ff33 100644 --- a/src/Tests/MeasureMap.UnitTest/Threading/WorkerThreadListTests.cs +++ b/src/Tests/MeasureMap.UnitTest/Threading/WorkerThreadListTests.cs @@ -11,10 +11,19 @@ namespace MeasureMap.UnitTest.Threading public class WorkerThreadListTests { [Test] - public void WorkerThreadList_StartNew() + public void WorkerThreadList_StartNew_Thread() { var list = new WorkerThreadList(); - var thread = list.StartNew(1, () => new Result()); + var thread = list.StartNew(1, () => new Result(), WorkerThread.Factory); + + thread.Should().NotBeNull(); + } + + [Test] + public void WorkerThreadList_StartNew_Task() + { + var list = new WorkerThreadList(); + var thread = list.StartNew(1, () => new Result(), WorkerTask.Factory); thread.Should().NotBeNull(); } @@ -23,7 +32,16 @@ public void WorkerThreadList_StartNew() public void WorkerThreadList_StartNew_Added() { var list = new WorkerThreadList(); - var thread = list.StartNew(1, () => new Result()); + var thread = list.StartNew(1, () => new Result(), WorkerThread.Factory); + + list.Single().Should().BeSameAs(thread); + } + + [Test] + public void WorkerThreadList_StartNew_Task_Added() + { + var list = new WorkerThreadList(); + var thread = list.StartNew(1, () => new Result(), WorkerTask.Factory); list.Single().Should().BeSameAs(thread); } @@ -81,6 +99,25 @@ public void WorkerThreadList_WaitAll() list.Should().OnlyContain(t => !t.IsAlive); } + [Test] + public void WorkerThreadList_Task_WaitAll() + { + var thread = new WorkerTask(1, () => + { + System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(1)).Wait(); + return new Result(); + }); + var list = new WorkerThreadList + { + thread + }; + thread.Start(); + + list.WaitAll(); + + list.Should().OnlyContain(t => !t.IsAlive); + } + [Test] public void WorkerThreadList_WaitAll_MultipleTreads() { @@ -89,12 +126,32 @@ public void WorkerThreadList_WaitAll_MultipleTreads() { System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(1)).Wait(); return new Result(); - }).Start(); + }, WorkerThread.Factory).Start(); + list.StartNew(1, () => + { + System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(1)).Wait(); + return new Result(); + }, WorkerThread.Factory).Start(); + + list.WaitAll(); + + list.Should().OnlyContain(t => !t.IsAlive); + } + + [Test] + public void WorkerThreadList_WaitAll_MultipleTasks() + { + var list = new WorkerThreadList(); + list.StartNew(1, () => + { + System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(1)).Wait(); + return new Result(); + }, WorkerTask.Factory).Start(); list.StartNew(1, () => { System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(1)).Wait(); return new Result(); - }).Start(); + }, WorkerTask.Factory).Start(); list.WaitAll(); diff --git a/src/Tests/MeasureMap.UnitTest/WorkerTests.cs b/src/Tests/MeasureMap.UnitTest/WorkerTests.cs index 3fee6b3..3570b3b 100644 --- a/src/Tests/MeasureMap.UnitTest/WorkerTests.cs +++ b/src/Tests/MeasureMap.UnitTest/WorkerTests.cs @@ -55,19 +55,5 @@ public void MeasureMap_Worker_Memory() Assert.That(result.EndSize > 0, () => "EndSize is smaller than 0"); Assert.That(result.Increase != 0, () => "Increase is 0"); } - - [Test] - public void MeasureMap_Worker_ThreadList() - { - Action task = () => new Worker(new ThreadList()); - task.Should().NotThrow(); - } - - [Test] - public void MeasureMap_Worker_ThreadList_Null() - { - Action task = () => new Worker(null); - task.Should().Throw(); - } } }