diff --git a/src/BenchmarkDotNet/Engines/Engine.cs b/src/BenchmarkDotNet/Engines/Engine.cs index 29d2e0ddd7..9dc8cc616e 100644 --- a/src/BenchmarkDotNet/Engines/Engine.cs +++ b/src/BenchmarkDotNet/Engines/Engine.cs @@ -166,12 +166,9 @@ public Measurement RunIteration(IterationData data) if (EngineEventSource.Log.IsEnabled()) EngineEventSource.Log.IterationStart(data.IterationMode, data.IterationStage, totalOperations); - Span stackMemory = randomizeMemory ? stackalloc byte[random.Next(32)] : Span.Empty; - - // Measure - var clock = Clock.Start(); - action(invokeCount / unrollFactor); - var clockSpan = clock.GetElapsed(); + var clockSpan = randomizeMemory + ? MeasureWithRandomMemory(action, invokeCount / unrollFactor) + : Measure(action, invokeCount / unrollFactor); if (EngineEventSource.Log.IsEnabled()) EngineEventSource.Log.IterationStop(data.IterationMode, data.IterationStage, totalOperations); @@ -190,9 +187,29 @@ public Measurement RunIteration(IterationData data) if (measurement.IterationStage == IterationStage.Jitting) jittingMeasurements.Add(measurement); + return measurement; + } + + // This is in a separate method, because stackalloc can affect code alignment, + // resulting in unexpected measurements on some AMD cpus, + // even if the stackalloc branch isn't executed. (#2366) + [MethodImpl(MethodImplOptions.NoInlining)] + private unsafe ClockSpan MeasureWithRandomMemory(Action action, long invokeCount) + { + byte* stackMemory = stackalloc byte[random.Next(32)]; + var clockSpan = Measure(action, invokeCount); Consume(stackMemory); + return clockSpan; + } - return measurement; + [MethodImpl(MethodImplOptions.NoInlining)] + private unsafe void Consume(byte* _) { } + + private ClockSpan Measure(Action action, long invokeCount) + { + var clock = Clock.Start(); + action(invokeCount); + return clock.GetElapsed(); } private (GcStats, ThreadingStats, double) GetExtraStats(IterationData data) @@ -224,9 +241,6 @@ public Measurement RunIteration(IterationData data) return (gcStats, threadingStats, exceptionsStats.ExceptionsCount / (double)totalOperationsCount); } - [MethodImpl(MethodImplOptions.NoInlining)] - private void Consume(in Span _) { } - private void RandomizeManagedHeapMemory() { // invoke global cleanup before global setup