diff --git a/src/BenchmarkDotNet/Helpers/Reflection.Emit/MethodBuilderExtensions.cs b/src/BenchmarkDotNet/Helpers/Reflection.Emit/MethodBuilderExtensions.cs index 4ddc1bcc3d..c09a7d19c2 100644 --- a/src/BenchmarkDotNet/Helpers/Reflection.Emit/MethodBuilderExtensions.cs +++ b/src/BenchmarkDotNet/Helpers/Reflection.Emit/MethodBuilderExtensions.cs @@ -1,4 +1,5 @@ -using System; +using BenchmarkDotNet.Portability; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -37,5 +38,13 @@ public static MethodBuilder SetNoOptimizationImplementationFlag(this MethodBuild return methodBuilder; } + + public static MethodBuilder SetAggressiveOptimizationImplementationFlag(this MethodBuilder methodBuilder) + { + methodBuilder.SetImplementationFlags( + methodBuilder.GetMethodImplementationFlags() | CodeGenHelper.AggressiveOptimizationOptionForEmit); + + return methodBuilder; + } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/CodeGen.cs b/src/BenchmarkDotNet/Portability/CodeGen.cs new file mode 100644 index 0000000000..0db9306b43 --- /dev/null +++ b/src/BenchmarkDotNet/Portability/CodeGen.cs @@ -0,0 +1,12 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace BenchmarkDotNet.Portability +{ + public static class CodeGenHelper + { + // AggressiveOptimization is not available in netstandard2.0, so just use the value casted to enum. + public const MethodImplOptions AggressiveOptimizationOption = (MethodImplOptions) 512; + public const MethodImplAttributes AggressiveOptimizationOptionForEmit = (MethodImplAttributes) 512; + } +} diff --git a/src/BenchmarkDotNet/Templates/BenchmarkType.txt b/src/BenchmarkDotNet/Templates/BenchmarkType.txt index d8f15f9138..f17737d646 100644 --- a/src/BenchmarkDotNet/Templates/BenchmarkType.txt +++ b/src/BenchmarkDotNet/Templates/BenchmarkType.txt @@ -113,9 +113,7 @@ private BenchmarkDotNet.Engines.Consumer consumer = new BenchmarkDotNet.Engines.Consumer(); -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void OverheadActionUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -125,9 +123,7 @@ } } -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void OverheadActionNoUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -137,9 +133,7 @@ } } -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void WorkloadActionUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -149,9 +143,7 @@ } } -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void WorkloadActionNoUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -175,9 +167,7 @@ #elif RETURNS_NON_CONSUMABLE_STRUCT_$ID$ -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void OverheadActionUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -189,9 +179,7 @@ BenchmarkDotNet.Engines.DeadCodeEliminationHelper.KeepAliveWithoutBoxing(result); } -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void OverheadActionNoUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -203,9 +191,7 @@ BenchmarkDotNet.Engines.DeadCodeEliminationHelper.KeepAliveWithoutBoxing(result); } -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void WorkloadActionUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -217,9 +203,7 @@ NonGenericKeepAliveWithoutBoxing(result); } -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void WorkloadActionNoUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -250,9 +234,7 @@ #elif RETURNS_BYREF_$ID$ -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void OverheadActionUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -264,9 +246,7 @@ BenchmarkDotNet.Engines.DeadCodeEliminationHelper.KeepAliveWithoutBoxing(value); } -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void OverheadActionNoUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -280,9 +260,7 @@ private $WorkloadMethodReturnType$ workloadDefaultValueHolder = default($WorkloadMethodReturnType$); -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void WorkloadActionUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -294,9 +272,7 @@ BenchmarkDotNet.Engines.DeadCodeEliminationHelper.KeepAliveWithoutBoxing(ref alias); } -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void WorkloadActionNoUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -321,9 +297,7 @@ } #elif RETURNS_BYREF_READONLY_$ID$ -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void OverheadActionUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -335,9 +309,7 @@ BenchmarkDotNet.Engines.DeadCodeEliminationHelper.KeepAliveWithoutBoxing(value); } -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void OverheadActionNoUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -351,9 +323,7 @@ private $WorkloadMethodReturnType$ workloadDefaultValueHolder = default($WorkloadMethodReturnType$); -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void WorkloadActionUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -365,9 +335,7 @@ BenchmarkDotNet.Engines.DeadCodeEliminationHelper.KeepAliveWithoutBoxingReadonly(alias); } -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void WorkloadActionNoUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -392,9 +360,7 @@ } #elif RETURNS_VOID_$ID$ -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void OverheadActionUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -404,9 +370,7 @@ } } -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void OverheadActionNoUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -416,9 +380,7 @@ } } -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void WorkloadActionUnroll(System.Int64 invokeCount) { $LoadArguments$ @@ -428,9 +390,7 @@ } } -#if NETCOREAPP3_0_OR_GREATER - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)] -#endif + [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] private void WorkloadActionNoUnroll(System.Int64 invokeCount) { $LoadArguments$ diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs index 7f9d47c62f..d2e63b460f 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs @@ -654,13 +654,14 @@ private MethodBuilder EmitActionImpl(string methodName, RunnableActionKind actio } // .method private hidebysig - // instance void OverheadActionUnroll(int64 invokeCount) cil managed + // instance void OverheadActionUnroll(int64 invokeCount) cil managed aggressiveoptimization var toArg = new EmitParameterInfo(0, InvokeCountParamName, typeof(long)); var actionMethodBuilder = runnableBuilder.DefineNonVirtualInstanceMethod( methodName, MethodAttributes.Private, EmitParameterInfo.CreateReturnVoidParameter(), - toArg); + toArg) + .SetAggressiveOptimizationImplementationFlag(); toArg.SetMember(actionMethodBuilder); // Emit impl diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkAction.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkAction.cs index df1911d0b0..5f4d6db36a 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkAction.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkAction.cs @@ -14,7 +14,8 @@ public abstract class BenchmarkAction /// Gets or sets invoke multiple times callback. /// Invoke multiple times callback. - public Action InvokeMultiple { get; protected set; } + public Action InvokeUnroll { get; protected set; } + public Action InvokeNoUnroll{ get; protected set; } /// Gets the last run result. /// The last run result. diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory_Implementations.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory_Implementations.cs index eef3ce8997..551e3001c9 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory_Implementations.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory_Implementations.cs @@ -1,5 +1,7 @@ -using System; +using BenchmarkDotNet.Portability; +using System; using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading.Tasks; namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit @@ -25,17 +27,26 @@ public BenchmarkActionVoid(object instance, MethodInfo method, int unrollFactor) InvokeSingle = callback; unrolledCallback = Unroll(callback, unrollFactor); - InvokeMultiple = InvokeMultipleHardcoded; + InvokeUnroll = WorkloadActionUnroll; + InvokeNoUnroll = WorkloadActionNoUnroll; } private static void OverheadStatic() { } private void OverheadInstance() { } - private void InvokeMultipleHardcoded(long repeatCount) + [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] + private void WorkloadActionUnroll(long repeatCount) { for (long i = 0; i < repeatCount; i++) unrolledCallback(); } + + [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] + private void WorkloadActionNoUnroll(long repeatCount) + { + for (long i = 0; i < repeatCount; i++) + callback(); + } } internal class BenchmarkAction : BenchmarkActionBase @@ -50,7 +61,8 @@ public BenchmarkAction(object instance, MethodInfo method, int unrollFactor) InvokeSingle = InvokeSingleHardcoded; unrolledCallback = Unroll(callback, unrollFactor); - InvokeMultiple = InvokeMultipleHardcoded; + InvokeUnroll = WorkloadActionUnroll; + InvokeNoUnroll = WorkloadActionNoUnroll; } private static T OverheadStatic() => default; @@ -58,12 +70,20 @@ public BenchmarkAction(object instance, MethodInfo method, int unrollFactor) private void InvokeSingleHardcoded() => result = callback(); - private void InvokeMultipleHardcoded(long repeatCount) + [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] + private void WorkloadActionUnroll(long repeatCount) { for (long i = 0; i < repeatCount; i++) result = unrolledCallback(); } + [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] + private void WorkloadActionNoUnroll(long repeatCount) + { + for (long i = 0; i < repeatCount; i++) + result = callback(); + } + public override object LastRunResult => result; } @@ -89,7 +109,8 @@ public BenchmarkActionTask(object instance, MethodInfo method, int unrollFactor) InvokeSingle = callback; unrolledCallback = Unroll(callback, unrollFactor); - InvokeMultiple = InvokeMultipleHardcoded; + InvokeUnroll = WorkloadActionUnroll; + InvokeNoUnroll = WorkloadActionNoUnroll; } @@ -99,11 +120,19 @@ private void Overhead() { } // must be kept in sync with TaskDeclarationsProvider.TargetMethodDelegate private void ExecuteBlocking() => startTaskCallback.Invoke().GetAwaiter().GetResult(); - private void InvokeMultipleHardcoded(long repeatCount) + [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] + private void WorkloadActionUnroll(long repeatCount) { for (long i = 0; i < repeatCount; i++) unrolledCallback(); } + + [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] + private void WorkloadActionNoUnroll(long repeatCount) + { + for (long i = 0; i < repeatCount; i++) + callback(); + } } internal class BenchmarkActionTask : BenchmarkActionBase @@ -129,7 +158,8 @@ public BenchmarkActionTask(object instance, MethodInfo method, int unrollFactor) InvokeSingle = InvokeSingleHardcoded; unrolledCallback = Unroll(callback, unrollFactor); - InvokeMultiple = InvokeMultipleHardcoded; + InvokeUnroll = WorkloadActionUnroll; + InvokeNoUnroll = WorkloadActionNoUnroll; } private T Overhead() => default; @@ -139,12 +169,20 @@ public BenchmarkActionTask(object instance, MethodInfo method, int unrollFactor) private void InvokeSingleHardcoded() => result = callback(); - private void InvokeMultipleHardcoded(long repeatCount) + [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] + private void WorkloadActionUnroll(long repeatCount) { for (long i = 0; i < repeatCount; i++) result = unrolledCallback(); } + [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] + private void WorkloadActionNoUnroll(long repeatCount) + { + for (long i = 0; i < repeatCount; i++) + result = callback(); + } + public override object LastRunResult => result; } @@ -172,7 +210,8 @@ public BenchmarkActionValueTask(object instance, MethodInfo method, int unrollFa unrolledCallback = Unroll(callback, unrollFactor); - InvokeMultiple = InvokeMultipleHardcoded; + InvokeUnroll = WorkloadActionUnroll; + InvokeNoUnroll = WorkloadActionNoUnroll; } private T Overhead() => default; @@ -182,12 +221,20 @@ public BenchmarkActionValueTask(object instance, MethodInfo method, int unrollFa private void InvokeSingleHardcoded() => result = callback(); - private void InvokeMultipleHardcoded(long repeatCount) + [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] + private void WorkloadActionUnroll(long repeatCount) { for (long i = 0; i < repeatCount; i++) result = unrolledCallback(); } + [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] + private void WorkloadActionNoUnroll(long repeatCount) + { + for (long i = 0; i < repeatCount; i++) + result = callback(); + } + public override object LastRunResult => result; } } diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitRunner.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitRunner.cs index e890000683..a52e7ebcf8 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitRunner.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitRunner.cs @@ -129,21 +129,13 @@ public static void RunCore(IHost host, BenchmarkCase benchmarkCase) var engineParameters = new EngineParameters { Host = host, - WorkloadActionNoUnroll = invocationCount => - { - for (int i = 0; i < invocationCount; i++) - workloadAction.InvokeSingle(); - }, - WorkloadActionUnroll = workloadAction.InvokeMultiple, + WorkloadActionNoUnroll = workloadAction.InvokeNoUnroll, + WorkloadActionUnroll = workloadAction.InvokeUnroll, Dummy1Action = dummy1.InvokeSingle, Dummy2Action = dummy2.InvokeSingle, Dummy3Action = dummy3.InvokeSingle, - OverheadActionNoUnroll = invocationCount => - { - for (int i = 0; i < invocationCount; i++) - overheadAction.InvokeSingle(); - }, - OverheadActionUnroll = overheadAction.InvokeMultiple, + OverheadActionNoUnroll = overheadAction.InvokeNoUnroll, + OverheadActionUnroll = overheadAction.InvokeUnroll, GlobalSetupAction = globalSetupAction.InvokeSingle, GlobalCleanupAction = globalCleanupAction.InvokeSingle, IterationSetupAction = iterationSetupAction.InvokeSingle, diff --git a/tests/BenchmarkDotNet.IntegrationTests/InProcessTest.cs b/tests/BenchmarkDotNet.IntegrationTests/InProcessTest.cs index a06870ad5e..b0b3b9b07c 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/InProcessTest.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/InProcessTest.cs @@ -157,18 +157,18 @@ private void TestInvoke(BenchmarkAction benchmarkAction, int unrollFactor, bool { benchmarkAction.InvokeSingle(); Assert.Equal(0, BenchmarkAllCases.Counter); - benchmarkAction.InvokeMultiple(0); + benchmarkAction.InvokeUnroll(0); Assert.Equal(0, BenchmarkAllCases.Counter); - benchmarkAction.InvokeMultiple(11); + benchmarkAction.InvokeUnroll(11); Assert.Equal(0, BenchmarkAllCases.Counter); } else { benchmarkAction.InvokeSingle(); Assert.Equal(1, BenchmarkAllCases.Counter); - benchmarkAction.InvokeMultiple(0); + benchmarkAction.InvokeUnroll(0); Assert.Equal(1, BenchmarkAllCases.Counter); - benchmarkAction.InvokeMultiple(11); + benchmarkAction.InvokeUnroll(11); Assert.Equal(BenchmarkAllCases.Counter, 1 + unrollFactor * 11); }