Skip to content

Commit 52cabdd

Browse files
committed
PR feedback.
1 parent cf37148 commit 52cabdd

File tree

4 files changed

+33
-118
lines changed

4 files changed

+33
-118
lines changed

src/BenchmarkDotNet/Engines/EngineJitStage.cs

Lines changed: 18 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@
88

99
namespace BenchmarkDotNet.Engines
1010
{
11-
internal abstract class EngineJitStage(int unrollFactor, EngineParameters parameters) : EngineStage(IterationStage.Jitting, IterationMode.Workload, parameters)
11+
internal abstract class EngineJitStage(EngineParameters parameters) : EngineStage(IterationStage.Jitting, IterationMode.Workload, parameters)
1212
{
13-
protected readonly int unrollFactor = unrollFactor;
1413
protected readonly Action<long> dummy1Action= _ => parameters.Dummy1Action();
1514
protected readonly Action<long> dummy2Action= _ => parameters.Dummy2Action();
1615
protected readonly Action<long> dummy3Action = _ => parameters.Dummy3Action();
@@ -24,26 +23,13 @@ internal sealed class EngineFirstJitStage : EngineJitStage
2423
// It is not worth spending a long time in jit stage for macro-benchmarks.
2524
private static readonly TimeInterval MaxTieringTime = TimeInterval.FromSeconds(10);
2625

27-
internal bool didJitUnroll = false;
2826
internal bool didStopEarly = false;
2927
internal Measurement lastMeasurement;
3028

31-
private readonly Action<long> overheadAction;
32-
private readonly Action<long> workloadAction;
3329
private readonly IEnumerator<IterationData> enumerator;
3430

35-
internal EngineFirstJitStage(int unrollFactor, EngineParameters parameters) : base(unrollFactor, parameters)
31+
internal EngineFirstJitStage(EngineParameters parameters) : base(parameters)
3632
{
37-
if (unrollFactor != 1)
38-
{
39-
overheadAction = parameters.OverheadActionUnroll;
40-
workloadAction = parameters.WorkloadActionUnroll;
41-
}
42-
else
43-
{
44-
overheadAction = parameters.OverheadActionNoUnroll;
45-
workloadAction = parameters.WorkloadActionNoUnroll;
46-
}
4733
enumerator = EnumerateIterations();
4834
}
4935

@@ -88,9 +74,9 @@ private IEnumerator<IterationData> EnumerateIterations()
8874
{
8975
++iterationIndex;
9076
yield return GetDummyIterationData(dummy1Action);
91-
yield return GetOverheadNoUnrollIterationData();
77+
yield return GetOverheadIterationData();
9278
yield return GetDummyIterationData(dummy2Action);
93-
yield return GetWorkloadNoUnrollIterationData();
79+
yield return GetWorkloadIterationData();
9480
yield return GetDummyIterationData(dummy3Action);
9581

9682
// If the jit is not tiered, we're done.
@@ -102,103 +88,33 @@ private IEnumerator<IterationData> EnumerateIterations()
10288
// Wait enough time for jit call counting to begin.
10389
MaybeSleep(JitInfo.TieredDelay);
10490

105-
// If the jit is configured for aggressive tiering, ignore how long it takes, and run 1 set of iterations per jit tier to fully promote the methods to tier1.
106-
if (JitInfo.TieredCallCountThreshold == 1)
107-
{
108-
++iterationIndex;
109-
yield return GetOverheadNoUnrollIterationData();
110-
yield return GetWorkloadNoUnrollIterationData();
111-
112-
MaybeSleep(JitInfo.BackgroundCompilationDelay);
113-
114-
if (JitInfo.IsDPGO)
115-
{
116-
++iterationIndex;
117-
yield return GetOverheadNoUnrollIterationData();
118-
yield return GetWorkloadNoUnrollIterationData();
119-
}
120-
121-
MaybeSleep(JitInfo.BackgroundCompilationDelay);
122-
123-
yield break;
124-
}
125-
126-
// Otherwise, attempt to promote methods to tier1, but don't spend too much time in jit stage.
91+
// Attempt to promote methods to tier1, but don't spend too much time in jit stage.
12792
StartedClock startedClock = parameters.TargetJob.ResolveValue(InfrastructureMode.ClockCharacteristic, parameters.Resolver).Start();
12893

129-
++iterationIndex;
130-
yield return GetOverheadNoUnrollIterationData();
131-
yield return GetWorkloadNoUnrollIterationData();
132-
133-
if (startedClock.GetElapsed().GetTimeValue() >= MaxTieringTime)
134-
{
135-
didStopEarly = true;
136-
yield break;
137-
}
138-
139-
int tier0CallCount = JitInfo.TieredCallCountThreshold - 1;
140-
// Run unroll method if it's not too many invocations.
141-
if (unrollFactor <= tier0CallCount)
142-
{
143-
tier0CallCount -= unrollFactor;
144-
++iterationIndex;
145-
yield return GetOverheadUnrollIterationData();
146-
yield return GetWorkloadUnrollIterationData();
147-
148-
didJitUnroll = unrollFactor != 1;
149-
150-
if (startedClock.GetElapsed().GetTimeValue() >= MaxTieringTime)
151-
{
152-
didStopEarly = true;
153-
yield break;
154-
}
155-
}
156-
157-
// Run the remaining iterations without unroll.
158-
for (int i = 0; i < tier0CallCount; ++i)
159-
{
160-
++iterationIndex;
161-
yield return GetOverheadNoUnrollIterationData();
162-
yield return GetWorkloadNoUnrollIterationData();
163-
164-
if (startedClock.GetElapsed().GetTimeValue() >= MaxTieringTime)
165-
{
166-
didStopEarly = true;
167-
yield break;
168-
}
169-
}
170-
171-
MaybeSleep(JitInfo.BackgroundCompilationDelay);
172-
173-
if (JitInfo.IsDPGO)
94+
for (int tierCount = JitInfo.IsDPGO ? 2 : 1; tierCount >= 0; --tierCount)
17495
{
175-
for (int i = 0; i < JitInfo.TieredCallCountThreshold; ++i)
96+
for (int callCount = JitInfo.TieredCallCountThreshold; callCount >= 0; --callCount)
17697
{
17798
++iterationIndex;
178-
yield return GetOverheadNoUnrollIterationData();
179-
yield return GetWorkloadNoUnrollIterationData();
99+
yield return GetOverheadIterationData();
100+
yield return GetWorkloadIterationData();
180101

181-
if (startedClock.GetElapsed().GetTimeValue() >= MaxTieringTime)
102+
if ((tierCount + callCount) > 0
103+
&& startedClock.GetElapsed().GetTimeValue() >= MaxTieringTime)
182104
{
183105
didStopEarly = true;
184106
yield break;
185107
}
186108
}
187-
}
188109

189-
MaybeSleep(JitInfo.BackgroundCompilationDelay);
110+
MaybeSleep(JitInfo.BackgroundCompilationDelay);
111+
}
190112
}
191113

192-
private IterationData GetOverheadUnrollIterationData()
193-
=> new(IterationMode.Overhead, IterationStage.Jitting, iterationIndex, unrollFactor, unrollFactor, () => { }, () => { }, overheadAction);
194-
195-
private IterationData GetWorkloadUnrollIterationData()
196-
=> new(IterationMode.Workload, IterationStage.Jitting, iterationIndex, unrollFactor, unrollFactor, parameters.IterationSetupAction, parameters.IterationCleanupAction, workloadAction);
197-
198-
private IterationData GetOverheadNoUnrollIterationData()
114+
private IterationData GetOverheadIterationData()
199115
=> new(IterationMode.Overhead, IterationStage.Jitting, iterationIndex, 1, 1, () => { }, () => { }, parameters.OverheadActionNoUnroll);
200116

201-
private IterationData GetWorkloadNoUnrollIterationData()
117+
private IterationData GetWorkloadIterationData()
202118
=> new(IterationMode.Workload, IterationStage.Jitting, iterationIndex, 1, 1, parameters.IterationSetupAction, parameters.IterationCleanupAction, parameters.WorkloadActionNoUnroll);
203119

204120
private void MaybeSleep(TimeSpan timeSpan)
@@ -210,8 +126,10 @@ private void MaybeSleep(TimeSpan timeSpan)
210126
}
211127
}
212128

