Skip to content

Commit 3c3798f

Browse files
committed
InProcessNoEmitToolchain changes:
- Overhead matches workload return type. - Use Consumer instead of writing to generic field. - Added support for pointer, ByRef, and ValueTask returns.
1 parent f32a2e7 commit 3c3798f

File tree

5 files changed

+327
-75
lines changed

5 files changed

+327
-75
lines changed

src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs

+5-3
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,7 @@ internal static bool IsStackOnlyWithImplicitCast(this Type argumentType, object?
185185
if (argumentInstance == null)
186186
return false;
187187

188-
// IsByRefLikeAttribute is not exposed for older runtimes, so we need to check it in an ugly way ;)
189-
bool isByRefLike = argumentType.GetCustomAttributes().Any(attribute => attribute.ToString()?.Contains("IsByRefLike") ?? false);
190-
if (!isByRefLike)
188+
if (!argumentType.IsByRefLike())
191189
return false;
192190

193191
var instanceType = argumentInstance.GetType();
@@ -209,5 +207,9 @@ private static bool IsRunnableGenericType(TypeInfo typeInfo)
209207
&& typeInfo.DeclaredConstructors.Any(ctor => ctor.IsPublic && ctor.GetParameters().Length == 0); // we need public parameterless ctor to create it
210208

211209
internal static bool IsLinqPad(this Assembly assembly) => assembly.FullName.IndexOf("LINQPAD", StringComparison.OrdinalIgnoreCase) >= 0;
210+
211+
internal static bool IsByRefLike(this Type type)
212+
// Type.IsByRefLike is not available in netstandard2.0.
213+
=> type.IsValueType && type.CustomAttributes.Any(attr => attr.AttributeType.FullName == "System.Runtime.CompilerServices.IsByRefLikeAttribute");
212214
}
213215
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
using System;
22

3-
using JetBrains.Annotations;
4-
53
namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit
64
{
75
/// <summary>Common API to run the Setup/Clean/Idle/Run methods</summary>
8-
[PublicAPI]
96
public abstract class BenchmarkAction
107
{
118
/// <summary>Gets or sets invoke single callback.</summary>
@@ -16,8 +13,7 @@ public abstract class BenchmarkAction
1613
/// <value>Invoke multiple times callback.</value>
1714
public Action<long> InvokeMultiple { get; protected set; }
1815

19-
/// <summary>Gets the last run result.</summary>
20-
/// <value>The last run result.</value>
21-
public virtual object LastRunResult => null;
16+
[Obsolete("The result is no longer stored past the iteration.", true)]
17+
public object LastRunResult => null;
2218
}
2319
}

src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory.cs

+36-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
using System;
2+
using System.Linq;
23
using System.Reflection;
34
using System.Runtime.CompilerServices;
45
using System.Threading.Tasks;
56

67
using BenchmarkDotNet.Extensions;
78
using BenchmarkDotNet.Running;
89

9-
using JetBrains.Annotations;
10-
1110
namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit
1211
{
1312
/// <summary>Helper class that creates <see cref="BenchmarkAction"/> instances. </summary>
@@ -29,9 +28,40 @@ private static BenchmarkAction CreateCore(
2928
if (resultType == typeof(void))
3029
return new BenchmarkActionVoid(resultInstance, targetMethod, unrollFactor);
3130

31+
if (resultType == typeof(void*))
32+
return new BenchmarkActionVoidPointer(resultInstance, targetMethod, unrollFactor);
33+
34+
if (resultType.IsPointer)
35+
return Create(
36+
typeof(BenchmarkActionPointer<>).MakeGenericType(resultType.GetElementType()),
37+
resultInstance,
38+
targetMethod,
39+
unrollFactor);
40+
41+
if (resultType.IsByRef)
42+
{
43+
var returnParameter = targetMethod?.ReturnParameter ?? fallbackIdleSignature.ReturnParameter;
44+
// System.Runtime.CompilerServices.IsReadOnlyAttribute is part of .NET Standard 2.1, we can't use it here..
45+
if (returnParameter.GetCustomAttributes().Any(attribute => attribute.GetType().Name == "IsReadOnlyAttribute"))
46+
return Create(
47+
typeof(BenchmarkActionByRefReadonly<>).MakeGenericType(resultType.GetElementType()),
48+
resultInstance,
49+
targetMethod,
50+
unrollFactor);
51+
52+
return Create(
53+
typeof(BenchmarkActionByRef<>).MakeGenericType(resultType.GetElementType()),
54+
resultInstance,
55+
targetMethod,
56+
unrollFactor);
57+
}
58+
3259
if (resultType == typeof(Task))
3360
return new BenchmarkActionTask(resultInstance, targetMethod, unrollFactor);
3461

62+
if (resultType == typeof(ValueTask))
63+
return new BenchmarkActionValueTask(resultInstance, targetMethod, unrollFactor);
64+
3565
if (resultType.GetTypeInfo().IsGenericType)
3666
{
3767
var genericType = resultType.GetGenericTypeDefinition();
@@ -51,10 +81,6 @@ private static BenchmarkAction CreateCore(
5181
unrollFactor);
5282
}
5383

54-
if (targetMethod == null && resultType.GetTypeInfo().IsValueType)
55-
// for Idle: we return int because creating bigger ValueType could take longer than benchmarked method itself.
56-
resultType = typeof(int);
57-
5884
return Create(
5985
typeof(BenchmarkAction<>).MakeGenericType(resultType),
6086
resultInstance,
@@ -88,6 +114,10 @@ private static void PrepareInstanceAndResultType(
88114
if (isUsingAsyncKeyword)
89115
throw new NotSupportedException("Async void is not supported by design.");
90116
}
117+
else if (resultType.IsByRefLike() || resultType?.GetElementType()?.IsByRefLike() == true)
118+
{
119+
throw new NotSupportedException("InProcessNoEmitToolchain does not support consuming ByRefLike return types.");
120+
}
91121
}
92122

93123
/// <summary>Helper to enforce .ctor signature.</summary>

0 commit comments

Comments
 (0)