Skip to content

Instantly share code, notes, and snippets.

@bgrainger
Last active February 13, 2023 05:34
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bgrainger/31da8d29299333e9de0b2f8d2e8d0b9b to your computer and use it in GitHub Desktop.
Save bgrainger/31da8d29299333e9de0b2f8d2e8d0b9b to your computer and use it in GitHub Desktop.
Benchmark async/await options in C# 7
BenchmarkDotNet=v0.10.3.0, OS=Microsoft Windows 10.0.14393
Processor=Intel(R) Xeon(R) CPU E5-1650 v3 3.50GHz, ProcessorCount=12
Frequency=3410069 Hz, Resolution=293.2492 ns, Timer=TSC
dotnet cli version=1.0.1
  [Host]     : .NET Core 4.6.25009.03, 64bit RyuJIT
  DefaultJob : .NET Core 4.6.25009.03, 64bit RyuJIT
Method Mean StdDev Gen 0 Allocated
CountAsync 90.1756 ns 0.8802 ns 0.0154 136 B
CountAsyncValueTask 97.2691 ns 0.8378 ns 0.0037 64 B
CountAsyncValueTaskLocalFunction 58.7156 ns 0.6790 ns 0.0118 96 B
CountAsyncValueTaskLocalFunctionNoCapture 46.9273 ns 0.5879 ns 0.0068 64 B
CountAsyncValueTaskMultipleFunctions 45.3743 ns 0.4451 ns 0.0069 64 B
[MemoryDiagnoser]
public class Program
{
static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<Program>();
}
[Benchmark]
public async Task<int> CountAsync()
{
var index = 0;
await s_semaphore.WaitAsync().ConfigureAwait(false);
s_counters[index]++;
s_semaphore.Release();
return s_counters[index];
}
[Benchmark]
public async ValueTask<int> CountAsyncValueTask()
{
var index = 0;
await s_semaphore.WaitAsync().ConfigureAwait(false);
s_counters[index]++;
s_semaphore.Release();
return s_counters[index];
}
[Benchmark]
public ValueTask<int> CountAsyncValueTaskLocalFunction()
{
var index = 0;
var task = s_semaphore.WaitAsync();
return task.IsCompleted ? new ValueTask<int>(Remainder()) :
new ValueTask<int>(Awaited());
async Task<int> Awaited()
{
await task.ConfigureAwait(false);
return Remainder();
}
int Remainder()
{
s_counters[index]++;
s_semaphore.Release();
return s_counters[index];
}
}
[Benchmark]
public ValueTask<int> CountAsyncValueTaskLocalFunctionNoCapture()
{
var index = 0;
var task = s_semaphore.WaitAsync();
return task.IsCompleted ? new ValueTask<int>(Remainder(index)) :
new ValueTask<int>(Awaited(task, index));
async Task<int> Awaited(Task task_, int index_)
{
await task_.ConfigureAwait(false);
return Remainder(index_);
}
int Remainder(int index_)
{
s_counters[index_]++;
s_semaphore.Release();
return s_counters[index_];
}
}
[Benchmark]
public ValueTask<int> CountAsyncValueTaskMultipleFunctions()
{
var index = 0;
var task = s_semaphore.WaitAsync();
return task.IsCompleted ? new ValueTask<int>(CountAsyncValueTaskMultipleFunctionsRemainder(index)) :
new ValueTask<int>(CountAsyncValueTaskMultipleFunctionsAwaited(task, index));
}
static async Task<int> CountAsyncValueTaskMultipleFunctionsAwaited(Task task, int index)
{
await task.ConfigureAwait(false);
return CountAsyncValueTaskMultipleFunctionsRemainder(index);
}
static int CountAsyncValueTaskMultipleFunctionsRemainder(int index)
{
s_counters[index]++;
s_semaphore.Release();
return s_counters[index];
}
static readonly int[] s_counters = new int[20];
static readonly SemaphoreSlim s_semaphore = new SemaphoreSlim(20, 20);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment