|
1 | | -using System; |
2 | | -using BenchmarkDotNet.Jobs; |
3 | | -using Perfolizer.Horology; |
4 | | - |
5 | 1 | namespace BenchmarkDotNet.Engines |
6 | 2 | { |
7 | 3 | public class EngineFactory : IEngineFactory |
8 | 4 | { |
9 | 5 | public IEngine CreateReadyToRun(EngineParameters engineParameters) |
10 | 6 | { |
11 | | - if (engineParameters.WorkloadActionNoUnroll == null) |
12 | | - throw new ArgumentNullException(nameof(engineParameters.WorkloadActionNoUnroll)); |
13 | | - if (engineParameters.WorkloadActionUnroll == null) |
14 | | - throw new ArgumentNullException(nameof(engineParameters.WorkloadActionUnroll)); |
15 | | - if (engineParameters.Dummy1Action == null) |
16 | | - throw new ArgumentNullException(nameof(engineParameters.Dummy1Action)); |
17 | | - if (engineParameters.Dummy2Action == null) |
18 | | - throw new ArgumentNullException(nameof(engineParameters.Dummy2Action)); |
19 | | - if (engineParameters.Dummy3Action == null) |
20 | | - throw new ArgumentNullException(nameof(engineParameters.Dummy3Action)); |
21 | | - if (engineParameters.OverheadActionNoUnroll == null) |
22 | | - throw new ArgumentNullException(nameof(engineParameters.OverheadActionNoUnroll)); |
23 | | - if (engineParameters.OverheadActionUnroll == null) |
24 | | - throw new ArgumentNullException(nameof(engineParameters.OverheadActionUnroll)); |
25 | | - if (engineParameters.TargetJob == null) |
26 | | - throw new ArgumentNullException(nameof(engineParameters.TargetJob)); |
27 | | - |
28 | | - engineParameters.GlobalSetupAction?.Invoke(); // whatever the settings are, we MUST call global setup here, the global cleanup is part of Engine's Dispose |
29 | | - |
30 | | - if (!engineParameters.NeedsJitting) // just create the engine, do NOT jit |
31 | | - return CreateMultiActionEngine(engineParameters); |
32 | | - |
33 | | - int jitIndex = 0; |
34 | | - |
35 | | - if (engineParameters.HasInvocationCount || engineParameters.HasUnrollFactor) // it's a job with explicit configuration, just create the engine and jit it |
36 | | - { |
37 | | - var warmedUpMultiActionEngine = CreateMultiActionEngine(engineParameters); |
38 | | - |
39 | | - DeadCodeEliminationHelper.KeepAliveWithoutBoxing(Jit(warmedUpMultiActionEngine, ++jitIndex, invokeCount: engineParameters.UnrollFactor, unrollFactor: engineParameters.UnrollFactor)); |
40 | | - |
41 | | - return warmedUpMultiActionEngine; |
42 | | - } |
43 | | - |
44 | | - var singleActionEngine = CreateSingleActionEngine(engineParameters); |
45 | | - var singleInvocationTime = Jit(singleActionEngine, ++jitIndex, invokeCount: 1, unrollFactor: 1); |
46 | | - double timesPerIteration = engineParameters.IterationTime / singleInvocationTime; // how many times can we run given benchmark per iteration |
47 | | - |
48 | | - if ((timesPerIteration < 1.5) && (singleInvocationTime < TimeInterval.FromSeconds(10.0))) |
49 | | - { |
50 | | - // if the Jitting took more than IterationTime/1.5 but still less than 10s (a magic number based on observations of reported bugs) |
51 | | - // we call it one more time to see if Jitting itself has not dominated the first invocation |
52 | | - // if it did, it should NOT be a single invocation engine (see #837, #1337, #1338, and #1780) |
53 | | - singleInvocationTime = Jit(singleActionEngine, ++jitIndex, invokeCount: 1, unrollFactor: 1); |
54 | | - timesPerIteration = engineParameters.IterationTime / singleInvocationTime; |
55 | | - } |
56 | | - |
57 | | - // executing once takes longer than iteration time => long running benchmark, needs no pilot and no overhead |
58 | | - // Or executing twice would put us well past the iteration time ==> needs no pilot and no overhead |
59 | | - if (timesPerIteration < 1.5) |
60 | | - return singleActionEngine; |
61 | | - |
62 | | - int defaultUnrollFactor = Job.Default.ResolveValue(RunMode.UnrollFactorCharacteristic, EngineParameters.DefaultResolver); |
63 | | - int roundedUpTimesPerIteration = (int)Math.Ceiling(timesPerIteration); |
64 | | - |
65 | | - if (roundedUpTimesPerIteration < defaultUnrollFactor) // if we run it defaultUnrollFactor times per iteration, it's going to take longer than IterationTime |
66 | | - { |
67 | | - var needsPilot = engineParameters.TargetJob |
68 | | - .WithUnrollFactor(1) // we don't want to use unroll factor! |
69 | | - .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) |
70 | | - .WithEvaluateOverhead(false); // it's something very time consuming, it overhead is too small compared to total time |
71 | | - |
72 | | - return CreateEngine(engineParameters, needsPilot, engineParameters.OverheadActionNoUnroll, engineParameters.WorkloadActionNoUnroll); |
73 | | - } |
74 | | - |
75 | | - var multiActionEngine = CreateMultiActionEngine(engineParameters); |
76 | | - |
77 | | - DeadCodeEliminationHelper.KeepAliveWithoutBoxing(Jit(multiActionEngine, ++jitIndex, invokeCount: defaultUnrollFactor, unrollFactor: defaultUnrollFactor)); |
78 | | - |
79 | | - return multiActionEngine; |
80 | | - } |
81 | | - |
82 | | - /// <returns>the time it took to run the benchmark</returns> |
83 | | - private static TimeInterval Jit(Engine engine, int jitIndex, int invokeCount, int unrollFactor) |
84 | | - { |
85 | | - engine.Dummy1Action.Invoke(); |
86 | | - |
87 | | - DeadCodeEliminationHelper.KeepAliveWithoutBoxing(engine.RunIteration(new IterationData(IterationMode.Overhead, IterationStage.Jitting, jitIndex, invokeCount, unrollFactor))); // don't forget to JIT idle |
| 7 | + var engine = new Engine(engineParameters); |
88 | 8 |
|
89 | | - engine.Dummy2Action.Invoke(); |
| 9 | + // TODO: Move GlobalSetup/Cleanup to Engine.Run. |
| 10 | + engine.Parameters.GlobalSetupAction.Invoke(); // whatever the settings are, we MUST call global setup here, the global cleanup is part of Engine's Dispose |
90 | 11 |
|
91 | | - var result = engine.RunIteration(new IterationData(IterationMode.Workload, IterationStage.Jitting, jitIndex, invokeCount, unrollFactor)); |
92 | | - |
93 | | - engine.Dummy3Action.Invoke(); |
94 | | - |
95 | | - engine.WriteLine(); |
96 | | - |
97 | | - return TimeInterval.FromNanoseconds(result.Nanoseconds); |
| 12 | + return engine; |
98 | 13 | } |
99 | | - |
100 | | - private static Engine CreateMultiActionEngine(EngineParameters engineParameters) |
101 | | - => CreateEngine(engineParameters, engineParameters.TargetJob, engineParameters.OverheadActionUnroll, engineParameters.WorkloadActionUnroll); |
102 | | - |
103 | | - private static Engine CreateSingleActionEngine(EngineParameters engineParameters) |
104 | | - => CreateEngine(engineParameters, |
105 | | - engineParameters.TargetJob |
106 | | - .WithInvocationCount(1).WithUnrollFactor(1) // run the benchmark exactly once per iteration |
107 | | - .WithEvaluateOverhead(false), // it's something very time consuming, it overhead is too small compared to total time |
108 | | - // todo: consider if we should set the warmup count to 2 |
109 | | - engineParameters.OverheadActionNoUnroll, |
110 | | - engineParameters.WorkloadActionNoUnroll); |
111 | | - |
112 | | - private static Engine CreateEngine(EngineParameters engineParameters, Job job, Action<long> idle, Action<long> main) |
113 | | - => new Engine( |
114 | | - engineParameters.Host, |
115 | | - EngineParameters.DefaultResolver, |
116 | | - engineParameters.Dummy1Action, |
117 | | - engineParameters.Dummy2Action, |
118 | | - engineParameters.Dummy3Action, |
119 | | - idle, |
120 | | - main, |
121 | | - job, |
122 | | - engineParameters.GlobalSetupAction, |
123 | | - engineParameters.GlobalCleanupAction, |
124 | | - engineParameters.IterationSetupAction, |
125 | | - engineParameters.IterationCleanupAction, |
126 | | - engineParameters.OperationsPerInvoke, |
127 | | - engineParameters.MeasureExtraStats, |
128 | | - engineParameters.BenchmarkName); |
129 | 14 | } |
130 | 15 | } |
0 commit comments