Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

have two main actions: with unroll and without, for no unroll icrease the step by 1 in pilot (not *2) #771

Merged
merged 2 commits into from
Jun 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 36 additions & 25 deletions src/BenchmarkDotNet/Engines/EngineFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ public class EngineFactory : IEngineFactory
{
public IEngine CreateReadyToRun(EngineParameters engineParameters)
{
if (engineParameters.MainSingleAction == null)
throw new ArgumentNullException(nameof(engineParameters.MainSingleAction));
if (engineParameters.MainMultiAction == null)
throw new ArgumentNullException(nameof(engineParameters.MainMultiAction));
if (engineParameters.MainActionNoUnroll == null)
throw new ArgumentNullException(nameof(engineParameters.MainActionNoUnroll));
if (engineParameters.MainActionUnroll == null)
throw new ArgumentNullException(nameof(engineParameters.MainActionUnroll));
if (engineParameters.Dummy1Action == null)
throw new ArgumentNullException(nameof(engineParameters.Dummy1Action));
if (engineParameters.Dummy2Action == null)
throw new ArgumentNullException(nameof(engineParameters.Dummy2Action));
if (engineParameters.Dummy3Action == null)
throw new ArgumentNullException(nameof(engineParameters.Dummy3Action));
if (engineParameters.IdleSingleAction == null)
throw new ArgumentNullException(nameof(engineParameters.IdleSingleAction));
if (engineParameters.IdleMultiAction == null)
throw new ArgumentNullException(nameof(engineParameters.IdleMultiAction));
if (engineParameters.IdleActionNoUnroll == null)
throw new ArgumentNullException(nameof(engineParameters.IdleActionNoUnroll));
if (engineParameters.IdleActionUnroll == null)
throw new ArgumentNullException(nameof(engineParameters.IdleActionUnroll));
if(engineParameters.TargetJob == null)
throw new ArgumentNullException(nameof(engineParameters.TargetJob));

Expand All @@ -44,20 +44,34 @@ public IEngine CreateReadyToRun(EngineParameters engineParameters)
}

var singleActionEngine = CreateSingleActionEngine(engineParameters);
if (Jit(singleActionEngine, ++jitIndex, invokeCount: 1, unrollFactor: 1) > engineParameters.IterationTime)
var singleInvocationTime = Jit(singleActionEngine, ++jitIndex, invokeCount: 1, unrollFactor: 1);

if (singleInvocationTime > engineParameters.IterationTime)
return singleActionEngine; // executing once takes longer than iteration time => long running benchmark, needs no pilot and no overhead

var multiActionEngine = CreateMultiActionEngine(engineParameters);
int defaultUnrollFactor = Job.Default.ResolveValue(RunMode.UnrollFactorCharacteristic, EngineParameters.DefaultResolver);

if (Jit(multiActionEngine, ++jitIndex, invokeCount: defaultUnrollFactor, unrollFactor: defaultUnrollFactor) > engineParameters.IterationTime)
{ // executing defaultUnrollFactor times takes longer than iteration time => medium running benchmark, needs no pilot and no overhead
var defaultUnrollFactorTimesPerIterationNoPilotNoOverhead = CreateJobWhichDoesNotNeedPilotAndOverheadEvaluation(engineParameters.TargetJob,
invocationCount: defaultUnrollFactor, unrollFactor: defaultUnrollFactor); // run the benchmark exactly once per iteration
double timesPerIteration = engineParameters.IterationTime / singleInvocationTime; // how many times can we run given benchmark per iteration

if (timesPerIteration < 1.5) // example: IterationTime is 0.5s, but single invocation takes 0.4s => we don't want to run it twice per iteration
return singleActionEngine;

int roundedUpTimesPerIteration = (int)Math.Ceiling(timesPerIteration);

if (roundedUpTimesPerIteration < defaultUnrollFactor) // if we run it defaultUnrollFactor times per iteration, it's going to take longer than IterationTime
{
var needsPilot = engineParameters.TargetJob
.WithUnrollFactor(1) // we don't want to use unroll factor!
.WithMinInvokeCount(2) // the minimum is 2 (not the default 4 which can be too much and not 1 which we already know is not enough)
.WithEvaluateOverhead(false); // it's something very time consuming, it overhead is too small compared to total time

return CreateEngine(engineParameters, defaultUnrollFactorTimesPerIterationNoPilotNoOverhead, engineParameters.IdleMultiAction, engineParameters.MainMultiAction);
return CreateEngine(engineParameters, needsPilot, engineParameters.IdleActionNoUnroll, engineParameters.MainActionNoUnroll);
}

var multiActionEngine = CreateMultiActionEngine(engineParameters);

DeadCodeEliminationHelper.KeepAliveWithoutBoxing(Jit(multiActionEngine, ++jitIndex, invokeCount: defaultUnrollFactor, unrollFactor: defaultUnrollFactor));

return multiActionEngine;
}

Expand All @@ -80,19 +94,16 @@ private static TimeInterval Jit(Engine engine, int jitIndex, int invokeCount, in
}

private static Engine CreateMultiActionEngine(EngineParameters engineParameters)
=> CreateEngine(engineParameters, engineParameters.TargetJob, engineParameters.IdleMultiAction, engineParameters.MainMultiAction);
=> CreateEngine(engineParameters, engineParameters.TargetJob, engineParameters.IdleActionUnroll, engineParameters.MainActionUnroll);

private static Engine CreateSingleActionEngine(EngineParameters engineParameters)
=> CreateEngine(engineParameters,
CreateJobWhichDoesNotNeedPilotAndOverheadEvaluation(engineParameters.TargetJob, invocationCount: 1, unrollFactor: 1), // run the benchmark exactly once per iteration
engineParameters.IdleSingleAction,
engineParameters.MainSingleAction);

private static Job CreateJobWhichDoesNotNeedPilotAndOverheadEvaluation(Job sourceJob, int invocationCount, int unrollFactor)
=> sourceJob
.WithInvocationCount(invocationCount).WithUnrollFactor(unrollFactor)
.WithEvaluateOverhead(false); // it's very time consuming, don't evaluate the overhead which would be 0,000025% of the target run or even less
// todo: consider if we should set the warmup count to 2
engineParameters.TargetJob
.WithInvocationCount(1).WithUnrollFactor(1) // run the benchmark exactly once per iteration
.WithEvaluateOverhead(false), // it's something very time consuming, it overhead is too small compared to total time
// todo: consider if we should set the warmup count to 2
engineParameters.IdleActionNoUnroll,
engineParameters.MainActionNoUnroll);