213-
internal sealed class EngineSecondJitStage(int unrollFactor, EngineParameters parameters) : EngineJitStage(unrollFactor, parameters)
129+
internal sealed class EngineSecondJitStage(int unrollFactor, EngineParameters parameters) : EngineJitStage(parameters)
214130
{
131+
private readonly int unrollFactor = unrollFactor;
132+
215133
internal override List<Measurement> GetMeasurementList() => new(GetMaxCallCount());
216134

217135
private static int GetMaxCallCount()

src/BenchmarkDotNet/Engines/EngineStage.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ internal static IEnumerable<EngineStage> EnumerateStages(EngineParameters parame
3333
int minInvokeCount = parameters.TargetJob.ResolveValue(AccuracyMode.MinInvokeCountCharacteristic, parameters.Resolver);
3434

3535
// AOT technically doesn't have a JIT, but we run jit stage regardless because of static constructors. #2004
36-
var jitStage = new EngineFirstJitStage(unrollFactor, parameters);
36+
var jitStage = new EngineFirstJitStage(parameters);
3737
yield return jitStage;
3838

3939
bool hasUnrollFactor = parameters.TargetJob.HasValue(RunMode.UnrollFactorCharacteristic);
@@ -58,8 +58,9 @@ internal static IEnumerable<EngineStage> EnumerateStages(EngineParameters parame
5858
skipPilotStage = !pilotStage.needsFurtherPilot;
5959
}
6060

61-
// If the first jit stage only jitted *NoUnroll methods, now we need to jit *Unroll methods if they're going to be used.
62-
if (!RuntimeInformation.IsAot && !jitStage.didJitUnroll && unrollFactor != 1)
61+
// The first jit stage only jitted *NoUnroll methods, now we need to jit *Unroll methods if they're going to be used.
62+
// TODO: This stage can be removed after we refactor the engine/codegen to pass the clock into the delegates.
63+
if (!RuntimeInformation.IsAot && unrollFactor != 1)
6364
{
6465
yield return new EngineSecondJitStage(unrollFactor, parameters);
6566
}

src/BenchmarkDotNet/Extensions/ProcessExtensions.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,8 @@ internal static void SetEnvironmentVariables(this ProcessStartInfo start, Benchm
150150
// disable ReSharper's Dynamic Program Analysis (see https://github.com/dotnet/BenchmarkDotNet/issues/1871 for details)
151151
start.EnvironmentVariables["JETBRAINS_DPA_AGENT_ENABLE"] = "0";
152152

153-
// Configure JIT to tier up aggressively.
154-
SetClrEnvironmentVariables(start, JitInfo.CallCountThresholdEnv, "1");
155-
// Ideally we would set both of these env vars, but AggressiveTiering sets CallCountingDelayMs to 0, and that breaks DisassemblyDiagnoser. https://github.com/dotnet/runtime/issues/117339
153+
// TODO: set CallCountingDelayMs without breaking DisassemblyDiagnoser. https://github.com/dotnet/runtime/issues/117339
156154
//SetClrEnvironmentVariables(start, JitInfo.CallCountingDelayMsEnv, "0");
157-
//SetClrEnvironmentVariables(start, JitInfo.AggressiveTieringEnv, "1");
158155

159156
if (!benchmarkCase.Job.HasValue(EnvironmentMode.EnvironmentVariablesCharacteristic))
160157
return;

tests/BenchmarkDotNet.Tests/Engine/EngineFactoryTests.cs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -133,37 +133,36 @@ public void BenchmarksThatRunLongerThanIterationTimeOnlyDuringFirstInvocationAre
133133
}
134134

135135
[Fact]
136-
public void ForJobsWithExplicitUnrollFactorTheGlobalSetupIsCalledAndMultiActionCodeGetsJitted()
137-
=> AssertGlobalSetupWasCalledAndMultiActionGotJitted(Job.Default.WithUnrollFactor(16));
136+
public void ForJobsWithExplicitUnrollFactorTheGlobalSetupAndUnrollAreCalled()
137+
=> AssertGlobalSetupWasCalledAndUnrollWasRan(Job.Default.WithUnrollFactor(16));
138138

139139
[Fact]
140-
public void ForJobsThatDontRequirePilotTheGlobalSetupIsCalledAndMultiActionCodeGetsJitted()
141-
=> AssertGlobalSetupWasCalledAndMultiActionGotJitted(Job.Default.WithInvocationCount(100));
140+
public void ForJobsThatDontRequirePilotTheGlobalSetupAndUnrollAreCalled()
141+
=> AssertGlobalSetupWasCalledAndUnrollWasRan(Job.Default.WithInvocationCount(100));
142142

143143
[Fact]
144144
public void NonVeryTimeConsumingBenchmarksAreExecutedMoreThanOncePerIterationWithUnrollFactorForDefaultSettings()
145-
=> AssertGlobalSetupWasCalledAndMultiActionGotJitted(Job.Default);
145+
=> AssertGlobalSetupWasCalledAndUnrollWasRan(Job.Default);
146146

147-
private void AssertGlobalSetupWasCalledAndMultiActionGotJitted(Job job)
147+
private void AssertGlobalSetupWasCalledAndUnrollWasRan(Job job)
148148
{
149149
var engineParameters = CreateEngineParameters(job);
150150

151151
var engine = (Engines.Engine) new EngineFactory().CreateReadyToRun(engineParameters);
152+
bool didRunUnroll = false;
152153
foreach (var stage in EngineStage.EnumerateStages(engine.Parameters))
153154
{
154155
var stageMeasurements = stage.GetMeasurementList();
155156
while (stage.GetShouldRunIteration(stageMeasurements, out var iterationData))
156157
{
158+
didRunUnroll |= iterationData.unrollFactor > 1;
157159
var measurement = new Measurement(0, iterationData.mode, iterationData.stage, iterationData.index, 1, 1);
158160
stageMeasurements.Add(measurement);
159161
}
160-
161-
Assert.IsType<EngineFirstJitStage>(stage);
162-
var jitStage = (EngineFirstJitStage) stage;
163-
Assert.True(jitStage.didJitUnroll);
164-
break;
165162
}
166163

164+
Assert.True(didRunUnroll);
165+
167166
Assert.Equal(1, timesGlobalSetupCalled);
168167
Assert.Equal(0, timesGlobalCleanupCalled);
169168

0 commit comments

Comments
 (0)