Skip to content

Commit

Permalink
InProcessNoEmitToolchain changes:
Browse files Browse the repository at this point in the history
- Overhead matches workload return type.
- Use Consumer instead of writing to generic field.
- Added support for pointer, ByRef, and ValueTask returns.
  • Loading branch information
timcassell committed May 27, 2023
1 parent f32a2e7 commit b6f7c74
Show file tree
Hide file tree
Showing 5 changed files with 325 additions and 73 deletions.
8 changes: 5 additions & 3 deletions src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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");
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
using System;

using JetBrains.Annotations;

namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit
{
/// <summary>Common API to run the Setup/Clean/Idle/Run methods</summary>
[PublicAPI]
public abstract class BenchmarkAction
{
/// <summary>Gets or sets invoke single callback.</summary>
Expand All @@ -16,8 +13,7 @@ public abstract class BenchmarkAction
/// <value>Invoke multiple times callback.</value>
public Action<long> InvokeMultiple { get; protected set; }

/// <summary>Gets the last run result.</summary>
/// <value>The last run result.</value>
public virtual object LastRunResult => null;
[Obsolete("The result is no longer stored past the iteration.", true)]
public object LastRunResult => null;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

using BenchmarkDotNet.Extensions;
using BenchmarkDotNet.Running;

using JetBrains.Annotations;

namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit
{
/// <summary>Helper class that creates <see cref="BenchmarkAction"/> instances. </summary>
Expand All @@ -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();
Expand All @@ -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,
Expand Down Expand Up @@ -88,6 +114,10 @@ private static void PrepareInstanceAndResultType(
if (isUsingAsyncKeyword)
throw new NotSupportedException("Async void is not supported by design.");
}
else if (resultType.IsByRefLike())
{
throw new NotSupportedException("InProcessNoEmitToolchain does not support consuming ByRefLike return types.");
}
}

/// <summary>Helper to enforce .ctor signature.</summary>
Expand Down
Loading

0 comments on commit b6f7c74

Please sign in to comment.