private static Engine CreateEngine(EngineParameters engineParameters, Job job, Action<long> idle, Action<long> main)
=> new Engine(
Expand Down
8 changes: 4 additions & 4 deletions src/BenchmarkDotNet/Engines/EngineParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ public class EngineParameters
public static readonly IResolver DefaultResolver = new CompositeResolver(BenchmarkRunner.DefaultResolver, EngineResolver.Instance);

public IHost Host { get; set; }
public Action<long> MainSingleAction { get; set; }
public Action<long> MainMultiAction { get; set; }
public Action<long> MainActionNoUnroll { get; set; }
public Action<long> MainActionUnroll { get; set; }
public Action Dummy1Action { get; set; }
public Action Dummy2Action { get; set; }
public Action Dummy3Action { get; set; }
public Action<long> IdleSingleAction { get; set; }
public Action<long> IdleMultiAction { get; set; }
public Action<long> IdleActionNoUnroll { get; set; }
public Action<long> IdleActionUnroll { get; set; }
public Job TargetJob { get; set; } = Job.Default;
public long OperationsPerInvoke { get; set; } = 1;
public Action GlobalSetupAction { get; set; }
Expand Down
6 changes: 5 additions & 1 deletion src/BenchmarkDotNet/Engines/EnginePilotStage.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Horology;
using BenchmarkDotNet.Jobs;

Expand Down Expand Up @@ -67,7 +68,10 @@ private long RunAuto()
if (invokeCount >= MaxInvokeCount)
break;

invokeCount *= 2;
if (unrollFactor == 1 && invokeCount < EnvResolver.DefaultUnrollFactorForThroughput)

This comment was marked as spam.

This comment was marked as spam.

invokeCount += 1;
else
invokeCount *= 2;
}
WriteLine();

