Skip to content

Commit

Permalink
Introduce IterationSetup/IterationCleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreyAkinshin committed May 29, 2017
1 parent 4dd789d commit 21369c2
Show file tree
Hide file tree
Showing 16 changed files with 201 additions and 10 deletions.
14 changes: 14 additions & 0 deletions src/BenchmarkDotNet.Core/Attributes/IterationCleanupAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using JetBrains.Annotations;

namespace BenchmarkDotNet.Attributes
{
/// <summary>
/// Marks method to be executed after each benchmark iteration.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
[MeansImplicitUse]
public class IterationCleanupAttribute : Attribute
{
}
}
14 changes: 14 additions & 0 deletions src/BenchmarkDotNet.Core/Attributes/IterationSetupAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using JetBrains.Annotations;

namespace BenchmarkDotNet.Attributes
{
/// <summary>
/// Marks method to be executed after each benchmark iteration.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
[MeansImplicitUse]
public class IterationSetupAttribute: Attribute
{
}
}
2 changes: 2 additions & 0 deletions src/BenchmarkDotNet.Core/Code/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ internal static string Generate(Benchmark benchmark)
Replace("$IdleMethodReturnType$", provider.IdleMethodReturnType).
Replace("$GlobalSetupMethodName$", provider.GlobalSetupMethodName).
Replace("$GlobalCleanupMethodName$", provider.GlobalCleanupMethodName).
Replace("$IterationSetupMethodName$", provider.IterationSetupMethodName).
Replace("$IterationCleanupMethodName$", provider.IterationCleanupMethodName).
Replace("$IdleImplementation$", provider.IdleImplementation).
Replace("$HasReturnValue$", provider.HasReturnValue).
Replace("$AdditionalLogic$", benchmark.Target.AdditionalLogic).
Expand Down
4 changes: 4 additions & 0 deletions src/BenchmarkDotNet.Core/Code/DeclarationsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ internal DeclarationsProvider(Target target)

public string GlobalCleanupMethodName => Target.GlobalCleanupMethod?.Name ?? EmptyAction;

public string IterationSetupMethodName => Target.IterationSetupMethod?.Name ?? EmptyAction;

public string IterationCleanupMethodName => Target.IterationCleanupMethod?.Name ?? EmptyAction;

public virtual string TargetMethodDelegate => Target.Method.Name;

public abstract string TargetMethodReturnTypeNamespace { get; }
Expand Down
10 changes: 9 additions & 1 deletion src/BenchmarkDotNet.Core/Engines/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public class Engine : IEngine
public long OperationsPerInvoke { get; }
public Action GlobalSetupAction { get; }
public Action GlobalCleanupAction { get; }
public Action IterationSetupAction { get; }
public Action IterationCleanupAction { get; }
public IResolver Resolver { get; }

