Skip to content

Commit

Permalink
Added missing ValueTask (non-generic) to InProcess (no emit) toolchains.
Browse files Browse the repository at this point in the history
  • Loading branch information
timcassell committed Feb 17, 2023
1 parent 43138da commit c51c486
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ public static partial class BenchmarkActionFactory
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 Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,45 @@ private void InvokeMultipleHardcoded(long repeatCount)
public override object LastRunResult => result;
}

internal class BenchmarkActionValueTask : BenchmarkActionBase
{
private readonly Func<ValueTask> startTaskCallback;
private readonly Action callback;
private readonly Action unrolledCallback;

public BenchmarkActionValueTask(object instance, MethodInfo method, int unrollFactor)
{
bool isIdle = method == null;
if (!isIdle)
{
startTaskCallback = CreateWorkload<Func<ValueTask>>(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() => Helpers.AwaitHelper.GetResult(startTaskCallback.Invoke());

private void InvokeMultipleHardcoded(long repeatCount)
{
for (long i = 0; i < repeatCount; i++)
unrolledCallback();
}
}

internal class BenchmarkActionValueTask<T> : BenchmarkActionBase
{
private readonly Func<ValueTask<T>> startTaskCallback;
Expand Down
184 changes: 163 additions & 21 deletions tests/BenchmarkDotNet.IntegrationTests/InProcessTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public InProcessTest(ITestOutputHelper output) : base(output)
[Fact]
public void BenchmarkActionTaskSupported() => TestInvoke(x => x.InvokeOnceTaskAsync(), UnrollFactor, null);

[Fact]
public void BenchmarkActionValueTaskSupported() => TestInvoke(x => x.InvokeOnceValueTaskAsync(), UnrollFactor, null);

[Fact]
public void BenchmarkActionRefTypeSupported() => TestInvoke(x => x.InvokeOnceRefType(), UnrollFactor, StringResult);

Expand All @@ -57,6 +60,18 @@ public InProcessTest(ITestOutputHelper output) : base(output)
[Fact]
public void BenchmarkActionValueTaskOfTSupported() => TestInvoke(x => x.InvokeOnceValueTaskOfT(), UnrollFactor, DecimalResult);

[Fact]
public void BenchmarkActionGlobalSetupTaskSupported() => TestInvokeSetupCleanupTask(x => BenchmarkSetupCleanupTask.GlobalSetup(), UnrollFactor);

[Fact]
public void BenchmarkActionGlobalCleanupTaskSupported() => TestInvokeSetupCleanupTask(x => x.GlobalCleanup(), UnrollFactor);

[Fact]
public void BenchmarkActionGlobalSetupValueTaskSupported() => TestInvokeSetupCleanupValueTask(x => BenchmarkSetupCleanupValueTask.GlobalSetup(), UnrollFactor);

[Fact]
public void BenchmarkActionGlobalCleanupValueTaskSupported() => TestInvokeSetupCleanupValueTask(x => x.GlobalCleanup(), UnrollFactor);

[Fact]
public void BenchmarkDifferentPlatformReturnsValidationError()
{
Expand All @@ -83,30 +98,30 @@ private void TestInvoke(Expression<Action<BenchmarkAllCases>> methodCall, int un

// Run mode
var action = BenchmarkActionFactory.CreateWorkload(descriptor, new BenchmarkAllCases(), unrollFactor);
TestInvoke(action, unrollFactor, false, null);
TestInvoke(action, unrollFactor, false, null, ref BenchmarkAllCases.Counter);

// Idle mode
action = BenchmarkActionFactory.CreateOverhead(descriptor, new BenchmarkAllCases(), unrollFactor);
TestInvoke(action, unrollFactor, true, null);
TestInvoke(action, unrollFactor, true, null, ref BenchmarkAllCases.Counter);

// GlobalSetup/GlobalCleanup
action = BenchmarkActionFactory.CreateGlobalSetup(descriptor, new BenchmarkAllCases());
TestInvoke(action, 1, false, null);
TestInvoke(action, 1, false, null, ref BenchmarkAllCases.Counter);
action = BenchmarkActionFactory.CreateGlobalCleanup(descriptor, new BenchmarkAllCases());
TestInvoke(action, 1, false, null);
TestInvoke(action, 1, false, null, ref BenchmarkAllCases.Counter);

// GlobalSetup/GlobalCleanup (empty)
descriptor = new Descriptor(typeof(BenchmarkAllCases), targetMethod);
action = BenchmarkActionFactory.CreateGlobalSetup(descriptor, new BenchmarkAllCases());
TestInvoke(action, unrollFactor, true, null);
TestInvoke(action, unrollFactor, true, null, ref BenchmarkAllCases.Counter);
action = BenchmarkActionFactory.CreateGlobalCleanup(descriptor, new BenchmarkAllCases());
TestInvoke(action, unrollFactor, true, null);
TestInvoke(action, unrollFactor, true, null, ref BenchmarkAllCases.Counter);

// Dummy (just in case something may broke)
action = BenchmarkActionFactory.CreateDummy();
TestInvoke(action, unrollFactor, true, null);
TestInvoke(action, unrollFactor, true, null, ref BenchmarkAllCases.Counter);
action = BenchmarkActionFactory.CreateDummy();
TestInvoke(action, unrollFactor, true, null);
TestInvoke(action, unrollFactor, true, null, ref BenchmarkAllCases.Counter);
}

[AssertionMethod]
Expand All @@ -117,7 +132,7 @@ private void TestInvoke<T>(Expression<Func<BenchmarkAllCases, T>> methodCall, in

// Run mode
var action = BenchmarkActionFactory.CreateWorkload(descriptor, new BenchmarkAllCases(), unrollFactor);
TestInvoke(action, unrollFactor, false, expectedResult);
TestInvoke(action, unrollFactor, false, expectedResult, ref BenchmarkAllCases.Counter);

// Idle mode

Expand All @@ -126,15 +141,83 @@ private void TestInvoke<T>(Expression<Func<BenchmarkAllCases, T>> methodCall, in
object idleExpected;
if (isValueTask)
idleExpected = GetDefault(typeof(T).GetGenericArguments()[0]);
else if (expectedResult == null || typeof(T) == typeof(Task) || typeof(T) == typeof(ValueTask))
idleExpected = null;
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);
TestInvoke(action, unrollFactor, true, idleExpected, ref BenchmarkAllCases.Counter);
}

[AssertionMethod]
private void TestInvokeSetupCleanupTask(Expression<Func<BenchmarkSetupCleanupTask, Task>> methodCall, int unrollFactor)
{
var targetMethod = ((MethodCallExpression) methodCall.Body).Method;
var descriptor = new Descriptor(typeof(BenchmarkSetupCleanupTask), targetMethod, targetMethod, targetMethod, targetMethod, targetMethod);

// Run mode
var action = BenchmarkActionFactory.CreateWorkload(descriptor, new BenchmarkSetupCleanupTask(), unrollFactor);
TestInvoke(action, unrollFactor, false, null, ref BenchmarkSetupCleanupTask.Counter);

// Idle mode
action = BenchmarkActionFactory.CreateOverhead(descriptor, new BenchmarkSetupCleanupTask(), unrollFactor);
TestInvoke(action, unrollFactor, true, null, ref BenchmarkSetupCleanupTask.Counter);

// GlobalSetup/GlobalCleanup
action = BenchmarkActionFactory.CreateGlobalSetup(descriptor, new BenchmarkSetupCleanupTask());
TestInvoke(action, 1, false, null, ref BenchmarkSetupCleanupTask.Counter);
action = BenchmarkActionFactory.CreateGlobalCleanup(descriptor, new BenchmarkSetupCleanupTask());
TestInvoke(action, 1, false, null, ref BenchmarkSetupCleanupTask.Counter);

// GlobalSetup/GlobalCleanup (empty)
descriptor = new Descriptor(typeof(BenchmarkSetupCleanupTask), targetMethod);
action = BenchmarkActionFactory.CreateGlobalSetup(descriptor, new BenchmarkSetupCleanupTask());
TestInvoke(action, unrollFactor, true, null, ref BenchmarkSetupCleanupTask.Counter);
action = BenchmarkActionFactory.CreateGlobalCleanup(descriptor, new BenchmarkSetupCleanupTask());
TestInvoke(action, unrollFactor, true, null, ref BenchmarkSetupCleanupTask.Counter);

// Dummy (just in case something may broke)
action = BenchmarkActionFactory.CreateDummy();
TestInvoke(action, unrollFactor, true, null, ref BenchmarkSetupCleanupTask.Counter);
action = BenchmarkActionFactory.CreateDummy();
TestInvoke(action, unrollFactor, true, null, ref BenchmarkSetupCleanupTask.Counter);
}

[AssertionMethod]
private void TestInvokeSetupCleanupValueTask(Expression<Func<BenchmarkSetupCleanupValueTask, ValueTask>> methodCall, int unrollFactor)
{
var targetMethod = ((MethodCallExpression) methodCall.Body).Method;
var descriptor = new Descriptor(typeof(BenchmarkSetupCleanupValueTask), targetMethod, targetMethod, targetMethod, targetMethod, targetMethod);

// Run mode
var action = BenchmarkActionFactory.CreateWorkload(descriptor, new BenchmarkSetupCleanupValueTask(), unrollFactor);
TestInvoke(action, unrollFactor, false, null, ref BenchmarkSetupCleanupValueTask.Counter);

// Idle mode
action = BenchmarkActionFactory.CreateOverhead(descriptor, new BenchmarkSetupCleanupValueTask(), unrollFactor);
TestInvoke(action, unrollFactor, true, null, ref BenchmarkSetupCleanupValueTask.Counter);

// GlobalSetup/GlobalCleanup
action = BenchmarkActionFactory.CreateGlobalSetup(descriptor, new BenchmarkSetupCleanupValueTask());
TestInvoke(action, 1, false, null, ref BenchmarkSetupCleanupValueTask.Counter);
action = BenchmarkActionFactory.CreateGlobalCleanup(descriptor, new BenchmarkSetupCleanupValueTask());
TestInvoke(action, 1, false, null, ref BenchmarkSetupCleanupValueTask.Counter);

// GlobalSetup/GlobalCleanup (empty)
descriptor = new Descriptor(typeof(BenchmarkSetupCleanupValueTask), targetMethod);
action = BenchmarkActionFactory.CreateGlobalSetup(descriptor, new BenchmarkSetupCleanupValueTask());
TestInvoke(action, unrollFactor, true, null, ref BenchmarkSetupCleanupValueTask.Counter);
action = BenchmarkActionFactory.CreateGlobalCleanup(descriptor, new BenchmarkSetupCleanupValueTask());
TestInvoke(action, unrollFactor, true, null, ref BenchmarkSetupCleanupValueTask.Counter);

// Dummy (just in case something may broke)
action = BenchmarkActionFactory.CreateDummy();
TestInvoke(action, unrollFactor, true, null, ref BenchmarkSetupCleanupValueTask.Counter);
action = BenchmarkActionFactory.CreateDummy();
TestInvoke(action, unrollFactor, true, null, ref BenchmarkSetupCleanupValueTask.Counter);
}

private static object GetDefault(Type type)
Expand All @@ -147,36 +230,36 @@ private static object GetDefault(Type type)
}

[AssertionMethod]
private void TestInvoke(BenchmarkAction benchmarkAction, int unrollFactor, bool isIdle, object expectedResult)
private void TestInvoke(BenchmarkAction benchmarkAction, int unrollFactor, bool isIdle, object expectedResult, ref int counter)
{
try
{
BenchmarkAllCases.Counter = 0;
counter = 0;

if (isIdle)
{
benchmarkAction.InvokeSingle();
Assert.Equal(0, BenchmarkAllCases.Counter);
Assert.Equal(0, counter);
benchmarkAction.InvokeMultiple(0);
Assert.Equal(0, BenchmarkAllCases.Counter);
Assert.Equal(0, counter);
benchmarkAction.InvokeMultiple(11);
Assert.Equal(0, BenchmarkAllCases.Counter);
Assert.Equal(0, counter);
}
else
{
benchmarkAction.InvokeSingle();
Assert.Equal(1, BenchmarkAllCases.Counter);
Assert.Equal(1, counter);
benchmarkAction.InvokeMultiple(0);
Assert.Equal(1, BenchmarkAllCases.Counter);
Assert.Equal(1, counter);
benchmarkAction.InvokeMultiple(11);
Assert.Equal(BenchmarkAllCases.Counter, 1 + unrollFactor * 11);
Assert.Equal(1 + unrollFactor * 11, counter);
}

Assert.Equal(benchmarkAction.LastRunResult, expectedResult);
}
finally
{
BenchmarkAllCases.Counter = 0;
counter = 0;
}
}

Expand Down Expand Up @@ -244,6 +327,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()
{
Expand Down Expand Up @@ -273,5 +363,57 @@ public ValueTask<decimal> InvokeOnceValueTaskOfT()
return new ValueTask<decimal>(DecimalResult);
}
}

[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
public class BenchmarkSetupCleanupTask
{
public static int Counter;

[GlobalSetup]
public static async Task GlobalSetup()
{
await Task.Yield();
Interlocked.Increment(ref Counter);
}

[GlobalCleanup]
public async Task GlobalCleanup()
{
await Task.Yield();
Interlocked.Increment(ref Counter);
}

[Benchmark]
public void InvokeOnceVoid()
{
Interlocked.Increment(ref Counter);
}
}

[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
public class BenchmarkSetupCleanupValueTask
{
public static int Counter;

[GlobalSetup]
public static async ValueTask GlobalSetup()
{
await Task.Yield();
Interlocked.Increment(ref Counter);
}

[GlobalCleanup]
public async ValueTask GlobalCleanup()
{
await Task.Yield();
Interlocked.Increment(ref Counter);
}

[Benchmark]
public void InvokeOnceVoid()
{
Interlocked.Increment(ref Counter);
}
}
}
}

0 comments on commit c51c486

Please sign in to comment.