Expand Down
4 changes: 3 additions & 1 deletion src/BenchmarkDotNet/Environments/EnvResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace BenchmarkDotNet.Environments
{
public class EnvResolver : Resolver
{
public const int DefaultUnrollFactorForThroughput = 16;

public static readonly IResolver Instance = new CompositeResolver(new EnvResolver(), GcResolver.Instance);

private EnvResolver()
Expand All @@ -26,7 +28,7 @@ private EnvResolver()
switch (strategy)
{
case RunStrategy.Throughput:
return 16;
return DefaultUnrollFactorForThroughput;
case RunStrategy.ColdStart:
case RunStrategy.Monitoring:
return 1;
Expand Down
80 changes: 52 additions & 28 deletions src/BenchmarkDotNet/Templates/BenchmarkType.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@
var engineParameters = new BenchmarkDotNet.Engines.EngineParameters()
{
Host = host,
MainMultiAction = instance.MainMultiAction,
MainSingleAction = instance.MainSingleAction,
MainActionUnroll = instance.MainActionUnroll,
MainActionNoUnroll = instance.MainActionNoUnroll,
Dummy1Action = instance.Dummy1,
Dummy2Action = instance.Dummy2,
Dummy3Action = instance.Dummy3,
IdleSingleAction = instance.IdleSingleAction,
IdleMultiAction = instance.IdleMultiAction,
IdleActionNoUnroll = instance.IdleActionNoUnroll,
IdleActionUnroll = instance.IdleActionUnroll,
GlobalSetupAction = instance.globalSetupAction,
GlobalCleanupAction = instance.globalCleanupAction,
IterationSetupAction = instance.iterationSetupAction,
Expand Down Expand Up @@ -112,7 +112,7 @@

private Consumer consumer = new Consumer();

private void IdleMultiAction(long invokeCount)
private void IdleActionUnroll(long invokeCount)
{
$LoadArguments$
for (long i = 0; i < invokeCount; i++)
Expand All @@ -121,13 +121,16 @@
}
}

private void IdleSingleAction(long _)
private void IdleActionNoUnroll(long invokeCount)
{
$LoadArguments$
consumer.Consume(idleDelegate($PassArguments$));
for (long i = 0; i < invokeCount; i++)
{
consumer.Consume(idleDelegate($PassArguments$));
}
}

private void MainMultiAction(long invokeCount)
private void MainActionUnroll(long invokeCount)
{
$LoadArguments$
for (long i = 0; i < invokeCount; i++)
Expand All @@ -136,10 +139,13 @@
}
}

private void MainSingleAction(long _)
private void MainActionNoUnroll(long invokeCount)
{
$LoadArguments$
consumer.Consume(targetDelegate($PassArguments$)$ConsumeField$);
for (long i = 0; i < invokeCount; i++)
{
consumer.Consume(targetDelegate($PassArguments$)$ConsumeField$);
}
}

[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
Expand All @@ -156,7 +162,7 @@

#elif RETURNS_NON_CONSUMABLE_STRUCT_$ID$

private void IdleMultiAction(long invokeCount)
private void IdleActionUnroll(long invokeCount)
{
$LoadArguments$
$IdleMethodReturnTypeName$ result = default($IdleMethodReturnTypeName$);
Expand All @@ -167,15 +173,18 @@
DeadCodeEliminationHelper.KeepAliveWithoutBoxing(result);
}

private void IdleSingleAction(long _)
private void IdleActionNoUnroll(long invokeCount)
{
$LoadArguments$
$IdleMethodReturnTypeName$ result = default($IdleMethodReturnTypeName$);
result = idleDelegate($PassArguments$);
for (long i = 0; i < invokeCount; i++)
{
result = idleDelegate($PassArguments$);
}
DeadCodeEliminationHelper.KeepAliveWithoutBoxing(result);
}

private void MainMultiAction(long invokeCount)
private void MainActionUnroll(long invokeCount)
{
$LoadArguments$
$TargetMethodReturnType$ result = default($TargetMethodReturnType$);
Expand All @@ -186,11 +195,14 @@
NonGenericKeepAliveWithoutBoxing(result);
}

private void MainSingleAction(long _)
private void MainActionNoUnroll(long invokeCount)
{
$LoadArguments$
$TargetMethodReturnType$ result = default($TargetMethodReturnType$);
result = targetDelegate($PassArguments$);
for (long i = 0; i < invokeCount; i++)
{
result = targetDelegate($PassArguments$);
}
NonGenericKeepAliveWithoutBoxing(result);
}

Expand All @@ -213,7 +225,7 @@

#elif RETURNS_BYREF_$ID$

private void IdleMultiAction(long invokeCount)
private void IdleActionUnroll(long invokeCount)
{
$LoadArguments$
$IdleMethodReturnTypeName$ value = default($IdleMethodReturnTypeName$);
Expand All @@ -224,17 +236,20 @@
DeadCodeEliminationHelper.KeepAliveWithoutBoxing(value);
}

private void IdleSingleAction(long _)
private void IdleActionNoUnroll(long invokeCount)
{
$LoadArguments$
$IdleMethodReturnTypeName$ value = default($IdleMethodReturnTypeName$);
value = idleDelegate($PassArguments$);
for (long i = 0; i < invokeCount; i++)
{
value = idleDelegate($PassArguments$);
}
DeadCodeEliminationHelper.KeepAliveWithoutBoxing(value);
}

private $TargetMethodReturnType$ mainDefaultValueHolder = default($TargetMethodReturnType$);

private void MainMultiAction(long invokeCount)
private void MainActionUnroll(long invokeCount)
{
$LoadArguments$
ref $TargetMethodReturnType$ alias = ref mainDefaultValueHolder;
Expand All @@ -245,11 +260,14 @@
DeadCodeEliminationHelper.KeepAliveWithoutBoxing(ref alias);
}

private void MainSingleAction(long _)
private void MainActionNoUnroll(long invokeCount)
{
$LoadArguments$
ref $TargetMethodReturnType$ alias = ref mainDefaultValueHolder;
alias = targetDelegate($PassArguments$);
for (long i = 0; i < invokeCount; i++)
{
alias = targetDelegate($PassArguments$);
}
DeadCodeEliminationHelper.KeepAliveWithoutBoxing(ref alias);
}

Expand All @@ -266,7 +284,7 @@
}
#elif RETURNS_VOID_$ID$

private void IdleMultiAction(long invokeCount)
private void IdleActionUnroll(long invokeCount)
{
$LoadArguments$
for (long i = 0; i < invokeCount; i++)
Expand All @@ -275,13 +293,16 @@
}
}

private void IdleSingleAction(long _)
private void IdleActionNoUnroll(long invokeCount)
{
$LoadArguments$
idleDelegate($PassArguments$);
for (long i = 0; i < invokeCount; i++)
{
idleDelegate($PassArguments$);
}
}

private void MainMultiAction(long invokeCount)
private void MainActionUnroll(long invokeCount)
{
$LoadArguments$
for (long i = 0; i < invokeCount; i++)
Expand All @@ -290,10 +311,13 @@
}
}

