Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save mattwarren/7227890bf6fc76cf050231511089d6da to your computer and use it in GitHub Desktop.
Save mattwarren/7227890bf6fc76cf050231511089d6da to your computer and use it in GitHub Desktop.
ContinueWith Micro Benchmark
[Config(typeof(Config))]
public class ContinueWithAllocations
{
private class Config : ManualConfig
{
public Config()
{
Add(new MemoryDiagnoser());
}
}
[Params(100, 200, 500)]
public int NumberOfTasks { get; set; }
private ConcurrentDictionary<Task, Task> tasks;
[Setup]
public void SetUp()
{
tasks = new ConcurrentDictionary<Task, Task>();
}
[Benchmark]
public Task ContinueWithClojureCapture()
{
for (int i = 0; i < NumberOfTasks; i++)
{
var runningTask = Task.Delay(1);
tasks.TryAdd(runningTask, runningTask);
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
runningTask.ContinueWith(t =>
{
Task toBeRemoved;
tasks.TryRemove(t, out toBeRemoved);
}, TaskContinuationOptions.ExecuteSynchronously);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
}
return Task.WhenAll(tasks.Values);
}
[Benchmark]
public Task ContinueWithWithoutClojureCapture()
{
for (int i = 0; i < NumberOfTasks; i++)
{
var runningTask = Task.Delay(1);
tasks.TryAdd(runningTask, runningTask);
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
runningTask.ContinueWith((t, state) =>
{
var runningTasks = (ConcurrentDictionary<Task, Task>) state;
Task toBeRemoved;
runningTasks.TryRemove(t, out toBeRemoved);
}, tasks, TaskContinuationOptions.ExecuteSynchronously);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
}
return Task.WhenAll(tasks.Values);
}
}
BenchmarkDotNet=v0.9.7.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-3615QM CPU 2.30GHz, ProcessorCount=8
Frequency=2241002 ticks, Resolution=446.2290 ns, Timer=TSC
HostCLR=MS.NET 4.0.30319.42000, Arch=64-bit RELEASE [RyuJIT]
JitModules=clrjit-v4.6.1080.0

Type=ContinueWithAllocations  Mode=Throughput  
                        Method | Platform |       Jit | NumberOfTasks |        Median |      StdDev | Gen 0 | Gen 1 | Gen 2 | Bytes Allocated/Op |

---------------------------------- |--------- |---------- |-------------- |-------------- |------------ |------ |------ |------ |------------------- | ContinueWithClojureCapture | X64 | LegacyJit | 100 | 584.2819 us | 31.0444 us | 0,00 | 0,00 | 0,00 | 72 771,83 | ContinueWithWithoutClojureCapture | X64 | LegacyJit | 100 | 562.1988 us | 22.2841 us | 0,00 | 0,00 | 0,00 | 72 515,91 | ContinueWithClojureCapture | X64 | RyuJit | 100 | 576.2550 us | 28.2475 us | 0,00 | 0,00 | 0,00 | 72 575,64 | ContinueWithWithoutClojureCapture | X64 | RyuJit | 100 | 565.3582 us | 25.2964 us | 0,00 | 0,00 | 0,00 | 69 739,98 | ContinueWithClojureCapture | X86 | LegacyJit | 100 | NA | NA | - | - | - | NaN | ContinueWithWithoutClojureCapture | X86 | LegacyJit | 100 | NA | NA | - | - | - | NaN | ContinueWithClojureCapture | X64 | LegacyJit | 200 | 931.4733 us | 52.2089 us | 0,00 | 0,00 | 0,00 | 108 105,39 | ContinueWithWithoutClojureCapture | X64 | LegacyJit | 200 | 893.3913 us | 45.0929 us | 0,00 | 0,00 | 0,00 | 92 125,85 | ContinueWithClojureCapture | X64 | RyuJit | 200 | 907.1329 us | 44.0440 us | 0,00 | 0,00 | 0,00 | 108 621,39 | ContinueWithWithoutClojureCapture | X64 | RyuJit | 200 | 927.7449 us | 59.2315 us | 0,00 | 0,00 | 0,00 | 107 089,14 | ContinueWithClojureCapture | X86 | LegacyJit | 200 | NA | NA | - | - | - | NaN | ContinueWithWithoutClojureCapture | X86 | LegacyJit | 200 | NA | NA | - | - | - | NaN | ContinueWithClojureCapture | X64 | LegacyJit | 500 | 1,848.7510 us | 159.8408 us | 0,00 | 0,00 | 0,00 | 226 353,87 | ContinueWithWithoutClojureCapture | X64 | LegacyJit | 500 | 1,838.6481 us | 135.8207 us | 0,00 | 0,00 | 0,00 | 183 727,11 | ContinueWithClojureCapture | X64 | RyuJit | 500 | 1,892.3359 us | 145.9936 us | 0,00 | 0,00 | 0,00 | 132 275,79 | ContinueWithWithoutClojureCapture | X64 | RyuJit | 500 | 1,834.6669 us | 131.8214 us | 0,00 | 0,00 | 0,00 | 187 833,11 | ContinueWithClojureCapture | X86 | LegacyJit | 500 | NA | NA | - | - | - | NaN | ContinueWithWithoutClojureCapture | X86 | LegacyJit | 500 | NA | NA | - | - | - | NaN |

Benchmarks with issues: ContinueWithAllocations_ContinueWithClojureCapture_LegacyX86_NumberOfTasks-100 ContinueWithAllocations_ContinueWithWithoutClojureCapture_LegacyX86_NumberOfTasks-100 ContinueWithAllocations_ContinueWithClojureCapture_LegacyX86_NumberOfTasks-200 ContinueWithAllocations_ContinueWithWithoutClojureCapture_LegacyX86_NumberOfTasks-200 ContinueWithAllocations_ContinueWithClojureCapture_LegacyX86_NumberOfTasks-500 ContinueWithAllocations_ContinueWithWithoutClojureCapture_LegacyX86_NumberOfTasks-500

@danielmarbach
Copy link

But even in the update results only with 1 task ContinueWithWithoutClojureCapture allocates less bytes. I all other scenarios ContinueWithClojureCapture seems to allocate less. I have to come up with a better test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment