Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/BenchmarkDotNet/BenchmarkDotNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<AssemblyTitle>BenchmarkDotNet</AssemblyTitle>
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net6.0;net8.0;net9.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NoWarn>$(NoWarn);1701;1702;1705;1591;3005;NU1702;CS3001;CS3003</NoWarn>
<AssemblyName>BenchmarkDotNet</AssemblyName>
Expand Down
12 changes: 9 additions & 3 deletions src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,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 @@ -235,5 +233,13 @@ 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)
#if NETSTANDARD2_0
// Type.IsByRefLike is not available in netstandard2.0.
=> type.IsValueType && type.CustomAttributes.Any(attr => attr.AttributeType.FullName == "System.Runtime.CompilerServices.IsByRefLikeAttribute");
#else
=> type.IsByRefLike;
#endif
}
}
6 changes: 6 additions & 0 deletions src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,12 @@ protected void GatherReferences(string projectFilePath, BuildPartition buildPart
// TODO: Add Aliases here for extern alias #2289
}

// Mono80IsSupported test fails when BenchmarkDotNet is restored for net9.0 if we don't remove the ProjectReference.
if (XUnitHelper.IsIntegrationTest.Value)
{
projectElement.RemoveChild(projectElement.SelectSingleNode("ItemGroup/ProjectReference").ParentNode);
}

xmlDoc.Save(artifactsPaths.ProjectFilePath);

BuildResult BuildProject(string tfm)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
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
internal abstract class BenchmarkAction
{
/// <summary>Gets or sets invoke single callback.</summary>
/// <value>Invoke single callback.</value>
Expand All @@ -16,9 +13,5 @@ public abstract class BenchmarkAction
/// <value>Invoke multiple times callback.</value>
public Action<long> InvokeUnroll { get; protected set; }
public Action<long> InvokeNoUnroll{ get; protected set; }

/// <summary>Gets the last run result.</summary>
/// <value>The last run result.</value>
public virtual object LastRunResult => null;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
Expand All @@ -9,27 +10,47 @@
namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit
{
/// <summary>Helper class that creates <see cref="BenchmarkAction"/> instances. </summary>
public static partial class BenchmarkActionFactory
internal static partial class BenchmarkActionFactory
{
/// <summary>
/// Dispatch method that creates <see cref="BenchmarkAction"/> using
/// <paramref name="targetMethod"/> or <paramref name="fallbackIdleSignature"/> to find correct implementation.
/// Either <paramref name="targetMethod"/> or <paramref name="fallbackIdleSignature"/> should be not <c>null</c>.
/// </summary>
private static BenchmarkAction CreateCore(
object instance,
MethodInfo? targetMethod,
MethodInfo? fallbackIdleSignature,
int unrollFactor)
private static BenchmarkAction CreateCore(object instance, MethodInfo? targetMethod, MethodInfo? fallbackIdleSignature, int unrollFactor)
{
PrepareInstanceAndResultType(instance, targetMethod, fallbackIdleSignature, out var resultInstance, out var resultType);

if (resultType == typeof(void))
return new BenchmarkActionVoid(resultInstance, targetMethod, unrollFactor);

if (resultType == typeof(void*))
return new BenchmarkActionVoidPointer(resultInstance, targetMethod, unrollFactor);

if (resultType.IsByRef)
{
var returnParameter = targetMethod?.ReturnParameter ?? fallbackIdleSignature.ReturnParameter;
// IsReadOnlyAttribute is not part of netstandard2.0, so we need to check the attribute name as usual.
if (returnParameter.GetCustomAttributes().Any(attribute => attribute.GetType().FullName == "System.Runtime.CompilerServices.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 @@ -49,10 +70,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 @@ -86,6 +103,10 @@ private static void PrepareInstanceAndResultType(
if (isUsingAsyncKeyword)
throw new NotSupportedException("Async void is not supported by design.");
}
else if (resultType.IsPointer && resultType != typeof(void*))
{
throw new NotSupportedException("InProcessNoEmitToolchain only supports void* return, not T*");
}
}

/// <summary>Helper to enforce .ctor signature.</summary>
Expand All @@ -109,7 +130,7 @@ public static BenchmarkAction CreateWorkload(Descriptor descriptor, object insta
/// <param name="unrollFactor">Unroll factor.</param>
/// <returns>Idle benchmark action.</returns>
public static BenchmarkAction CreateOverhead(Descriptor descriptor, object instance, int unrollFactor) =>
CreateCore(instance, null, descriptor.WorkloadMethod, unrollFactor);
CreateCore(instance, null, FallbackSignature, unrollFactor);

/// <summary>Creates global setup benchmark action.</summary>
/// <param name="descriptor">Descriptor info.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ 5. Implementation should match to the code in BenchmarkProgram.txt.

// DONTTOUCH: Be VERY CAREFUL when changing the code.
// Please, ensure that the implementation is in sync with content of BenchmarkProgram.txt

/// <summary>Helper class that creates <see cref="BenchmarkAction"/> instances. </summary>
public static partial class BenchmarkActionFactory
internal static partial class BenchmarkActionFactory
{
/// <summary>Base class that provides reusable API for final implementations.</summary>
internal abstract class BenchmarkActionBase : BenchmarkAction
Expand All @@ -35,19 +33,15 @@ protected static TDelegate CreateWorkload<TDelegate>(object? targetInstance, Met
return (TDelegate)(object)workloadMethod.CreateDelegate(typeof(TDelegate), targetInstance);
}

protected static TDelegate CreateWorkloadOrOverhead<TDelegate>(
object? targetInstance,
MethodInfo? workloadMethod,
TDelegate overheadStaticCallback,
TDelegate overheadInstanceCallback) where TDelegate : notnull
protected Action CreateWorkloadOrOverhead(object? instance, MethodInfo? method)
{
if (workloadMethod == null)
return targetInstance == null ? overheadStaticCallback : overheadInstanceCallback;

if (workloadMethod.IsStatic)
return (TDelegate)(object)workloadMethod.CreateDelegate(typeof(TDelegate));

return (TDelegate)(object)workloadMethod.CreateDelegate(typeof(TDelegate), targetInstance);
if (method == null)
{
return instance == null ? OverheadStatic : OverheadInstance;
}
return method.IsStatic
? (Action) method.CreateDelegate(typeof(Action))
: (Action) method.CreateDelegate(typeof(Action), instance);
}

protected static TDelegate Unroll<TDelegate>(TDelegate callback, int unrollFactor)
Expand All @@ -61,6 +55,11 @@ protected static TDelegate Unroll<TDelegate>(TDelegate callback, int unrollFacto
return (TDelegate)(object)Delegate.Combine(
Enumerable.Repeat((Delegate)(object)callback, unrollFactor).ToArray());
}

// must be kept in sync with VoidDeclarationsProvider.IdleImplementation
private static void OverheadStatic() { }

private void OverheadInstance() { }
}
}
}
Loading