Introduce UnrollFactor
This commit is contained in:
Родитель
2a6578034b
Коммит
03fb04c7a0
|
@ -41,7 +41,8 @@ In this category, you can specifiy how to benchmark each method.
|
|||
* `WarmupCount`: how many warmup iterations should be performed
|
||||
* `TargetCount`: how many target iterations should be performed
|
||||
* `IterationTime`: desired time of a single iteration
|
||||
* `InvocationCount`: count of invocation in a single iteration (if specified, `IterationTime` will be ignored)
|
||||
* `UnrollFactor`: how many times the benchmark method will be invoked per one iteration of a generated loop
|
||||
* `InvocationCount`: count of invocation in a single iteration (if specified, `IterationTime` will be ignored), must be a multiple of `UnrollFactor`
|
||||
|
||||
Usually, you shouldn't specify such characteristics like `LaunchCount`, `WarmupCount`, `TargetCount`, or `IterationTime` because BenchmarkDotNet has a smart algorithm to choose these values automatically based on recieved measurements. You can specify it for testing purposes or when you are damn sure that you know perfect characteristics for your benchmark (when you set `TargetCount` = `20` you should unserstand why `20` is a good value for your case).
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
using BenchmarkDotNet.Attributes;
|
||||
|
||||
namespace BenchmarkDotNet.Samples.Intro
|
||||
{
|
||||
public class IntroEmptyMethods
|
||||
{
|
||||
[Benchmark]
|
||||
public void Empty1()
|
||||
{
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Empty2()
|
||||
{
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Empty3()
|
||||
{
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Empty4()
|
||||
{
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Empty5()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,8 @@ using BenchmarkDotNet.Attributes.Jobs;
|
|||
|
||||
namespace BenchmarkDotNet.Samples.Intro
|
||||
{
|
||||
[DryJob]
|
||||
[ShortRunJob]
|
||||
[KeepBenchmarkFiles()]
|
||||
public class IntroJobsFull
|
||||
{
|
||||
[Benchmark(Baseline = true)]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
@ -6,6 +7,7 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Characteristics;
|
||||
using BenchmarkDotNet.Core.Helpers;
|
||||
using BenchmarkDotNet.Environments;
|
||||
using BenchmarkDotNet.Extensions;
|
||||
using BenchmarkDotNet.Helpers;
|
||||
using BenchmarkDotNet.Running;
|
||||
|
@ -18,7 +20,7 @@ namespace BenchmarkDotNet.Code
|
|||
{
|
||||
var provider = GetDeclarationsProvider(benchmark.Target);
|
||||
|
||||
return new SmartStringBuilder(ResourceHelper.LoadTemplate("BenchmarkProgram.txt")).
|
||||
string text = new SmartStringBuilder(ResourceHelper.LoadTemplate("BenchmarkProgram.txt")).
|
||||
Replace("$OperationsPerInvoke$", provider.OperationsPerInvoke).
|
||||
Replace("$TargetTypeNamespace$", provider.TargetTypeNamespace).
|
||||
Replace("$TargetMethodReturnTypeNamespace$", provider.TargetMethodReturnTypeNamespace).
|
||||
|
@ -35,6 +37,29 @@ namespace BenchmarkDotNet.Code
|
|||
Replace("$JobSetDefinition$", GetJobsSetDefinition(benchmark)).
|
||||
Replace("$ParamsContent$", GetParamsContent(benchmark)).
|
||||
ToString();
|
||||
|
||||
text = Unroll(text, benchmark.Job.Run.UnrollFactor.Resolve(EnvResolver.Instance));
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
private static string Unroll(string text, int factor)
|
||||
{
|
||||
const string unrollDirective = "@Unroll@";
|
||||
var oldLines = text.Split('\n');
|
||||
var newLines = new List<string>();
|
||||
foreach (string line in oldLines)
|
||||
{
|
||||
if (line.Contains(unrollDirective))
|
||||
{
|
||||
string newLine = line.Replace(unrollDirective, "");
|
||||
for (int i = 0; i < factor; i++)
|
||||
newLines.Add(newLine);
|
||||
}
|
||||
else
|
||||
newLines.Add(line);
|
||||
}
|
||||
return string.Join("\n", newLines);
|
||||
}
|
||||
|
||||
private static string GetJobsSetDefinition(Benchmark benchmark)
|
||||
|
|
|
@ -48,6 +48,7 @@ namespace BenchmarkDotNet.Configs
|
|||
{
|
||||
yield return BaselineValidator.FailOnError;
|
||||
yield return JitOptimizationsValidator.DontFailOnError;
|
||||
yield return UnrollFactorValidator.Default;
|
||||
}
|
||||
|
||||
public IEnumerable<Job> GetJobs() => Enumerable.Empty<Job>();
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace BenchmarkDotNet.Engines
|
|||
public class Engine : IEngine
|
||||
{
|
||||
public const int MinInvokeCount = 4;
|
||||
public const int MinIterationTimeMs = 200;
|
||||
public static readonly TimeInterval MinIterationTime = 200 * TimeInterval.Millisecond;
|
||||
|
||||
public Job TargetJob { get; set; } = Job.Default;
|
||||
public long OperationsPerInvoke { get; set; } = 1;
|
||||
|
@ -47,6 +47,7 @@ namespace BenchmarkDotNet.Engines
|
|||
Jitting();
|
||||
|
||||
long invokeCount = 1;
|
||||
int unrollFactor = TargetJob.Run.UnrollFactor.Resolve(Resolver);
|
||||
IList<Measurement> idle = null;
|
||||
|
||||
if (TargetJob.Run.RunStrategy.Resolve(Resolver) != RunStrategy.ColdStart)
|
||||
|
@ -55,13 +56,13 @@ namespace BenchmarkDotNet.Engines
|
|||
|
||||
if (TargetJob.Accuracy.EvaluateOverhead.Resolve(Resolver))
|
||||
{
|
||||
warmupStage.RunIdle(invokeCount);
|
||||
idle = targetStage.RunIdle(invokeCount);
|
||||
warmupStage.RunIdle(invokeCount, unrollFactor);
|
||||
idle = targetStage.RunIdle(invokeCount, unrollFactor);
|
||||
}
|
||||
|
||||
warmupStage.RunMain(invokeCount);
|
||||
warmupStage.RunMain(invokeCount, unrollFactor);
|
||||
}
|
||||
var main = targetStage.RunMain(invokeCount);
|
||||
var main = targetStage.RunMain(invokeCount, unrollFactor);
|
||||
|
||||
// TODO: Move calculation of the result measurements to a separated class
|
||||
PrintResult(idle, main);
|
||||
|
@ -78,7 +79,8 @@ namespace BenchmarkDotNet.Engines
|
|||
private void PrintResult(IList<Measurement> idle, IList<Measurement> main)
|
||||
{
|
||||
// TODO: use Accuracy.RemoveOutliers
|
||||
var overhead = idle == null ? 0.0 : new Statistics(idle.Select(m => m.Nanoseconds)).Median;
|
||||
// TODO: check if resulted measurements are too small (like < 0.1ns)
|
||||
double overhead = idle == null ? 0.0 : new Statistics(idle.Select(m => m.Nanoseconds)).Median;
|
||||
int resultIndex = 0;
|
||||
foreach (var measurement in main)
|
||||
{
|
||||
|
@ -97,7 +99,8 @@ namespace BenchmarkDotNet.Engines
|
|||
{
|
||||
// Initialization
|
||||
long invokeCount = data.InvokeCount;
|
||||
var totalOperations = invokeCount * OperationsPerInvoke;
|
||||
int unrollFactor = data.UnrollFactor;
|
||||
long totalOperations = invokeCount * OperationsPerInvoke;
|
||||
var action = data.IterationMode.IsIdle() ? IdleAction : MainAction;
|
||||
|
||||
// Setup
|
||||
|
@ -106,7 +109,7 @@ namespace BenchmarkDotNet.Engines
|
|||
|
||||
// Measure
|
||||
var clock = TargetJob.Infrastructure.Clock.Resolve(Resolver).Start();
|
||||
action(invokeCount);
|
||||
action(invokeCount / unrollFactor);
|
||||
var clockSpan = clock.Stop();
|
||||
|
||||
// Cleanup
|
||||
|
|
|
@ -28,9 +28,12 @@ namespace BenchmarkDotNet.Engines
|
|||
/// </summary>
|
||||
private long RunAuto()
|
||||
{
|
||||
long invokeCount = TargetAccuracy.MinInvokeCount.Resolve(Resolver);
|
||||
int unrollFactor = TargetJob.Run.UnrollFactor.Resolve(Resolver);
|
||||
Func<long, long> autocorrect = count => (count + unrollFactor - 1) / unrollFactor * unrollFactor;
|
||||
|
||||
long invokeCount = autocorrect(TargetAccuracy.MinInvokeCount.Resolve(Resolver));
|
||||
double maxError = TargetAccuracy.MaxStdErrRelative.Resolve(Resolver); // TODO: introduce a StdErr factor
|
||||
double minIterationTome = TimeUnit.Convert(Engine.MinIterationTimeMs, TimeUnit.Millisecond, TimeUnit.Nanosecond);
|
||||
double minIterationTime = Engine.MinIterationTime.Nanoseconds;
|
||||
|
||||
double resolution = TargetClock.GetResolution().Nanoseconds;
|
||||
|
||||
|
@ -38,12 +41,12 @@ namespace BenchmarkDotNet.Engines
|
|||
while (true)
|
||||
{
|
||||
iterationCounter++;
|
||||
var measurement = RunIteration(IterationMode.Pilot, iterationCounter, invokeCount);
|
||||
double iterationTime = measurement.Nanoseconds;
|
||||
var measurement = RunIteration(IterationMode.Pilot, iterationCounter, invokeCount, unrollFactor);
|
||||
double iterationTime = measurement.Nanoseconds;
|
||||
double operationError = 2.0 * resolution / invokeCount; // An operation error which has arisen due to the Chronometer precision
|
||||
double operationMaxError = iterationTime / invokeCount * maxError; // Max acceptable operation error
|
||||
|
||||
bool isFinished = operationError < operationMaxError && iterationTime >= minIterationTome;
|
||||
bool isFinished = operationError < operationMaxError && iterationTime >= minIterationTime;
|
||||
if (isFinished)
|
||||
break;
|
||||
if (invokeCount >= MaxInvokeCount)
|
||||
|
@ -61,7 +64,10 @@ namespace BenchmarkDotNet.Engines
|
|||
/// </summary>
|
||||
private long RunSpecific()
|
||||
{
|
||||
long invokeCount = Engine.MinInvokeCount;
|
||||
int unrollFactor = TargetJob.Run.UnrollFactor.Resolve(Resolver);
|
||||
Func<long, long> autocorrect = count => (count + unrollFactor - 1) / unrollFactor * unrollFactor;
|
||||
|
||||
long invokeCount = autocorrect(Engine.MinInvokeCount);
|
||||
double targetIterationTime = TargetJob.Run.IterationTime.Resolve(Resolver).ToNanoseconds();
|
||||
int iterationCounter = 0;
|
||||
|
||||
|
@ -69,9 +75,9 @@ namespace BenchmarkDotNet.Engines
|
|||
while (true)
|
||||
{
|
||||
iterationCounter++;
|
||||
var measurement = RunIteration(IterationMode.Pilot, iterationCounter, invokeCount);
|
||||
var measurement = RunIteration(IterationMode.Pilot, iterationCounter, invokeCount, unrollFactor);
|
||||
double actualIterationTime = measurement.Nanoseconds;
|
||||
long newInvokeCount = Math.Max(TargetAccuracy.MinInvokeCount.Resolve(Resolver), (long) Math.Round(invokeCount * targetIterationTime / actualIterationTime));
|
||||
long newInvokeCount = autocorrect(Math.Max(TargetAccuracy.MinInvokeCount.Resolve(Resolver), (long) Math.Round(invokeCount * targetIterationTime / actualIterationTime)));
|
||||
|
||||
if (newInvokeCount < invokeCount)
|
||||
downCount++;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using BenchmarkDotNet.Characteristics;
|
||||
using System;
|
||||
using BenchmarkDotNet.Characteristics;
|
||||
using BenchmarkDotNet.Horology;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using BenchmarkDotNet.Reports;
|
||||
|
@ -19,9 +20,11 @@ namespace BenchmarkDotNet.Engines
|
|||
protected IClock TargetClock => engine.Resolver.Resolve(TargetJob.Infrastructure.Clock);
|
||||
protected IResolver Resolver => engine.Resolver;
|
||||
|
||||
protected Measurement RunIteration(IterationMode mode, int index, long invokeCount)
|
||||
protected Measurement RunIteration(IterationMode mode, int index, long invokeCount, int unrollFactor)
|
||||
{
|
||||
return engine.RunIteration(new IterationData(mode, index, invokeCount));
|
||||
if (invokeCount % unrollFactor != 0)
|
||||
throw new ArgumentOutOfRangeException($"InvokeCount({invokeCount}) should be a multiple of UnrollFactor({unrollFactor}).");
|
||||
return engine.RunIteration(new IterationData(mode, index, invokeCount, unrollFactor));
|
||||
}
|
||||
|
||||
protected void WriteLine() => engine.WriteLine();
|
||||
|
|
|
@ -18,17 +18,17 @@ namespace BenchmarkDotNet.Engines
|
|||
{
|
||||
}
|
||||
|
||||
public List<Measurement> Run(long invokeCount, IterationMode iterationMode, ICharacteristic<int> iterationCount)
|
||||
public List<Measurement> Run(long invokeCount, IterationMode iterationMode, ICharacteristic<int> iterationCount, int unrollFactor)
|
||||
{
|
||||
return iterationCount.IsDefault
|
||||
? RunAuto(invokeCount, iterationMode)
|
||||
: RunSpecific(invokeCount, iterationMode, iterationCount.SpecifiedValue);
|
||||
? RunAuto(invokeCount, iterationMode, unrollFactor)
|
||||
: RunSpecific(invokeCount, iterationMode, iterationCount.SpecifiedValue, unrollFactor);
|
||||
}
|
||||
|
||||
public List<Measurement> RunIdle(long invokeCount) => Run(invokeCount, IterationMode.IdleTarget, TargetJob.Run.TargetCount.MakeDefault());
|
||||
public List<Measurement> RunMain(long invokeCount) => Run(invokeCount, IterationMode.MainTarget, TargetJob.Run.TargetCount);
|
||||
public List<Measurement> RunIdle(long invokeCount, int unrollFactor) => Run(invokeCount, IterationMode.IdleTarget, TargetJob.Run.TargetCount.MakeDefault(), unrollFactor);
|
||||
public List<Measurement> RunMain(long invokeCount, int unrollFactor) => Run(invokeCount, IterationMode.MainTarget, TargetJob.Run.TargetCount, unrollFactor);
|
||||
|
||||
private List<Measurement> RunAuto(long invokeCount, IterationMode iterationMode)
|
||||
private List<Measurement> RunAuto(long invokeCount, IterationMode iterationMode, int unrollFactor)
|
||||
{
|
||||
var measurements = new List<Measurement>();
|
||||
int iterationCounter = 0;
|
||||
|
@ -37,7 +37,7 @@ namespace BenchmarkDotNet.Engines
|
|||
while (true)
|
||||
{
|
||||
iterationCounter++;
|
||||
var measurement = RunIteration(iterationMode, iterationCounter, invokeCount);
|
||||
var measurement = RunIteration(iterationMode, iterationCounter, invokeCount, unrollFactor);
|
||||
measurements.Add(measurement);
|
||||
|
||||
var statistics = new Statistics(measurements.Select(m => m.Nanoseconds));
|
||||
|
@ -56,11 +56,11 @@ namespace BenchmarkDotNet.Engines
|
|||
return measurements;
|
||||
}
|
||||
|
||||
private List<Measurement> RunSpecific(long invokeCount, IterationMode iterationMode, int iterationCount)
|
||||
private List<Measurement> RunSpecific(long invokeCount, IterationMode iterationMode, int iterationCount, int unrollFactor)
|
||||
{
|
||||
var measurements = new List<Measurement>();
|
||||
for (int i = 0; i < iterationCount; i++)
|
||||
measurements.Add(RunIteration(iterationMode, i + 1, invokeCount));
|
||||
measurements.Add(RunIteration(iterationMode, i + 1, invokeCount, unrollFactor));
|
||||
WriteLine();
|
||||
return measurements;
|
||||
}
|
||||
|
|
|
@ -15,24 +15,24 @@ namespace BenchmarkDotNet.Engines
|
|||
{
|
||||
}
|
||||
|
||||
public List<Measurement> Run(long invokeCount, IterationMode iterationMode, ICharacteristic<int> iterationCount)
|
||||
public List<Measurement> Run(long invokeCount, IterationMode iterationMode, ICharacteristic<int> iterationCount, int unrollFactor)
|
||||
{
|
||||
return iterationCount.IsDefault
|
||||
? RunAuto(invokeCount, iterationMode)
|
||||
: RunSpecific(invokeCount, iterationMode, iterationCount.SpecifiedValue);
|
||||
? RunAuto(invokeCount, iterationMode, unrollFactor)
|
||||
: RunSpecific(invokeCount, iterationMode, iterationCount.SpecifiedValue, unrollFactor);
|
||||
}
|
||||
|
||||
public void RunIdle(long invokeCount) => Run(invokeCount, IterationMode.IdleWarmup, TargetJob.Run.WarmupCount.MakeDefault());
|
||||
public void RunMain(long invokeCount) => Run(invokeCount, IterationMode.MainWarmup, TargetJob.Run.WarmupCount);
|
||||
public void RunIdle(long invokeCount, int unrollFactor) => Run(invokeCount, IterationMode.IdleWarmup, TargetJob.Run.WarmupCount.MakeDefault(), unrollFactor);
|
||||
public void RunMain(long invokeCount, int unrollFactor) => Run(invokeCount, IterationMode.MainWarmup, TargetJob.Run.WarmupCount, unrollFactor);
|
||||
|
||||
private List<Measurement> RunAuto(long invokeCount, IterationMode iterationMode)
|
||||
private List<Measurement> RunAuto(long invokeCount, IterationMode iterationMode, int unrollFactor)
|
||||
{
|
||||
int iterationCounter = 0;
|
||||
var measurements = new List<Measurement>(MaxIterationCount);
|
||||
while (true)
|
||||
{
|
||||
iterationCounter++;
|
||||
measurements.Add(RunIteration(iterationMode, iterationCounter, invokeCount));
|
||||
measurements.Add(RunIteration(iterationMode, iterationCounter, invokeCount, unrollFactor));
|
||||
if (IsWarmupFinished(measurements, iterationMode))
|
||||
break;
|
||||
}
|
||||
|
@ -40,11 +40,11 @@ namespace BenchmarkDotNet.Engines
|
|||
return measurements;
|
||||
}
|
||||
|
||||
private List<Measurement> RunSpecific(long invokeCount, IterationMode iterationMode, int iterationCount)
|
||||
private List<Measurement> RunSpecific(long invokeCount, IterationMode iterationMode, int iterationCount, int unrollFactor)
|
||||
{
|
||||
var measurements = new List<Measurement>(iterationCount);
|
||||
for (int i = 0; i < iterationCount; i++)
|
||||
measurements.Add(RunIteration(iterationMode, i + 1, invokeCount));
|
||||
measurements.Add(RunIteration(iterationMode, i + 1, invokeCount, unrollFactor));
|
||||
WriteLine();
|
||||
return measurements;
|
||||
}
|
||||
|
|
|
@ -5,12 +5,14 @@
|
|||
public IterationMode IterationMode { get; }
|
||||
public int Index { get; }
|
||||
public long InvokeCount { get; }
|
||||
public int UnrollFactor { get; }
|
||||
|
||||
public IterationData(IterationMode iterationMode, int index, long invokeCount)
|
||||
public IterationData(IterationMode iterationMode, int index, long invokeCount, int unrollFactor)
|
||||
{
|
||||
IterationMode = iterationMode;
|
||||
Index = index;
|
||||
InvokeCount = invokeCount;
|
||||
UnrollFactor = unrollFactor;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,8 @@ namespace BenchmarkDotNet.Environments
|
|||
// TODO: find a better place
|
||||
var acc = Job.Default.Accuracy;
|
||||
Register(acc.AnaylyzeLaunchVariance, () => false);
|
||||
var run = Job.Default.Run;
|
||||
Register(run.UnrollFactor, () => 16);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ namespace BenchmarkDotNet.Jobs
|
|||
public static Job WithTargetCount(this Job job, int count) => job.With(job.Run.TargetCount.Mutate(count));
|
||||
public static Job WithIterationTime(this Job job, TimeInterval time) => job.With(job.Run.IterationTime.Mutate(time));
|
||||
public static Job WithInvocationCount(this Job job, int count) => job.With(job.Run.InvocationCount.Mutate(count));
|
||||
public static Job WithUnrollFactor(this Job job, int factor) => job.With(job.Run.UnrollFactor.Mutate(factor));
|
||||
|
||||
// Infrastructure
|
||||
public static Job With(this Job job, IToolchain toolchain) => job.With(job.Infrastructure.Toolchain.Mutate(toolchain));
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace BenchmarkDotNet.Jobs
|
|||
{
|
||||
public static readonly RunMode Default = new RunMode();
|
||||
|
||||
public static readonly JobMutator Dry = CreateMutator(nameof(Dry), 1, 1, 1, Engines.RunStrategy.ColdStart);
|
||||
public static readonly JobMutator Dry = CreateMutator(nameof(Dry), 1, 1, 1, Engines.RunStrategy.ColdStart).Add(Default.UnrollFactor.Mutate(1));
|
||||
public static readonly JobMutator Short = CreateMutator(nameof(Short), 1, 3, 3);
|
||||
public static readonly JobMutator Medium = CreateMutator(nameof(Medium), 2, 10, 15);
|
||||
public static readonly JobMutator Long = CreateMutator(nameof(Long), 3, 15, 100);
|
||||
|
@ -17,22 +17,24 @@ namespace BenchmarkDotNet.Jobs
|
|||
private static ICharacteristic<T> Create<T>(string id) => Characteristic<T>.Create("Run", id);
|
||||
|
||||
/// <summary>
|
||||
/// RunStrategy
|
||||
/// Available values: Throughput and ColdStart.
|
||||
/// Throughput: default strategy which allows to get good precision level.
|
||||
/// ColdStart: should be used only for measuring cold start of the application or testing purpose.
|
||||
/// </summary>
|
||||
public ICharacteristic<RunStrategy> RunStrategy { get; private set; } = Create<RunStrategy>(nameof(RunStrategy));
|
||||
|
||||
/// <summary>
|
||||
/// LaunchCount
|
||||
/// How many times we should launch process with target benchmark.
|
||||
/// </summary>
|
||||
public ICharacteristic<int> LaunchCount { get; private set; } = Create<int>(nameof(LaunchCount));
|
||||
|
||||
/// <summary>
|
||||
/// WarmupCount
|
||||
/// How many warmup iterations should be performed.
|
||||
/// </summary>
|
||||
public ICharacteristic<int> WarmupCount { get; private set; } = Create<int>(nameof(WarmupCount));
|
||||
|
||||
/// <summary>
|
||||
/// TargetCount
|
||||
/// How many target iterations should be performed
|
||||
/// </summary>
|
||||
public ICharacteristic<int> TargetCount { get; private set; } = Create<int>(nameof(TargetCount));
|
||||
|
||||
|
@ -42,10 +44,17 @@ namespace BenchmarkDotNet.Jobs
|
|||
public ICharacteristic<TimeInterval> IterationTime { get; private set; } = Create<TimeInterval>(nameof(IterationTime));
|
||||
|
||||
/// <summary>
|
||||
/// Invocation count in a single iteration. If specified, <see cref="IterationTime"/> will be ignored.
|
||||
/// Invocation count in a single iteration.
|
||||
/// If specified, <see cref="IterationTime"/> will be ignored.
|
||||
/// If specified, it must be a multiple of <see cref="UnrollFactor"/>.
|
||||
/// </summary>
|
||||
public ICharacteristic<int> InvocationCount { get; private set; } = Create<int>(nameof(InvocationCount));
|
||||
|
||||
/// <summary>
|
||||
/// How many times the benchmark method will be invoked per one iteration of a generated loop.
|
||||
/// </summary>
|
||||
public ICharacteristic<int> UnrollFactor { get; private set; }= Create<int>(nameof(UnrollFactor));
|
||||
|
||||
public static JobMutator CreateMutator(string id, int launchCount, int warmupCount, int targetCount,
|
||||
RunStrategy strategy = Engines.RunStrategy.Throughput)
|
||||
{
|
||||
|
@ -66,6 +75,7 @@ namespace BenchmarkDotNet.Jobs
|
|||
mode.TargetCount = mode.TargetCount.Mutate(set);
|
||||
mode.IterationTime = mode.IterationTime.Mutate(set);
|
||||
mode.InvocationCount = mode.InvocationCount.Mutate(set);
|
||||
mode.UnrollFactor = mode.UnrollFactor.Mutate(set);
|
||||
return mode;
|
||||
}
|
||||
|
||||
|
@ -75,7 +85,8 @@ namespace BenchmarkDotNet.Jobs
|
|||
WarmupCount,
|
||||
TargetCount,
|
||||
IterationTime,
|
||||
InvocationCount
|
||||
InvocationCount,
|
||||
UnrollFactor
|
||||
);
|
||||
}
|
||||
}
|
|
@ -84,13 +84,17 @@ namespace BenchmarkDotNet.Autogenerated
|
|||
private void IdleMultiAction(long invokeCount)
|
||||
{
|
||||
for (long i = 0; i < invokeCount; i++)
|
||||
consumer.Consume(idleAction());
|
||||
{
|
||||
consumer.Consume(idleAction());@Unroll@
|
||||
}
|
||||
}
|
||||
|
||||
private void MainMultiAction(long invokeCount)
|
||||
{
|
||||
for (long i = 0; i < invokeCount; i++)
|
||||
consumer.Consume(mainAction());
|
||||
{
|
||||
consumer.Consume(mainAction());@Unroll@
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -98,13 +102,17 @@ namespace BenchmarkDotNet.Autogenerated
|
|||
private void IdleMultiAction(long invokeCount)
|
||||
{
|
||||
for (long i = 0; i < invokeCount; i++)
|
||||
idleAction();
|
||||
{
|
||||
idleAction();@Unroll@
|
||||
}
|
||||
}
|
||||
|
||||
private void MainMultiAction(long invokeCount)
|
||||
{
|
||||
for (long i = 0; i < invokeCount; i++)
|
||||
mainAction();
|
||||
{
|
||||
mainAction();@Unroll@
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -8,7 +8,8 @@ namespace BenchmarkDotNet.Validators
|
|||
{
|
||||
private static readonly IValidator[] MandatoryValidators =
|
||||
{
|
||||
BaselineValidator.FailOnError
|
||||
BaselineValidator.FailOnError,
|
||||
UnrollFactorValidator.Default
|
||||
};
|
||||
|
||||
internal readonly IValidator[] Validators;
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
using System.Collections.Generic;
|
||||
using BenchmarkDotNet.Characteristics;
|
||||
using BenchmarkDotNet.Environments;
|
||||
using BenchmarkDotNet.Running;
|
||||
|
||||
namespace BenchmarkDotNet.Validators
|
||||
{
|
||||
public class UnrollFactorValidator : IValidator
|
||||
{
|
||||
public static readonly IValidator Default = new UnrollFactorValidator();
|
||||
|
||||
private UnrollFactorValidator()
|
||||
{
|
||||
}
|
||||
|
||||
public bool TreatsWarningsAsErrors => true;
|
||||
|
||||
public IEnumerable<ValidationError> Validate(IList<Benchmark> benchmarks)
|
||||
{
|
||||
var resolver = EnvResolver.Instance; // TODO: use specified resolver.
|
||||
foreach (var benchmark in benchmarks)
|
||||
{
|
||||
var run = benchmark.Job.Run;
|
||||
int unrollFactor = run.UnrollFactor.Resolve(resolver);
|
||||
if (unrollFactor <= 0)
|
||||
{
|
||||
string message = $"Specified UnrollFactor ({unrollFactor}) must be greater than zero";
|
||||
yield return new ValidationError(true, message, benchmark);
|
||||
}
|
||||
else if (!run.InvocationCount.IsDefault)
|
||||
{
|
||||
int invocationCount = run.InvocationCount.SpecifiedValue;
|
||||
if (invocationCount % unrollFactor != 0)
|
||||
{
|
||||
string message = $"Specified InvocationCount ({invocationCount}) must be a multiple of UnrollFactor ({unrollFactor})";
|
||||
yield return new ValidationError(true, message, benchmark);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ namespace BenchmarkDotNet.Tests.Engine
|
|||
max = min;
|
||||
var job = Job.Default;
|
||||
var stage = CreateStage(job, measure);
|
||||
var measurements = stage.Run(1, mode, Characteristic<int>.Create(""));
|
||||
var measurements = stage.Run(1, mode, Characteristic<int>.Create(""), 1);
|
||||
int count = measurements.Count;
|
||||
output.WriteLine($"MeasurementCount = {count} (Min= {min}, Max = {max})");
|
||||
Assert.InRange(count, min, max);
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace BenchmarkDotNet.Tests.Engine
|
|||
max = min;
|
||||
var job = Job.Default;
|
||||
var stage = CreateStage(job, measure);
|
||||
var measurements = stage.Run(1, mode, Characteristic<int>.Create(""));
|
||||
var measurements = stage.Run(1, mode, Characteristic<int>.Create(""), 1);
|
||||
int count = measurements.Count;
|
||||
output.WriteLine($"MeasurementCount = {count} (Min= {min}, Max = {max})");
|
||||
Assert.InRange(count, min, max);
|
||||
|
|
Загрузка…
Ссылка в новой задаче