private void MainSingleAction(long _)
private void MainActionNoUnroll(long invokeCount)
{
$LoadArguments$
targetDelegate($PassArguments$);
for (long i = 0; i < invokeCount; i++)
{
targetDelegate($PassArguments$);
}
}

[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
Expand Down
16 changes: 12 additions & 4 deletions src/BenchmarkDotNet/Toolchains/InProcess/InProcessRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,21 @@ public static void RunCore(IHost host, Benchmark benchmark, BenchmarkActionCodeg
var engineParameters = new EngineParameters
{
Host = host,
MainSingleAction = _ => mainAction.InvokeSingle(),
MainMultiAction = mainAction.InvokeMultiple,
MainActionNoUnroll = invocationCount =>
{
for (int i = 0; i < invocationCount; i++)
mainAction.InvokeSingle();
},
MainActionUnroll = mainAction.InvokeMultiple,
Dummy1Action = dummy1.InvokeSingle,
Dummy2Action = dummy2.InvokeSingle,
Dummy3Action = dummy3.InvokeSingle,
IdleSingleAction = _ => idleAction.InvokeSingle(),
IdleMultiAction = idleAction.InvokeMultiple,
IdleActionNoUnroll = invocationCount =>
{
for (int i = 0; i < invocationCount; i++)
idleAction.InvokeSingle();
},
IdleActionUnroll = idleAction.InvokeMultiple,
GlobalSetupAction = globalSetupAction.InvokeSingle,
GlobalCleanupAction = globalCleanupAction.InvokeSingle,
IterationSetupAction = iterationSetupAction.InvokeSingle,
Expand Down
Loading