Skip to content

Instantly share code, notes, and snippets.

@mgravell
Last active April 19, 2017 10:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mgravell/c570ec103afe6960426ba2de45ff61a1 to your computer and use it in GitHub Desktop.
Save mgravell/c570ec103afe6960426ba2de45ff61a1 to your computer and use it in GitHub Desktop.
async; ValueTask vs Task
Objective: compare Task<T> vs ValueTask<T> in a scenario that should
be ideal for ValueTask<T> - non-trivial results, but usually (always)
already completed.
Hypothesis: ValueTask<T> should be more efficient, as it does not (in
the "already completed" case) involve allocation.
Methodology: create a test rig that computes a number using multiple
nested roll-up operations, comparing sync vs async-Task<T> vs async-ValueTask<T>;
use "async"/"await" for the rollups in the async cases. Run all tests using
BenchmarkDotNet from command-line in release mode. It is expected that both
Task<T> and ValueTask<T> will have some overhead compared to non-async version.
Follow-up: added "HandCranked" versions that seek to avoid the await machinery.
Faster, but ugly as sin.
Code: https://github.com/mgravell/protobuf-net/tree/async/src/TheAwaitingGame
Results: ValueTask<T> appears to take *longer* than Task<T>; update - but it allocates far less:
--------------------------------------------
```
> dotnet run -c Release -f net462
Total time: 00:03:57 (237.7 sec)
// * Summary *
BenchmarkDotNet=v0.10.3.111-nightly, OS=Windows 10.0.14393
Processor=Intel(R) Core(TM) i7-5930K CPU 3.50GHz, ProcessorCount=12
Frequency=3416975 Hz, Resolution=292.6565 ns, Timer=TSC
dotnet cli version=1.0.3
[Host] : .NET Core 4.6.25009.03, 64bit RyuJIT
Job-LKSOIG : .NET Core 4.6.25009.03, 64bit RyuJIT
Force=False
Method | Mean | StdErr | StdDev | Op/s | Gen 0 | Allocated |
------------------------------- |-----------:|----------:|----------:|---------:|-------:|----------:|
Sync | 10.9782 us | 0.0168 us | 0.0580 us | 91089.95 | - | 0 kB |
TaskAsync | 17.7219 us | 0.0532 us | 0.1919 us | 56427.3 | 3.8625 | 24.25 kB |
TaskCheckedAsync | 17.5696 us | 0.0480 us | 0.1732 us | 56916.64 | 3.8625 | 24.25 kB |
ValueTaskAsync | 21.5139 us | 0.0526 us | 0.1895 us | 46481.58 | - | 0 kB |
ValueTaskWrappedAsync | 20.4348 us | 0.0396 us | 0.1371 us | 48936.02 | 0.6333 | 3.99 kB |
ValueTaskDecimalReferenceAsync | 17.4158 us | 0.0562 us | 0.2025 us | 57419.19 | 1.5458 | 9.7 kB |
ValueTaskCheckedAsync | 18.8079 us | 0.0203 us | 0.0732 us | 53169.22 | - | 0 kB |
HandCrankedAsync | 15.1644 us | 0.0283 us | 0.1020 us | 65944.09 | - | 0 kB |
AssertCompletedAsync | 16.3290 us | 0.1084 us | 0.4057 us | 61240.56 | - | 0 kB |
TaskDoubleAsync | 15.2432 us | 0.0418 us | 0.1506 us | 65603.14 | 3.4750 | 21.82 kB |
ValueTaskDoubleAsync | 17.1229 us | 0.1955 us | 0.8059 us | 58401.46 | - | 0 kB |
```
--------------------------------------------
```
> dotnet run -c Release -f netcoreapp1.1
Total time: 00:01:23 (83.08 sec)
// * Summary *
BenchmarkDotNet=v0.10.3.0, OS=Microsoft Windows 10.0.15063
Processor=Intel(R) Core(TM) i7-7700HQ CPU 2.80GHz, ProcessorCount=8
Frequency=2742190 Hz, Resolution=364.6720 ns, Timer=TSC
dotnet cli version=1.0.2
[Host] : .NET Core 4.6.25009.03, 64bit RyuJIT
DefaultJob : .NET Core 4.6.25009.03, 64bit RyuJIT
Method | Mean | StdDev | Gen 0 | Allocated |
----------------- |---------- |---------- |---------- |---------- |
Sync | 3.0868 ms | 0.0634 ms | - | 68 B |
TaskAsync | 4.7674 ms | 0.0915 ms | 1796.8750 | 6.06 MB |
ValueTaskAsync | 5.7727 ms | 0.0591 ms | - | 135 B |
HandCrankedAsync | 4.6280 ms | 0.0449 ms | - | 135 B |
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment