diff --git a/src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs b/src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs
index b38691f2c9..be926645cf 100644
--- a/src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs
+++ b/src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs
@@ -185,9 +185,7 @@ internal static bool IsStackOnlyWithImplicitCast(this Type argumentType, object?
if (argumentInstance == null)
return false;
- // IsByRefLikeAttribute is not exposed for older runtimes, so we need to check it in an ugly way ;)
- bool isByRefLike = argumentType.GetCustomAttributes().Any(attribute => attribute.ToString()?.Contains("IsByRefLike") ?? false);
- if (!isByRefLike)
+ if (!argumentType.IsByRefLike())
return false;
var instanceType = argumentInstance.GetType();
@@ -209,5 +207,9 @@ private static bool IsRunnableGenericType(TypeInfo typeInfo)
&& typeInfo.DeclaredConstructors.Any(ctor => ctor.IsPublic && ctor.GetParameters().Length == 0); // we need public parameterless ctor to create it
internal static bool IsLinqPad(this Assembly assembly) => assembly.FullName.IndexOf("LINQPAD", StringComparison.OrdinalIgnoreCase) >= 0;
+
+ internal static bool IsByRefLike(this Type type)
+ // Type.IsByRefLike is not available in netstandard2.0.
+ => type.IsValueType && type.CustomAttributes.Any(attr => attr.AttributeType.FullName == "System.Runtime.CompilerServices.IsByRefLikeAttribute");
}
}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkAction.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkAction.cs
index df1911d0b0..02cc14d5e8 100644
--- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkAction.cs
+++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkAction.cs
@@ -1,11 +1,8 @@
using System;
-using JetBrains.Annotations;
-
namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit
{
/// Common API to run the Setup/Clean/Idle/Run methods
- [PublicAPI]
public abstract class BenchmarkAction
{
/// Gets or sets invoke single callback.
@@ -16,8 +13,7 @@ public abstract class BenchmarkAction
/// Invoke multiple times callback.
public Action InvokeMultiple { get; protected set; }
- /// Gets the last run result.
- /// The last run result.
- public virtual object LastRunResult => null;
+ [Obsolete("The result is no longer stored past the iteration.", true)]
+ public object LastRunResult => null;
}
}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory.cs
index 6b0f2468d8..9174e10baa 100644
--- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory.cs
+++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
@@ -6,8 +7,6 @@
using BenchmarkDotNet.Extensions;
using BenchmarkDotNet.Running;
-using JetBrains.Annotations;
-
namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit
{
/// Helper class that creates instances.
@@ -29,9 +28,40 @@ private static BenchmarkAction CreateCore(
if (resultType == typeof(void))
return new BenchmarkActionVoid(resultInstance, targetMethod, unrollFactor);
+ if (resultType == typeof(void*))
+ return new BenchmarkActionVoidPointer(resultInstance, targetMethod, unrollFactor);
+
+ if (resultType.IsPointer)
+ return Create(
+ typeof(BenchmarkActionPointer<>).MakeGenericType(resultType.GetElementType()),
+ resultInstance,
+ targetMethod,
+ unrollFactor);
+
+ if (resultType.IsByRef)
+ {
+ var returnParameter = targetMethod?.ReturnParameter ?? fallbackIdleSignature.ReturnParameter;
+ // System.Runtime.CompilerServices.IsReadOnlyAttribute is part of .NET Standard 2.1, we can't use it here..
+ if (returnParameter.GetCustomAttributes().Any(attribute => attribute.GetType().Name == "IsReadOnlyAttribute"))
+ return Create(
+ typeof(BenchmarkActionByRefReadonly<>).MakeGenericType(resultType.GetElementType()),
+ resultInstance,
+ targetMethod,
+ unrollFactor);
+
+ return Create(
+ typeof(BenchmarkActionByRef<>).MakeGenericType(resultType.GetElementType()),
+ resultInstance,
+ targetMethod,
+ unrollFactor);
+ }
+
if (resultType == typeof(Task))
return new BenchmarkActionTask(resultInstance, targetMethod, unrollFactor);
+ if (resultType == typeof(ValueTask))
+ return new BenchmarkActionValueTask(resultInstance, targetMethod, unrollFactor);
+
if (resultType.GetTypeInfo().IsGenericType)
{
var genericType = resultType.GetGenericTypeDefinition();
@@ -51,10 +81,6 @@ private static BenchmarkAction CreateCore(
unrollFactor);
}
- if (targetMethod == null && resultType.GetTypeInfo().IsValueType)
- // for Idle: we return int because creating bigger ValueType could take longer than benchmarked method itself.
- resultType = typeof(int);
-
return Create(
typeof(BenchmarkAction<>).MakeGenericType(resultType),
resultInstance,
@@ -88,6 +114,10 @@ private static void PrepareInstanceAndResultType(
if (isUsingAsyncKeyword)
throw new NotSupportedException("Async void is not supported by design.");
}
+ else if (resultType.IsByRefLike() || resultType.GetElementType()?.IsByRefLike() == true)
+ {
+ throw new NotSupportedException("InProcessNoEmitToolchain does not support consuming ByRefLike return types.");
+ }
}
/// Helper to enforce .ctor signature.
diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory_Implementations.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory_Implementations.cs
index eef3ce8997..0d0cbdd9ca 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.Engines;
+using System;
using System.Reflection;
+using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit
@@ -34,7 +36,136 @@ private void OverheadInstance() { }
private void InvokeMultipleHardcoded(long repeatCount)
{
for (long i = 0; i < repeatCount; i++)
+ {
unrolledCallback();
+ }
+ }
+ }
+
+ internal unsafe class BenchmarkActionVoidPointer : BenchmarkActionBase
+ {
+ private delegate void* PointerFunc();
+
+ private readonly PointerFunc callback;
+ private readonly PointerFunc unrolledCallback;
+ private readonly Consumer consumer = new ();
+
+ public BenchmarkActionVoidPointer(object instance, MethodInfo method, int unrollFactor)
+ {
+ callback = CreateWorkloadOrOverhead(instance, method, OverheadStatic, OverheadInstance);
+ InvokeSingle = InvokeSingleHardcoded;
+
+ unrolledCallback = Unroll(callback, unrollFactor);
+ InvokeMultiple = InvokeMultipleHardcoded;
+ }
+
+ private static void* OverheadStatic() => default;
+ private void* OverheadInstance() => default;
+
+ private void InvokeSingleHardcoded() => consumer.Consume(callback());
+
+ private void InvokeMultipleHardcoded(long repeatCount)
+ {
+ for (long i = 0; i < repeatCount; i++)
+ {
+ consumer.Consume(unrolledCallback());
+ }
+ }
+ }
+
+ internal unsafe class BenchmarkActionPointer : BenchmarkActionBase
+ where T : unmanaged
+ {
+ private delegate T* PointerFunc();
+
+ private readonly PointerFunc callback;
+ private readonly PointerFunc unrolledCallback;
+ private readonly Consumer consumer = new ();
+
+ public BenchmarkActionPointer(object instance, MethodInfo method, int unrollFactor)
+ {
+ callback = CreateWorkloadOrOverhead(instance, method, OverheadStatic, OverheadInstance);
+ InvokeSingle = InvokeSingleHardcoded;
+
+ unrolledCallback = Unroll(callback, unrollFactor);
+ InvokeMultiple = InvokeMultipleHardcoded;
+ }
+
+ private static T* OverheadStatic() => default;
+ private T* OverheadInstance() => default;
+
+ private void InvokeSingleHardcoded() => consumer.Consume(callback());
+
+ private void InvokeMultipleHardcoded(long repeatCount)
+ {
+ for (long i = 0; i < repeatCount; i++)
+ {
+ consumer.Consume(unrolledCallback());
+ }
+ }
+ }
+
+ internal unsafe class BenchmarkActionByRef : BenchmarkActionBase
+ {
+ private delegate ref T ByRefFunc();
+
+ private readonly ByRefFunc callback;
+ private readonly ByRefFunc unrolledCallback;
+ private readonly Consumer consumer = new ();
+ private static T overheadDefaultValueHolder;
+
+ public BenchmarkActionByRef(object instance, MethodInfo method, int unrollFactor)
+ {
+ callback = CreateWorkloadOrOverhead(instance, method, OverheadStatic, OverheadInstance);
+ InvokeSingle = InvokeSingleHardcoded;
+
+ unrolledCallback = Unroll(callback, unrollFactor);
+ InvokeMultiple = InvokeMultipleHardcoded;
+ }
+
+ private static ref T OverheadStatic() => ref overheadDefaultValueHolder;
+ private ref T OverheadInstance() => ref overheadDefaultValueHolder;
+
+ private void InvokeSingleHardcoded() => consumer.Consume(callback());
+
+ private void InvokeMultipleHardcoded(long repeatCount)
+ {
+ for (long i = 0; i < repeatCount; i++)
+ {
+ consumer.Consume(unrolledCallback());
+ }
+ }
+ }
+
+ internal unsafe class BenchmarkActionByRefReadonly : BenchmarkActionBase
+ {
+ private delegate ref readonly T ByRefReadonlyFunc();
+
+ private readonly ByRefReadonlyFunc callback;
+ private readonly ByRefReadonlyFunc unrolledCallback;
+ private readonly Consumer consumer = new ();
+ private static T overheadDefaultValueHolder;
+
+ public BenchmarkActionByRefReadonly(object instance, MethodInfo method, int unrollFactor)
+ {
+ callback = CreateWorkloadOrOverhead(instance, method, OverheadStatic, OverheadInstance);
+ InvokeSingle = InvokeSingleHardcoded;
+
+ unrolledCallback = Unroll(callback, unrollFactor);
+ InvokeMultiple = InvokeMultipleHardcoded;
+ }
+
+ private static ref readonly T OverheadStatic() => ref overheadDefaultValueHolder;
+ private ref readonly T OverheadInstance() => ref overheadDefaultValueHolder;
+
+ private void InvokeSingleHardcoded() => consumer.Consume(callback());
+
+ private void InvokeMultipleHardcoded(long repeatCount)
+ {
+ for (long i = 0; i < repeatCount; i++)
+ {
+ consumer.Consume(unrolledCallback());
+ }
}
}
@@ -42,7 +173,7 @@ internal class BenchmarkAction : BenchmarkActionBase
{
private readonly Func callback;
private readonly Func unrolledCallback;
- private T result;
+ private readonly Consumer consumer = new ();
public BenchmarkAction(object instance, MethodInfo method, int unrollFactor)
{
@@ -54,17 +185,18 @@ public BenchmarkAction(object instance, MethodInfo method, int unrollFactor)
}
private static T OverheadStatic() => default;
+
private T OverheadInstance() => default;
- private void InvokeSingleHardcoded() => result = callback();
+ private void InvokeSingleHardcoded() => consumer.Consume(callback());
private void InvokeMultipleHardcoded(long repeatCount)
{
for (long i = 0; i < repeatCount; i++)
- result = unrolledCallback();
+ {
+ consumer.Consume(unrolledCallback());
+ }
}
-
- public override object LastRunResult => result;
}
internal class BenchmarkActionTask : BenchmarkActionBase
@@ -102,7 +234,9 @@ private void Overhead() { }
private void InvokeMultipleHardcoded(long repeatCount)
{
for (long i = 0; i < repeatCount; i++)
+ {
unrolledCallback();
+ }
}
}
@@ -111,7 +245,7 @@ internal class BenchmarkActionTask : BenchmarkActionBase
private readonly Func> startTaskCallback;
private readonly Func callback;
private readonly Func unrolledCallback;
- private T result;
+ private readonly Consumer consumer = new ();
public BenchmarkActionTask(object instance, MethodInfo method, int unrollFactor)
{
@@ -137,15 +271,56 @@ public BenchmarkActionTask(object instance, MethodInfo method, int unrollFactor)
// must be kept in sync with GenericTaskDeclarationsProvider.TargetMethodDelegate
private T ExecuteBlocking() => startTaskCallback().GetAwaiter().GetResult();
- private void InvokeSingleHardcoded() => result = callback();
+ private void InvokeSingleHardcoded() => consumer.Consume(callback());
private void InvokeMultipleHardcoded(long repeatCount)
{
for (long i = 0; i < repeatCount; i++)
- result = unrolledCallback();
+ {
+ consumer.Consume(unrolledCallback());
+ }
}
+ }
+
+ internal class BenchmarkActionValueTask : BenchmarkActionBase
+ {
+ private readonly Func startTaskCallback;
+ private readonly Action callback;
+ private readonly Action unrolledCallback;
- public override object LastRunResult => result;
+ public BenchmarkActionValueTask(object instance, MethodInfo method, int unrollFactor)
+ {
+ bool isIdle = method == null;
+ if (!isIdle)
+ {
+ startTaskCallback = CreateWorkload>(instance, method);
+ callback = ExecuteBlocking;
+ }
+ else
+ {
+ callback = Overhead;
+ }
+
+ InvokeSingle = callback;
+
+ unrolledCallback = Unroll(callback, unrollFactor);
+ InvokeMultiple = InvokeMultipleHardcoded;
+
+ }
+
+ // must be kept in sync with VoidDeclarationsProvider.IdleImplementation
+ private void Overhead() { }
+
+ // must be kept in sync with TaskDeclarationsProvider.TargetMethodDelegate
+ private void ExecuteBlocking() => startTaskCallback.Invoke().GetAwaiter().GetResult();
+
+ private void InvokeMultipleHardcoded(long repeatCount)
+ {
+ for (long i = 0; i < repeatCount; i++)
+ {
+ unrolledCallback();
+ }
+ }
}
internal class BenchmarkActionValueTask : BenchmarkActionBase
@@ -153,7 +328,7 @@ internal class BenchmarkActionValueTask : BenchmarkActionBase
private readonly Func> startTaskCallback;
private readonly Func callback;
private readonly Func unrolledCallback;
- private T result;
+ private readonly Consumer consumer = new ();
public BenchmarkActionValueTask(object instance, MethodInfo method, int unrollFactor)
{
@@ -180,15 +355,15 @@ public BenchmarkActionValueTask(object instance, MethodInfo method, int unrollFa
// must be kept in sync with GenericTaskDeclarationsProvider.TargetMethodDelegate
private T ExecuteBlocking() => startTaskCallback().GetAwaiter().GetResult();
- private void InvokeSingleHardcoded() => result = callback();
+ private void InvokeSingleHardcoded() => consumer.Consume(callback());
private void InvokeMultipleHardcoded(long repeatCount)
{
for (long i = 0; i < repeatCount; i++)
- result = unrolledCallback();
+ {
+ consumer.Consume(unrolledCallback());
+ }
}
-
- public override object LastRunResult => result;
}
}
}
\ No newline at end of file
diff --git a/tests/BenchmarkDotNet.IntegrationTests/InProcessTest.cs b/tests/BenchmarkDotNet.IntegrationTests/InProcessTest.cs
index a06870ad5e..fb3d28f275 100644
--- a/tests/BenchmarkDotNet.IntegrationTests/InProcessTest.cs
+++ b/tests/BenchmarkDotNet.IntegrationTests/InProcessTest.cs
@@ -43,19 +43,35 @@ public InProcessTest(ITestOutputHelper output) : base(output)
public void BenchmarkActionVoidSupported() => TestInvoke(x => x.InvokeOnceVoid(), UnrollFactor);
[Fact]
- public void BenchmarkActionTaskSupported() => TestInvoke(x => x.InvokeOnceTaskAsync(), UnrollFactor, null);
+ public void BenchmarkActionTaskSupported() => TestInvoke(x => x.InvokeOnceTaskAsync(), UnrollFactor);
[Fact]
- public void BenchmarkActionRefTypeSupported() => TestInvoke(x => x.InvokeOnceRefType(), UnrollFactor, StringResult);
+ public void BenchmarkActionValueTaskSupported() => TestInvoke(x => x.InvokeOnceValueTaskAsync(), UnrollFactor);
[Fact]
- public void BenchmarkActionValueTypeSupported() => TestInvoke(x => x.InvokeOnceValueType(), UnrollFactor, DecimalResult);
+ public void BenchmarkActionRefTypeSupported() => TestInvoke(x => x.InvokeOnceRefType(), UnrollFactor);
[Fact]
- public void BenchmarkActionTaskOfTSupported() => TestInvoke(x => x.InvokeOnceTaskOfTAsync(), UnrollFactor, StringResult);
+ public void BenchmarkActionValueTypeSupported() => TestInvoke(x => x.InvokeOnceValueType(), UnrollFactor);
[Fact]
- public void BenchmarkActionValueTaskOfTSupported() => TestInvoke(x => x.InvokeOnceValueTaskOfT(), UnrollFactor, DecimalResult);
+ public void BenchmarkActionTaskOfTSupported() => TestInvoke(x => x.InvokeOnceTaskOfTAsync(), UnrollFactor);
+
+ [Fact]
+ public void BenchmarkActionValueTaskOfTSupported() => TestInvoke(x => x.InvokeOnceValueTaskOfT(), UnrollFactor);
+
+ [Fact]
+ public unsafe void BenchmarkActionStructPointerSupported() => TestInvoke(x => x.InvokeOnceStructPointerType(), UnrollFactor);
+
+ [Fact]
+ public unsafe void BenchmarkActionVoidPointerSupported() => TestInvoke(x => x.InvokeOnceVoidPointerType(), UnrollFactor);
+
+ // Can't use ref returns in expression, so pass the MethodInfo directly instead.
+ [Fact]
+ public void BenchmarkActionByRefTypeSupported() => TestInvoke(typeof(BenchmarkAllCases).GetMethod(nameof(BenchmarkAllCases.InvokeOnceByRefType)), UnrollFactor);
+
+ [Fact]
+ public void BenchmarkActionByRefReadonlyValueTypeSupported() => TestInvoke(typeof(BenchmarkAllCases).GetMethod(nameof(BenchmarkAllCases.InvokeOnceByRefReadonlyType)), UnrollFactor);
[Fact]
public void BenchmarkDifferentPlatformReturnsValidationError()
@@ -83,71 +99,55 @@ private void TestInvoke(Expression> methodCall, int un
// Run mode
var action = BenchmarkActionFactory.CreateWorkload(descriptor, new BenchmarkAllCases(), unrollFactor);
- TestInvoke(action, unrollFactor, false, null);
+ TestInvoke(action, unrollFactor, false);
// Idle mode
action = BenchmarkActionFactory.CreateOverhead(descriptor, new BenchmarkAllCases(), unrollFactor);
- TestInvoke(action, unrollFactor, true, null);
+ TestInvoke(action, unrollFactor, true);
// GlobalSetup/GlobalCleanup
action = BenchmarkActionFactory.CreateGlobalSetup(descriptor, new BenchmarkAllCases());
- TestInvoke(action, 1, false, null);
+ TestInvoke(action, 1, false);
action = BenchmarkActionFactory.CreateGlobalCleanup(descriptor, new BenchmarkAllCases());
- TestInvoke(action, 1, false, null);
+ TestInvoke(action, 1, false);
// GlobalSetup/GlobalCleanup (empty)
descriptor = new Descriptor(typeof(BenchmarkAllCases), targetMethod);
action = BenchmarkActionFactory.CreateGlobalSetup(descriptor, new BenchmarkAllCases());
- TestInvoke(action, unrollFactor, true, null);
+ TestInvoke(action, unrollFactor, true);
action = BenchmarkActionFactory.CreateGlobalCleanup(descriptor, new BenchmarkAllCases());
- TestInvoke(action, unrollFactor, true, null);
+ TestInvoke(action, unrollFactor, true);
// Dummy (just in case something may broke)
action = BenchmarkActionFactory.CreateDummy();
- TestInvoke(action, unrollFactor, true, null);
+ TestInvoke(action, unrollFactor, true);
action = BenchmarkActionFactory.CreateDummy();
- TestInvoke(action, unrollFactor, true, null);
+ TestInvoke(action, unrollFactor, true);
}
[AssertionMethod]
- private void TestInvoke(Expression> methodCall, int unrollFactor, object expectedResult)
+ private void TestInvoke(Expression> methodCall, int unrollFactor)
{
var targetMethod = ((MethodCallExpression)methodCall.Body).Method;
+ TestInvoke(targetMethod, unrollFactor);
+ }
+
+ [AssertionMethod]
+ private void TestInvoke(MethodInfo targetMethod, int unrollFactor)
+ {
var descriptor = new Descriptor(typeof(BenchmarkAllCases), targetMethod);
// Run mode
var action = BenchmarkActionFactory.CreateWorkload(descriptor, new BenchmarkAllCases(), unrollFactor);
- TestInvoke(action, unrollFactor, false, expectedResult);
+ TestInvoke(action, unrollFactor, false);
// Idle mode
-
- bool isValueTask = typeof(T).IsConstructedGenericType && typeof(T).GetGenericTypeDefinition() == typeof(ValueTask<>);
-
- object idleExpected;
- if (isValueTask)
- idleExpected = GetDefault(typeof(T).GetGenericArguments()[0]);
- else if (typeof(T).GetTypeInfo().IsValueType)
- idleExpected = 0;
- else if (expectedResult == null || typeof(T) == typeof(Task))
- idleExpected = null;
- else
- idleExpected = GetDefault(expectedResult.GetType());
-
action = BenchmarkActionFactory.CreateOverhead(descriptor, new BenchmarkAllCases(), unrollFactor);
- TestInvoke(action, unrollFactor, true, idleExpected);
- }
-
- private static object GetDefault(Type type)
- {
- if (type.GetTypeInfo().IsValueType)
- {
- return Activator.CreateInstance(type);
- }
- return null;
+ TestInvoke(action, unrollFactor, true);
}
[AssertionMethod]
- private void TestInvoke(BenchmarkAction benchmarkAction, int unrollFactor, bool isIdle, object expectedResult)
+ private void TestInvoke(BenchmarkAction benchmarkAction, int unrollFactor, bool isIdle)
{
try
{
@@ -171,8 +171,6 @@ private void TestInvoke(BenchmarkAction benchmarkAction, int unrollFactor, bool
benchmarkAction.InvokeMultiple(11);
Assert.Equal(BenchmarkAllCases.Counter, 1 + unrollFactor * 11);
}
-
- Assert.Equal(benchmarkAction.LastRunResult, expectedResult);
}
finally
{
@@ -244,6 +242,13 @@ public async Task InvokeOnceTaskAsync()
Interlocked.Increment(ref Counter);
}
+ [Benchmark]
+ public async ValueTask InvokeOnceValueTaskAsync()
+ {
+ await Task.Yield();
+ Interlocked.Increment(ref Counter);
+ }
+
[Benchmark]
public string InvokeOnceRefType()
{
@@ -272,6 +277,34 @@ public ValueTask InvokeOnceValueTaskOfT()
Interlocked.Increment(ref Counter);
return new ValueTask(DecimalResult);
}
+
+ [Benchmark]
+ public ref int InvokeOnceByRefType()
+ {
+ Interlocked.Increment(ref Counter);
+ return ref Counter;
+ }
+
+ [Benchmark]
+ public ref readonly int InvokeOnceByRefReadonlyType()
+ {
+ Interlocked.Increment(ref Counter);
+ return ref Counter;
+ }
+
+ [Benchmark]
+ public unsafe decimal* InvokeOnceStructPointerType()
+ {
+ Interlocked.Increment(ref Counter);
+ return default;
+ }
+
+ [Benchmark]
+ public unsafe void* InvokeOnceVoidPointerType()
+ {
+ Interlocked.Increment(ref Counter);
+ return default;
+ }
}
}
}
\ No newline at end of file