|
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