private IClock Clock { get; }
Expand All @@ -44,7 +46,7 @@ public class Engine : IEngine
internal Engine(
IHost host,
Action dummy1Action, Action dummy2Action, Action dummy3Action, Action<long> idleAction, Action<long> mainAction, Job targetJob,
Action globalSetupAction, Action globalCleanupAction, long operationsPerInvoke)
Action globalSetupAction, Action globalCleanupAction, Action iterationSetupAction, Action iterationCleanupAction, long operationsPerInvoke)
{
Host = host;
IsDiagnoserAttached = host.IsDiagnoserAttached;
Expand All @@ -56,6 +58,8 @@ internal Engine(
TargetJob = targetJob;
GlobalSetupAction = globalSetupAction;
GlobalCleanupAction = globalCleanupAction;
IterationSetupAction = iterationSetupAction;
IterationCleanupAction = iterationCleanupAction;
OperationsPerInvoke = operationsPerInvoke;

Resolver = new CompositeResolver(BenchmarkRunnerCore.DefaultResolver, EngineResolver.Instance);
Expand All @@ -76,6 +80,8 @@ public void PreAllocate()
{
var list = new List<Measurement> { new Measurement(), new Measurement() };
list.Sort(); // provoke JIT, static ctors etc (was allocating 1740 bytes with first call)

// ReSharper disable once CompareOfFloatsByEqualityOperator
if (TimeUnit.All == null || list[0].Nanoseconds != default(double))
throw new Exception("just use this things here to provoke static ctor");
isPreAllocated = true;
Expand Down Expand Up @@ -143,13 +149,15 @@ public Measurement RunIteration(IterationData data)
long totalOperations = invokeCount * OperationsPerInvoke;
var action = data.IterationMode.IsIdle() ? IdleAction : MainAction;

IterationSetupAction();
GcCollect();

// Measure
var clock = Clock.Start();
action(invokeCount / unrollFactor);
var clockSpan = clock.Stop();

IterationCleanupAction();
GcCollect();

// Results
Expand Down
2 changes: 2 additions & 0 deletions src/BenchmarkDotNet.Core/Engines/EngineFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public IEngine Create(EngineParameters engineParameters)
engineParameters.TargetJob,
engineParameters.GlobalSetupAction,
engineParameters.GlobalCleanupAction,
engineParameters.IterationSetupAction,
engineParameters.IterationCleanupAction,
engineParameters.OperationsPerInvoke);
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/BenchmarkDotNet.Core/Engines/EngineParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public class EngineParameters
public long OperationsPerInvoke { get; set; } = 1;
public Action GlobalSetupAction { get; set; } = null;
public Action GlobalCleanupAction { get; set; } = null;
public Action IterationSetupAction { get; set; } = null;
public Action IterationCleanupAction { get; set; } = null;
public IResolver Resolver { get; set; }
}
}
21 changes: 16 additions & 5 deletions src/BenchmarkDotNet.Core/Running/BenchmarkConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public static Benchmark[] MethodsToBenchmarks(Type containingType, MethodInfo[]
var globalSetupMethod = GetWrappingMethod<GlobalSetupAttribute>(methods, "GlobalSetup");
var targetMethods = methods.Where(method => method.HasAttribute<BenchmarkAttribute>()).ToArray();
var globalCleanupMethod = GetWrappingMethod<GlobalCleanupAttribute>(methods, "GlobalCleanup");
var iterationSetupMethod = GetWrappingMethod<IterationSetupAttribute>(methods, "IterationSetup");
var iterationCleanupMethod = GetWrappingMethod<IterationCleanupAttribute>(methods, "IterationCleanup");

var parameterDefinitions = GetParameterDefinitions(containingType);
var parameterInstancesList = parameterDefinitions.Expand();
Expand All @@ -36,7 +38,7 @@ public static Benchmark[] MethodsToBenchmarks(Type containingType, MethodInfo[]
rawJobs = new[] { Job.Default };
var jobs = rawJobs.Distinct().ToArray();

var targets = GetTargets(targetMethods, containingType, globalSetupMethod, globalCleanupMethod).ToArray();
var targets = GetTargets(targetMethods, containingType, globalSetupMethod, globalCleanupMethod, iterationSetupMethod, iterationCleanupMethod).ToArray();

var benchmarks = (
from target in targets
Expand Down Expand Up @@ -65,15 +67,22 @@ public static IConfig GetFullConfig(Type type, IConfig config)
return config;
}

private static IEnumerable<Target> GetTargets(MethodInfo[] targetMethods, Type type, MethodInfo globalSetupMethod, MethodInfo globalCleanupMethod) => targetMethods.
Where(m => m.HasAttribute<BenchmarkAttribute>()).
Select(methodInfo => CreateTarget(type, globalSetupMethod, methodInfo, globalCleanupMethod, methodInfo.ResolveAttribute<BenchmarkAttribute>(), targetMethods));
private static IEnumerable<Target> GetTargets(MethodInfo[] targetMethods, Type type, MethodInfo globalSetupMethod, MethodInfo globalCleanupMethod,
MethodInfo iterationSetupMethod, MethodInfo iterationCleanupMethod)
{
return targetMethods
.Where(m => m.HasAttribute<BenchmarkAttribute>())
.Select(methodInfo => CreateTarget(type, globalSetupMethod, methodInfo, globalCleanupMethod, iterationSetupMethod, iterationCleanupMethod,
methodInfo.ResolveAttribute<BenchmarkAttribute>(), targetMethods));
}

private static Target CreateTarget(
Type type,
MethodInfo globalSetupMethod,
MethodInfo methodInfo,
MethodInfo globalCleanupMethod,
MethodInfo globalCleanupMethod,
MethodInfo iterationSetupMethod,
MethodInfo iterationCleanupMethod,
BenchmarkAttribute attr,
MethodInfo[] targetMethods)
{
Expand All @@ -82,6 +91,8 @@ private static Target CreateTarget(
methodInfo,
globalSetupMethod,
globalCleanupMethod,
iterationSetupMethod,
iterationCleanupMethod,
attr.Description,
baseline: attr.Baseline,
categories: GetCategories(methodInfo),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ from type in compilerResults.CompiledAssembly.GetTypes()
from benchmark in TypeToBenchmarks(type, config)
let target = benchmark.Target
select new Benchmark(
new Target(target.Type, target.Method, target.GlobalSetupMethod, target.GlobalCleanupMethod, target.MethodDisplayInfo, benchmarkContent, target.Baseline, target.Categories, target.OperationsPerInvoke),
new Target(target.Type, target.Method, target.GlobalSetupMethod, target.GlobalCleanupMethod,
target.IterationSetupMethod, target.IterationCleanupMethod,
target.MethodDisplayInfo, benchmarkContent, target.Baseline, target.Categories, target.OperationsPerInvoke),
benchmark.Job,
benchmark.Parameters)).ToArray();
}
Expand Down
6 changes: 6 additions & 0 deletions src/BenchmarkDotNet.Core/Running/Target.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public class Target
public MethodInfo Method { get; }
public MethodInfo GlobalSetupMethod { get; }
public MethodInfo GlobalCleanupMethod { get; }
public MethodInfo IterationSetupMethod { get; }
public MethodInfo IterationCleanupMethod { get; }
public string AdditionalLogic { get; }
public int OperationsPerInvoke { get; }
public string MethodDisplayInfo { get; }
Expand All @@ -30,6 +32,8 @@ public Target(
MethodInfo method,
MethodInfo globalSetupMethod = null,
MethodInfo globalCleanupMethod = null,
MethodInfo iterationSetupMethod = null,
MethodInfo iterationCleanupMethod = null,
string description = null,
string additionalLogic = null,
bool baseline = false,
Expand All @@ -41,6 +45,8 @@ public Target(
Method = method;
GlobalSetupMethod = globalSetupMethod;
GlobalCleanupMethod = globalCleanupMethod;
IterationSetupMethod = iterationSetupMethod;
IterationCleanupMethod = iterationCleanupMethod;
OperationsPerInvoke = operationsPerInvoke;
AdditionalLogic = additionalLogic ?? string.Empty;
MethodDisplayInfo = FormatDescription(description) ?? method?.Name ?? "Untitled";
Expand Down
10 changes: 9 additions & 1 deletion src/BenchmarkDotNet.Core/Templates/BenchmarkProgram.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ namespace BenchmarkDotNet.Autogenerated
IdleAction = instance.IdleMultiAction,
GlobalSetupAction = instance.globalSetupAction,
GlobalCleanupAction = instance.globalCleanupAction,
IterationSetupAction = instance.iterationSetupAction,
IterationCleanupAction = instance.iterationCleanupAction,
TargetJob = job,
OperationsPerInvoke = $OperationsPerInvoke$
};
Expand All @@ -113,9 +115,11 @@ namespace BenchmarkDotNet.Autogenerated
engine.PreAllocate();

instance?.globalSetupAction();
instance?.iterationSetupAction();
instance?.iterationCleanupAction();

if (job.ResolveValue(RunMode.RunStrategyCharacteristic, EngineResolver.Instance).NeedsJitting())
engine.Jitting(); // does first call to main action, must be executed after globalSetup()!
engine.Jitting(); // does first call to main action, must be executed after globalSetup() and iterationSetup()!

if(host.IsDiagnoserAttached)
host.AfterGlobalSetup();
Expand All @@ -133,12 +137,16 @@ namespace BenchmarkDotNet.Autogenerated
{
globalSetupAction = $GlobalSetupMethodName$;
globalCleanupAction = $GlobalCleanupMethodName$;
iterationSetupAction = $IterationSetupMethodName$;
iterationCleanupAction = $IterationCleanupMethodName$;
idleAction = Idle;
mainAction = $TargetMethodDelegate$;
}

private Action globalSetupAction;
private Action globalCleanupAction;
private Action iterationSetupAction;
private Action iterationCleanupAction;
private $IdleMethodDelegateType$ idleAction;
private $TargetMethodDelegateType$ mainAction;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,20 @@ public static BenchmarkAction CreateGlobalSetup(Target target, object instance)
public static BenchmarkAction CreateGlobalCleanup(Target target, object instance) =>
CreateCore(instance, target.GlobalCleanupMethod, FallbackSignature, BenchmarkActionCodegen.DelegateCombine, 1);

/// <summary>Creates global setup benchmark action.</summary>
/// <param name="target">Target info.</param>
/// <param name="instance">Instance of target.</param>
/// <returns>Setup benchmark action.</returns>
public static BenchmarkAction CreateIterationSetup(Target target, object instance) =>
CreateCore(instance, target.IterationSetupMethod, FallbackSignature, BenchmarkActionCodegen.DelegateCombine, 1);

/// <summary>Creates global cleanup benchmark action.</summary>
/// <param name="target">Target info.</param>
/// <param name="instance">Instance of target.</param>
/// <returns>Cleanup benchmark action.</returns>
public static BenchmarkAction CreateIterationCleanup(Target target, object instance) =>
CreateCore(instance, target.IterationCleanupMethod, FallbackSignature, BenchmarkActionCodegen.DelegateCombine, 1);

/// <summary>Creates a dummy benchmark action.</summary>
/// <returns>Dummy benchmark action.</returns>
public static BenchmarkAction CreateDummy() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public static void RunCore(IHost host, Benchmark benchmark, BenchmarkActionCodeg
var idleAction = BenchmarkActionFactory.CreateIdle(target, instance, codegenMode, unrollFactor);
var globalSetupAction = BenchmarkActionFactory.CreateGlobalSetup(target, instance);
var globalCleanupAction = BenchmarkActionFactory.CreateGlobalCleanup(target, instance);
var iterationSetupAction = BenchmarkActionFactory.CreateIterationSetup(target, instance);
var iterationCleanupAction = BenchmarkActionFactory.CreateIterationCleanup(target, instance);
var dummy1 = BenchmarkActionFactory.CreateDummy();
var dummy2 = BenchmarkActionFactory.CreateDummy();
var dummy3 = BenchmarkActionFactory.CreateDummy();
Expand All @@ -80,6 +82,8 @@ public static void RunCore(IHost host, Benchmark benchmark, BenchmarkActionCodeg
IdleAction = idleAction.InvokeMultiple,
GlobalSetupAction = globalSetupAction.InvokeSingle,
GlobalCleanupAction = globalCleanupAction.InvokeSingle,
IterationSetupAction = iterationSetupAction.InvokeSingle,
IterationCleanupAction = iterationCleanupAction.InvokeSingle,
TargetJob = job,
OperationsPerInvoke = target.OperationsPerInvoke
};
Expand Down
89 changes: 89 additions & 0 deletions tests/BenchmarkDotNet.IntegrationTests/AllSetupAndCleanupTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Engines;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Tests.Loggers;
using Xunit;
using Xunit.Abstractions;

namespace BenchmarkDotNet.IntegrationTests
{
public class AllSetupAndCleanupTest : BenchmarkTestExecutor
{
private const string Prefix = "// ### Called: ";
private const string GlobalSetupCalled = Prefix + "GlobalSetup";
private const string GlobalCleanupCalled = Prefix + "GlobalCleanup";
private const string IterationSetupCalled = Prefix + "IterationSetup";
private const string IterationCleanupCalled = Prefix + "IterationCleanup";
private const string BenchmarkCalled = Prefix + "Benchmark";
private const string OutputDelimeter = "===========================================================";

private readonly string[] expectedLogLines = {

This comment has been minimized.

Copy link
@adamsitnik

adamsitnik May 31, 2017

Member

👍 another good idea!

"// ### Called: GlobalSetup",

"// ### Called: IterationSetup (1)", // IterationSetup Jitting
"// ### Called: IterationCleanup (1)", // IterationCleanup Jitting

"// ### Called: IterationSetup (2)", // MainWarmup1
"// ### Called: Benchmark", // MainWarmup1
"// ### Called: IterationCleanup (2)", // MainWarmup1
"// ### Called: IterationSetup (3)", // MainWarmup2
"// ### Called: Benchmark", // MainWarmup2
"// ### Called: IterationCleanup (3)", // MainWarmup2

"// ### Called: IterationSetup (4)", // MainTarget1
"// ### Called: Benchmark", // MainTarget1
"// ### Called: IterationCleanup (4)", // MainTarget1
"// ### Called: IterationSetup (5)", // MainTarget2
"// ### Called: Benchmark", // MainTarget2
"// ### Called: IterationCleanup (5)", // MainTarget2
"// ### Called: IterationSetup (6)", // MainTarget3
"// ### Called: Benchmark", // MainTarget3
"// ### Called: IterationCleanup (6)", // MainTarget3

"// ### Called: GlobalCleanup"
};

public AllSetupAndCleanupTest(ITestOutputHelper output) : base(output) { }

[Fact]
public void AllSetupAndCleanupMethodRunsTest()
{
var logger = new OutputLogger(Output);
var miniJob = Job.Default.With(RunStrategy.Monitoring).WithWarmupCount(2).WithTargetCount(3).WithInvocationCount(1).WithUnrollFactor(1).WithId("MiniJob");
var config = CreateSimpleConfig(logger, miniJob);

CanExecute<AllSetupAndCleanupAttributeBenchmarks>(config);
Output.WriteLine(OutputDelimeter);
Output.WriteLine(OutputDelimeter);
Output.WriteLine(OutputDelimeter);

var actualLogLines = logger.GetLog().Split('\r', '\n').Where(line => line.StartsWith(Prefix)).ToArray();
foreach (string line in actualLogLines)
Output.WriteLine(line);
Assert.Equal(expectedLogLines, actualLogLines);
}

public class AllSetupAndCleanupAttributeBenchmarks
{
private int setupCounter;
private int cleanupCounter;

[IterationSetup]
public void IterationSetup() => Console.WriteLine(IterationSetupCalled + " (" + ++setupCounter + ")");

[IterationCleanup]
public void IterationCleanup() => Console.WriteLine(IterationCleanupCalled + " (" + ++cleanupCounter + ")");

[GlobalSetup]
public void GlobalSetup() => Console.WriteLine(GlobalSetupCalled);

[GlobalCleanup]
public void GlobalCleanup() => Console.WriteLine(GlobalCleanupCalled);

[Benchmark]
public void Benchmark() => Console.WriteLine(BenchmarkCalled);
}
}
}
Loading

0 comments on commit 21369c2

Please sign in to comment.