diff --git a/CHANGELOG.md b/CHANGELOG.md index 92599bc..01c5d38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,16 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## vNext +## v2.2.0 ### Added - ThreadBehaviour to define how a thread is created - Allow benchmarks to be run on the MainThread +- OnExecuted to run a delegate after each task run +- Trace - Edit TraceOptions as delegate ### Changed - Added IDisposable to IThreadSessionHandler ### Fixed -- +- Markdowntracer traced all data when using DetailPerThread ## v2.1.0 ### Added diff --git a/MeasureMap.sln b/MeasureMap.sln index 456ca42..ad4829f 100644 --- a/MeasureMap.sln +++ b/MeasureMap.sln @@ -11,7 +11,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{C302527D EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3BE4645D-C5EB-4675-AEE2-68BFE6AC84EB}" ProjectSection(SolutionItems) = preProject - .travis.yml = .travis.yml appveyor.yml = appveyor.yml .github\workflows\build.yml = .github\workflows\build.yml CHANGELOG.md = CHANGELOG.md @@ -21,7 +20,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{364A83BE-EC1F-42DA-ACCB-69DE17631CF3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MeasureMap.Benchmark", "src\Tests\MeasureMap.Benchmark\MeasureMap.Benchmark.csproj", "{5BA1A434-30F8-4233-B4C1-A668AB50BC4B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MeasureMap.Benchmark", "src\Tests\MeasureMap.Benchmark\MeasureMap.Benchmark.csproj", "{5BA1A434-30F8-4233-B4C1-A668AB50BC4B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/README.md b/README.md index b483718..e29aca7 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # MeasureMap .NET Benchmarking made simple -[![Build Status](https://img.shields.io/travis/com/WickedFlame/MeasureMap/master.svg?label=Travis-CI&style=for-the-badge)](https://app.travis-ci.com/github/WickedFlame/MeasureMap) [![Build status](https://img.shields.io/appveyor/build/chriswalpen/measuremap/master?label=Master&logo=appveyor&style=for-the-badge)](https://ci.appveyor.com/project/chriswalpen/measuremap/branch/master) [![Build status](https://img.shields.io/appveyor/build/chriswalpen/measuremap/dev?label=Dev&logo=appveyor&style=for-the-badge)](https://ci.appveyor.com/project/chriswalpen/measuremap/branch/dev) + [![NuGet Version](https://img.shields.io/nuget/v/measuremap.svg?style=for-the-badge&label=Latest)](https://www.nuget.org/packages/measuremap/) [![NuGet Version](https://img.shields.io/nuget/vpre/measuremap.svg?style=for-the-badge&label=RC)](https://www.nuget.org/packages/measuremap/) @@ -11,11 +11,15 @@ [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=WickedFlame_MeasureMap&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=WickedFlame_MeasureMap) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=WickedFlame_MeasureMap&metric=coverage)](https://sonarcloud.io/summary/new_code?id=WickedFlame_MeasureMap) -Measuremap is a lightweight assembly that allows profiling and benchmarking code. +Measuremap allows profiling and benchmarking from simple code fragmets to full applications. Visit [https://wickedflame.github.io/MeasureMap/](https://wickedflame.github.io/MeasureMap/) for the full documentation. - + +MeasureMap uses the builder pattern and a fluent API to make benchmarking as simple as possible. + ## Profiling +Profiles are initiated with ProfilerSession.StartSession(). + ```csharp var result = ProfilerSession.StartSession() .Task(() => @@ -34,6 +38,9 @@ Assert.IsTrue(result.AverageMilliseconds < 20); ## Benchmarking +Benchmarks are a collection of ProfilerSessions. +These are initiated and started with the BenchmarkRunner. + ```csharp var sha256 = SHA256.Create(); var md5 = MD5.Create(); diff --git a/appveyor.yml b/appveyor.yml index 30f3492..18a8d5c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,7 @@ # http://www.appveyor.com/docs/appveyor-yml environment: - base_version: 2.1.0 + base_version: 2.2.0 # version format version: $(base_version).{build} @@ -27,7 +27,7 @@ for: - dotnet restore ./build/_build.csproj build_script: - - nuke Release --isrc true --version "%base_version%" --buildno "%APPVEYOR_BUILD_NUMBER%" + - nuke Release --isrc true --buildno "%APPVEYOR_BUILD_NUMBER%" test: off @@ -48,7 +48,7 @@ for: - dotnet restore ./build/_build.csproj build_script: - - nuke Release --isrc false --version "%base_version%" --buildno "%APPVEYOR_BUILD_NUMBER%" + - nuke Release --isrc false --buildno "%APPVEYOR_BUILD_NUMBER%" --version $(base_version) test: off diff --git a/build/Build.cs b/build/Build.cs index 7e8c8c2..cb1a621 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -38,7 +38,7 @@ class Build : NukeBuild [GitRepository] readonly GitRepository GitRepository; [Parameter("Version to be injected in the Build")] - public string Version { get; set; } = $"2.1.0"; + public string Version { get; set; } = $"2.1.1"; [Parameter("The Buildnumber provided by the CI")] public int BuildNo = 6; diff --git a/src/MeasureMap/ProfilerResultExtensions.cs b/src/MeasureMap/ProfilerResultExtensions.cs index 0271af8..5f5c9e2 100644 --- a/src/MeasureMap/ProfilerResultExtensions.cs +++ b/src/MeasureMap/ProfilerResultExtensions.cs @@ -95,6 +95,19 @@ public static void Trace(this IProfilerResult result, TraceOptions options) result.Trace(options.Tracer, options.ResultWriter, options); } + /// + /// Trace the output of a Profiler + /// + /// + /// + public static void Trace(this IProfilerResult result, Action factory) + { + var options = TraceOptions.Default.Clone(); + factory.Invoke(options); + + result.Trace(options); + } + /// /// Trace the output of a Profiler /// diff --git a/src/MeasureMap/ProfilerSessionExtensions.cs b/src/MeasureMap/ProfilerSessionExtensions.cs index 01cd946..3fc464b 100644 --- a/src/MeasureMap/ProfilerSessionExtensions.cs +++ b/src/MeasureMap/ProfilerSessionExtensions.cs @@ -1,6 +1,7 @@ using System; using MeasureMap.Diagnostics; using MeasureMap.Runners; +using MeasureMap.TaskHandlers; namespace MeasureMap { @@ -298,6 +299,18 @@ public static ProfilerSession SetMinLogLevel(this ProfilerSession session, LogLe return session; } + /// + /// Add a delegate that is run after the task is executed. It is possible to add multiple delegates by calling this method multiple times + /// + /// + /// + /// + public static ProfilerSession OnExecuted(this ProfilerSession session, Action execution) + { + session.ProcessingPipeline.SetNext(new OnExecutedTaskHandler(execution)); + return session; + } + internal static ProfilerSession AppendSettings(this ProfilerSession session, ProfilerSettings settings) { session.SetMinLogLevel(settings.Logger.MinLogLevel); diff --git a/src/MeasureMap/TaskHandlers/OnExecutedTaskHandler.cs b/src/MeasureMap/TaskHandlers/OnExecutedTaskHandler.cs new file mode 100644 index 0000000..590ed61 --- /dev/null +++ b/src/MeasureMap/TaskHandlers/OnExecutedTaskHandler.cs @@ -0,0 +1,35 @@ +using System; + +namespace MeasureMap.TaskHandlers +{ + /// + /// TaskHandler that executes a delegate after the taskrun + /// + public class OnExecutedTaskHandler : TaskHandler + { + private readonly Action _execution; + + /// + /// + /// + /// + public OnExecutedTaskHandler(Action execution) + { + _execution = execution; + } + + /// + /// Executes the task + /// + /// The current execution context + /// The resulting collection of the executions + public override IIterationResult Run(IExecutionContext context) + { + var result = base.Run(context); + + System.Threading.Tasks.Task.Run(() => _execution.Invoke(result)); + + return result; + } + } +} diff --git a/src/MeasureMap/Tracers/MarkDownTracer.cs b/src/MeasureMap/Tracers/MarkDownTracer.cs index 272ecb3..965180d 100644 --- a/src/MeasureMap/Tracers/MarkDownTracer.cs +++ b/src/MeasureMap/Tracers/MarkDownTracer.cs @@ -75,7 +75,7 @@ public void Trace(IProfilerResult result, IResultWriter writer, TraceOptions opt } } - if (options.TraceDetail >= TraceDetail.DetailPerThread) + if (options.TraceDetail > TraceDetail.DetailPerThread) { writer.WriteLine(string.Empty); writer.WriteLine("## Details per Iteration and Thread"); diff --git a/src/Tests/MeasureMap.UnitTest/ProfileSessionTests.cs b/src/Tests/MeasureMap.UnitTest/ProfileSessionTests.cs index 6f947c6..501ab28 100644 --- a/src/Tests/MeasureMap.UnitTest/ProfileSessionTests.cs +++ b/src/Tests/MeasureMap.UnitTest/ProfileSessionTests.cs @@ -656,6 +656,59 @@ public void ProfileSession_SetExecutionHandler() mock.Verify(x => x.Execute(It.IsAny(), It.IsAny())); } + [Test] + public void ProfileSession_OnExecuted() + { + var calls = 0; + + ProfilerSession.StartSession() + .SetIterations(5) + .RunWarmup(false) + .OnExecuted(r => calls++) + .Task(() => { }) + .RunSession(); + + calls.Should().Be(5); + } + + [Test] + public void ProfileSession_OnExecuted_Warmup() + { + var calls = 0; + + ProfilerSession.StartSession() + .OnExecuted(r => calls++) + .Task(() => { }) + .RunSession(); + + calls.Should().Be(2); + } + + [Test] + public void ProfileSession_OnExecuted_IIterationResult() + { + ProfilerSession.StartSession() + .OnExecuted(r => r.Should().BeAssignableTo()) + .Task(() => { }) + .RunSession(); + } + + [Test] + public void ProfileSession_OnExecuted_MultipleRegistrations() + { + var first = 0; + var second = 0; + + ProfilerSession.StartSession() + .OnExecuted(r => first++) + .OnExecuted(r => second++) + .Task(() => { }) + .RunSession(); + + first.Should().Be(2); + second.Should().Be(2); + } + private void Task() { System.Threading.Thread.Sleep(TimeSpan.FromSeconds(0.002)); diff --git a/src/Tests/MeasureMap.UnitTest/Tracers/TracerTests.cs b/src/Tests/MeasureMap.UnitTest/Tracers/TracerTests.cs index 2c38e8a..153c811 100644 --- a/src/Tests/MeasureMap.UnitTest/Tracers/TracerTests.cs +++ b/src/Tests/MeasureMap.UnitTest/Tracers/TracerTests.cs @@ -1,4 +1,6 @@ -using System.Text; +using System; +using System.Text; +using FluentAssertions; using MeasureMap.Tracers; using NUnit.Framework; using Polaroider; @@ -122,6 +124,33 @@ public void Tracer_Profiler_Options() ((StringResultWriter)options.ResultWriter).Value.MatchSnapshot(); } + + [Test] + public void Tracer_Profiler_Options_Delegate() + { + TraceOptions options = null; + + var result = ResultFactory.CreateResult(); + + result.Trace(o => + { + o.ResultWriter = new StringResultWriter(); + o.Tracer = new MarkDownTracer(); + options = o; + }); + + ((StringResultWriter)options.ResultWriter).Value.MatchSnapshot(); + } + + [Test] + public void Tracer_Profiler_Options_Delegate_Simple() + { + var result = ResultFactory.CreateResult(); + + Action action = () => result.Trace(o => o.TraceDetail = TraceDetail.DetailPerThread); + + action.Should().NotThrow(); + } } public class StringResultWriter : IResultWriter diff --git a/src/Tests/MeasureMap.UnitTest/Tracers/_Snapshots/MarkDownTracerTests_MarkDownTracer_ProfilerResult_DetailPerThread.snapshot b/src/Tests/MeasureMap.UnitTest/Tracers/_Snapshots/MarkDownTracerTests_MarkDownTracer_ProfilerResult_DetailPerThread.snapshot index 15c6dcd..460d6a5 100644 --- a/src/Tests/MeasureMap.UnitTest/Tracers/_Snapshots/MarkDownTracerTests_MarkDownTracer_ProfilerResult_DetailPerThread.snapshot +++ b/src/Tests/MeasureMap.UnitTest/Tracers/_Snapshots/MarkDownTracerTests_MarkDownTracer_ProfilerResult_DetailPerThread.snapshot @@ -23,27 +23,3 @@ | 1 | 10 | 00:00:00.0000013 | 00:00:00.0000018 | 00:00:00.0000009 | 740740.74074/s | | 2 | 10 | 00:00:00.0000013 | 00:00:00.0000018 | 00:00:00.0000009 | 740740.74074/s | -## Details per Iteration and Thread -| ThreadId | Iteration | TimeStamp | Milliseconds | -| -------- | --------- | --------------------------- | -----------: | -| 1 | 1 | 2012-12-21T01:01:01.0010001 | 0.0018 ms | -| 2 | 1 | 2012-12-21T01:01:01.0010001 | 0.0018 ms | -| 1 | 2 | 2012-12-21T01:01:01.0010002 | 0.0017 ms | -| 2 | 2 | 2012-12-21T01:01:01.0010002 | 0.0017 ms | -| 1 | 3 | 2012-12-21T01:01:01.0010003 | 0.0016 ms | -| 2 | 3 | 2012-12-21T01:01:01.0010003 | 0.0016 ms | -| 1 | 4 | 2012-12-21T01:01:01.0010004 | 0.0015 ms | -| 2 | 4 | 2012-12-21T01:01:01.0010004 | 0.0015 ms | -| 1 | 5 | 2012-12-21T01:01:01.0010005 | 0.0014 ms | -| 2 | 5 | 2012-12-21T01:01:01.0010005 | 0.0014 ms | -| 1 | 6 | 2012-12-21T01:01:01.0010006 | 0.0013 ms | -| 2 | 6 | 2012-12-21T01:01:01.0010006 | 0.0013 ms | -| 1 | 7 | 2012-12-21T01:01:01.0010007 | 0.0012 ms | -| 2 | 7 | 2012-12-21T01:01:01.0010007 | 0.0012 ms | -| 1 | 8 | 2012-12-21T01:01:01.0010008 | 0.0011 ms | -| 2 | 8 | 2012-12-21T01:01:01.0010008 | 0.0011 ms | -| 1 | 9 | 2012-12-21T01:01:01.0010009 | 0.001 ms | -| 2 | 9 | 2012-12-21T01:01:01.0010009 | 0.001 ms | -| 1 | 10 | 2012-12-21T01:01:01.0010010 | 0.0009 ms | -| 2 | 10 | 2012-12-21T01:01:01.0010010 | 0.0009 ms | - diff --git a/src/Tests/MeasureMap.UnitTest/Tracers/_Snapshots/MetricsTests_Metrics_Profiler_Detailed.snapshot b/src/Tests/MeasureMap.UnitTest/Tracers/_Snapshots/MetricsTests_Metrics_Profiler_Detailed.snapshot index 15c6dcd..460d6a5 100644 --- a/src/Tests/MeasureMap.UnitTest/Tracers/_Snapshots/MetricsTests_Metrics_Profiler_Detailed.snapshot +++ b/src/Tests/MeasureMap.UnitTest/Tracers/_Snapshots/MetricsTests_Metrics_Profiler_Detailed.snapshot @@ -23,27 +23,3 @@ | 1 | 10 | 00:00:00.0000013 | 00:00:00.0000018 | 00:00:00.0000009 | 740740.74074/s | | 2 | 10 | 00:00:00.0000013 | 00:00:00.0000018 | 00:00:00.0000009 | 740740.74074/s | -## Details per Iteration and Thread -| ThreadId | Iteration | TimeStamp | Milliseconds | -| -------- | --------- | --------------------------- | -----------: | -| 1 | 1 | 2012-12-21T01:01:01.0010001 | 0.0018 ms | -| 2 | 1 | 2012-12-21T01:01:01.0010001 | 0.0018 ms | -| 1 | 2 | 2012-12-21T01:01:01.0010002 | 0.0017 ms | -| 2 | 2 | 2012-12-21T01:01:01.0010002 | 0.0017 ms | -| 1 | 3 | 2012-12-21T01:01:01.0010003 | 0.0016 ms | -| 2 | 3 | 2012-12-21T01:01:01.0010003 | 0.0016 ms | -| 1 | 4 | 2012-12-21T01:01:01.0010004 | 0.0015 ms | -| 2 | 4 | 2012-12-21T01:01:01.0010004 | 0.0015 ms | -| 1 | 5 | 2012-12-21T01:01:01.0010005 | 0.0014 ms | -| 2 | 5 | 2012-12-21T01:01:01.0010005 | 0.0014 ms | -| 1 | 6 | 2012-12-21T01:01:01.0010006 | 0.0013 ms | -| 2 | 6 | 2012-12-21T01:01:01.0010006 | 0.0013 ms | -| 1 | 7 | 2012-12-21T01:01:01.0010007 | 0.0012 ms | -| 2 | 7 | 2012-12-21T01:01:01.0010007 | 0.0012 ms | -| 1 | 8 | 2012-12-21T01:01:01.0010008 | 0.0011 ms | -| 2 | 8 | 2012-12-21T01:01:01.0010008 | 0.0011 ms | -| 1 | 9 | 2012-12-21T01:01:01.0010009 | 0.001 ms | -| 2 | 9 | 2012-12-21T01:01:01.0010009 | 0.001 ms | -| 1 | 10 | 2012-12-21T01:01:01.0010010 | 0.0009 ms | -| 2 | 10 | 2012-12-21T01:01:01.0010010 | 0.0009 ms | - diff --git a/src/Tests/MeasureMap.UnitTest/Tracers/_Snapshots/TracerTests_Tracer_Profiler_Options_Delegate.snapshot b/src/Tests/MeasureMap.UnitTest/Tracers/_Snapshots/TracerTests_Tracer_Profiler_Options_Delegate.snapshot new file mode 100644 index 0000000..a98efd7 --- /dev/null +++ b/src/Tests/MeasureMap.UnitTest/Tracers/_Snapshots/TracerTests_Tracer_Profiler_Options_Delegate.snapshot @@ -0,0 +1,19 @@ +---data +# MeasureMap - Profiler result +## Summary +| Category | Metric | Value | +| :------- | :---------------- | ---------------: | +| Warmup | Duration Warmup | 00:00:00 | +| Setup | Threads | 2 | +| | Iterations | 20 | +| Duration | Duration | 00:00:00.0000270 | +| | Total Time | 00:00:00.0000270 | +| | Avg. Time | 00:00:00.0000013 | +| | Avg. Milliseconds | 0.0013 ms | +| | Throughput | 740740.74074/s | +| | Fastest | 00:00:00.0000009 | +| | Slowest | 00:00:00.0000018 | +| Memory | Memory init size | 0 | +| | Memory end size | 0 | +| | Memory increase | 0 | +