Skip to content

Instantly share code, notes, and snippets.

@ReubenBond
Last active December 22, 2016 21:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ReubenBond/445a7cd47bf403b323f6 to your computer and use it in GitHub Desktop.
Save ReubenBond/445a7cd47bf403b323f6 to your computer and use it in GitHub Desktop.
Perf test for comparing await vs. ContinueWith in Orleans GrainMethodInvokers
using System;
using System.Threading.Tasks;
namespace AwaitVersusContinueWith
{
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using BenchmarkDotNet;
public static class Program
{
public delegate Task<object> Invoker(
IReminderTableGrain grain,
int interfaceId,
int methodId,
object[] arguments);
public class Benchmarks
{
private static readonly List<KeyValuePair<string, Invoker>> Tests =
new Dictionary<string, Invoker>
{
{ "await", InvokeAwait },
{ "await in Box", InvokeBoxAwait },
{ "Box ExecuteSynchronously", InvokeBoxSync },
{ "Box optimized", InvokeBoxMaySync },
{ "Inlined ContinueWith", InvokeContinueWith },
{ "Box (Current Orleans)", InvokeBox }
}.ToList();
[Params(1, 2, 3, 4, 5, 6, 7, 8/*, 9, 10*/)]
public int methodId;
private static readonly ReminderTableGrain Grain = new ReminderTableGrain();
[Benchmark]
public void InvokeAwaitTest()
{
InvokeAwait(Grain, 0, this.methodId, null).Wait();
}
[Benchmark]
public void InvokeBoxAwaitTest()
{
InvokeBoxAwait(Grain, 0, this.methodId, null).Wait();
}
[Benchmark]
public void InvokeBoxSyncTest()
{
InvokeBoxSync(Grain, 0, this.methodId, null).Wait();
}
[Benchmark]
public void InvokeBoxMaySyncTest()
{
InvokeBoxMaySync(Grain, 0, this.methodId, null).Wait();
}
[Benchmark]
public void InvokeContinueWithTest()
{
InvokeContinueWith(Grain, 0, this.methodId, null).Wait();
}
[Benchmark]
public void InvokeBoxTest()
{
InvokeBox(Grain, 0, this.methodId, null).Wait();
}
}
public static void Main(string[] args)
{
new BenchmarkRunner().RunCompetition(new Benchmarks());
/*
var tests = new Dictionary<string, Invoker>
{
{ "await", InvokeAwait },
{ "await in Box", InvokeBoxAwait },
{ "Box ExecuteSynchronously", InvokeBoxSync },
{ "Box optimized", InvokeBoxMaySync },
{ "Inlined ContinueWith", InvokeContinueWith },
{ "Box (Current Orleans)", InvokeBox }
};
var methods = new Dictionary<string, bool>
{
{ "completed Task", false },
{ "completed Task<int>", false },
{ "completed Task<string>", false },
{ "completed Task<object>", false },
{ "yielded Task", false },
{ "yielded Task<int>", false },
{ "yielded Task<string>", false },
{ "yielded Task<object>", false },
{ "Faulted", true },
{ "Cancelled", true }
};
var grain = new ReminderTableGrain();
Console.WriteLine($",{string.Join(",", tests.Keys)}");
var i = 0;
foreach (var method in methods)
{
Console.Write($"{method.Key}");
var methodId = ++i;
foreach (var test in tests)
{
var elapsed = Tester(() => test.Value(grain, 0, methodId, null), method.Value);
Console.Write($",{elapsed}");
}
Console.WriteLine();
}*/
//Console.ReadKey();
}
static Task<object> Box<T>(Task<T> task)
{
return task.ContinueWith(t => (object)t.GetAwaiter().GetResult());
}
static Task<object> Box(Task task)
{
return task.ContinueWith(
t =>
{
t.GetAwaiter().GetResult();
return default(object);
});
}
static Task<object> BoxSync<T>(Task<T> task)
{
return task.ContinueWith(
t => (object)t.GetAwaiter().GetResult(),
TaskContinuationOptions.ExecuteSynchronously);
}
static Task<object> BoxSync(Task task)
{
return task.ContinueWith(
t =>
{
t.GetAwaiter().GetResult();
return default(object);
},
TaskContinuationOptions.ExecuteSynchronously);
}
static Task<object> BoxMaySync(Task<object> task)
{
return task;
}
static Task<object> BoxMaySync<T>(Task<T> task)
{
switch (task.Status)
{
case TaskStatus.RanToCompletion:
return Task.FromResult((object)task.GetAwaiter().GetResult());
case TaskStatus.Faulted:
{
var completion = new TaskCompletionSource<object>();
completion.SetException(task.Exception);
return completion.Task;
}
case TaskStatus.Canceled:
{
var completion = new TaskCompletionSource<object>();
completion.SetCanceled();
return completion.Task;
}
default:
return BoxAwait(task);
}
}
static Task<object> BoxMaySync(Task task)
{
switch (task.Status)
{
case TaskStatus.RanToCompletion:
return Task.FromResult(default(object));
case TaskStatus.Faulted:
{
var completion = new TaskCompletionSource<object>();
completion.SetException(task.Exception);
return completion.Task;
}
case TaskStatus.Canceled:
{
var completion = new TaskCompletionSource<object>();
completion.SetCanceled();
return completion.Task;
}
default:
return BoxAwait(task);
}
}
static async Task<object> BoxAwait<T>(Task<T> task)
{
return await task;
}
static async Task<object> BoxAwait(Task task)
{
await task;
return null;
}
static long Tester(Func<Task> func, bool shouldThrow)
{
GC.Collect();
var sw = new Stopwatch();
const int WarmupIterations = 10;
const int NormalIterations = 10000000 + WarmupIterations;
const int BadIterations = 100000 + WarmupIterations;
var iterations = shouldThrow ? BadIterations : NormalIterations;
for (var i = 0; i < iterations; i++)
{
if (i == WarmupIterations)
{
sw.Start();
}
try
{
func().GetAwaiter().GetResult();
if (shouldThrow)
{
throw new Exception("FAILED TO THROW");
}
}
catch
{
if (!shouldThrow)
{
throw;
}
}
}
sw.Stop();
return sw.ElapsedMilliseconds;
}
// Define other methods and classes here
public static async Task<object> InvokeAwait(
IReminderTableGrain grain,
int interfaceId,
int methodId,
object[] arguments)
{
if (grain == null) throw new ArgumentNullException("grain");
switch (interfaceId)
{
case 0:
switch (methodId)
{
case 1:
await grain.Go();
return null;
case 2:
return await grain.GoInt();
case 3:
return await grain.GoString();
case 4:
return await grain.GoObject();
case 5:
await grain.Go2();
return null;
case 6:
return await grain.GoInt2();
case 7:
return await grain.GoString2();
case 8:
return await grain.GoObject2();
case 9:
return await grain.GoFault();
case 10:
return await grain.GoCancel();
default:
throw new NotImplementedException("interfaceId=" + -1135060418 + ",methodId=" + methodId);
}
default:
throw new NotImplementedException("interfaceId=" + interfaceId);
}
}
public static Task<object> InvokeContinueWith(
IReminderTableGrain grain,
int interfaceId,
int methodId,
object[] arguments)
{
if (grain == null) throw new ArgumentNullException("grain");
switch (interfaceId)
{
case 0:
switch (methodId)
{
case 1:
return
grain.Go()
.ContinueWith(_ =>
{
_.Wait();
return default(object);
});
case 2:
return grain.GoInt().ContinueWith(_ => (object)_.Result);
case 3:
return grain.GoString().ContinueWith(_ => (object)_.Result);
case 4:
return grain.GoObject().ContinueWith(_ => (object)_.Result);
case 5:
return grain.Go2()
.ContinueWith(_ =>
{
_.Wait();
return default(object);
});
case 6:
return grain.GoInt2().ContinueWith(_ => (object)_.Result);
case 7:
return grain.GoString2().ContinueWith(_ => (object)_.Result);
case 8:
return grain.GoObject2().ContinueWith(_ => (object)_.Result);
case 9:
return grain.GoFault().ContinueWith(_ => (object)_.Result);
case 10:
return grain.GoCancel().ContinueWith(_ => (object)_.Result);
default:
throw new NotImplementedException("interfaceId=" + -1135060418 + ",methodId=" + methodId);
}
default:
throw new NotImplementedException("interfaceId=" + interfaceId);
}
}
public static Task<object> InvokeBox(
IReminderTableGrain grain,
int interfaceId,
int methodId,
object[] arguments)
{
if (grain == null) throw new ArgumentNullException("grain");
switch (interfaceId)
{
case 0:
switch (methodId)
{
case 1:
return Box(grain.Go());
case 2:
return Box(grain.GoInt());
case 3:
return Box(grain.GoString());
case 4:
return Box(grain.GoObject());
case 5:
return Box(grain.Go2());
case 6:
return Box(grain.GoInt2());
case 7:
return Box(grain.GoString2());
case 8:
return Box(grain.GoObject2());
case 9:
return Box(grain.GoFault());
case 10:
return Box(grain.GoCancel());
default:
throw new NotImplementedException("interfaceId=" + -1135060418 + ",methodId=" + methodId);
}
default:
throw new NotImplementedException("interfaceId=" + interfaceId);
}
}
public static Task<object> InvokeBoxMaySync(
IReminderTableGrain grain,
int interfaceId,
int methodId,
object[] arguments)
{
if (grain == null) throw new ArgumentNullException("grain");
switch (interfaceId)
{
case 0:
switch (methodId)
{
case 1:
return BoxMaySync(grain.Go());
case 2:
return BoxMaySync(grain.GoInt());
case 3:
return BoxMaySync(grain.GoString());
case 4:
return BoxMaySync(grain.GoObject());
case 5:
return BoxMaySync(grain.Go2());
case 6:
return BoxMaySync(grain.GoInt2());
case 7:
return BoxMaySync(grain.GoString2());
case 8:
return BoxMaySync(grain.GoObject2());
case 9:
return BoxMaySync(grain.GoFault());
case 10:
return BoxMaySync(grain.GoCancel());
default:
throw new NotImplementedException("interfaceId=" + -1135060418 + ",methodId=" + methodId);
}
default:
throw new NotImplementedException("interfaceId=" + interfaceId);
}
}
public static Task<object> InvokeBoxSync(
IReminderTableGrain grain,
int interfaceId,
int methodId,
object[] arguments)
{
if (grain == null) throw new ArgumentNullException("grain");
switch (interfaceId)
{
case 0:
switch (methodId)
{
case 1:
return BoxSync(grain.Go());
case 2:
return BoxSync(grain.GoInt());
case 3:
return BoxSync(grain.GoString());
case 4:
return BoxSync(grain.GoObject());
case 5:
return BoxSync(grain.Go2());
case 6:
return BoxSync(grain.GoInt2());
case 7:
return BoxSync(grain.GoString2());
case 8:
return BoxSync(grain.GoObject2());
case 9:
return BoxSync(grain.GoFault());
case 10:
return BoxSync(grain.GoCancel());
default:
throw new NotImplementedException("interfaceId=" + -1135060418 + ",methodId=" + methodId);
}
default:
throw new NotImplementedException("interfaceId=" + interfaceId);
}
}
public static Task<object> InvokeBoxAwait(
IReminderTableGrain grain,
int interfaceId,
int methodId,
object[] arguments)
{
if (grain == null) throw new ArgumentNullException("grain");
switch (interfaceId)
{
case 0:
switch (methodId)
{
case 1:
return BoxAwait(grain.Go());
case 2:
return BoxAwait(grain.GoInt());
case 3:
return BoxAwait(grain.GoString());
case 4:
return BoxAwait(grain.GoObject());
case 5:
return BoxAwait(grain.Go2());
case 6:
return BoxAwait(grain.GoInt2());
case 7:
return BoxAwait(grain.GoString2());
case 8:
return BoxAwait(grain.GoObject2());
case 9:
return BoxAwait(grain.GoFault());
case 10:
return BoxAwait(grain.GoCancel());
default:
throw new NotImplementedException("interfaceId=" + -1135060418 + ",methodId=" + methodId);
}
default:
throw new NotImplementedException("interfaceId=" + interfaceId);
}
}
public interface IReminderTableGrain
{
Task Go();
Task<int> GoInt();
Task<string> GoString();
Task<object> GoObject();
Task Go2();
Task<int> GoInt2();
Task<string> GoString2();
Task<object> GoObject2();
Task<string> GoFault();
Task<object> GoCancel();
}
public class ReminderTableGrain : IReminderTableGrain
{
private static readonly TaskCompletionSource<string> faulted;
private static readonly TaskCompletionSource<object> cancelled;
static ReminderTableGrain()
{
faulted = new TaskCompletionSource<string>();
faulted.TrySetException(new Exception("test"));
cancelled = new TaskCompletionSource<object>();
cancelled.SetCanceled();
}
public Task Go()
{
return Task.FromResult(0);
}
public Task<int> GoInt()
{
return Task.FromResult(0);
}
public Task<string> GoString()
{
return Task.FromResult("test");
}
public Task<object> GoObject()
{
return Task.FromResult<object>("test");
}
public Task Go2()
{
return Task.Factory.StartNew(() => 0);
}
public Task<int> GoInt2()
{
return Task.Factory.StartNew(() => 0);
}
public Task<string> GoString2()
{
return Task.Factory.StartNew(() => "test");
}
public Task<object> GoObject2()
{
return Task.Factory.StartNew(() => (object)"test");
}
public Task<string> GoFault()
{
return faulted.Task;
}
public Task<object> GoCancel()
{
return cancelled.Task;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment