Async Await Performance

async await performance?

A Task object represent the deferred result of a pending operation. You don't have to use tasks and async/await if you don't have any pending operations. Otherwise, I believe async/await code is generally more efficient than its bare TPL ContinueWith analogue.

Let's do some timing:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication
{
class Program
{
// async/await version
static async Task<int> Test1Async(Task<int> task)
{
return await task;
}

// TPL version
static Task<int> Test2Async(Task<int> task)
{
return task.ContinueWith(
t => t.Result,
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}

static void Tester(string name, Func<Task<int>, Task<int>> func)
{
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
for (int i = 0; i < 10000000; i++)
{
func(Task.FromResult(0)).Wait();
}
sw.Stop();
Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds);
}

static void Main(string[] args)
{
Tester("Test1Async", Test1Async);
Tester("Test2Async", Test2Async);
}
}
}

The output:


Test1Async: 1582ms
Test2Async: 4975ms

So, by default, await continuations are handled more efficiently than ContinueWith continuations. Let's optimize this code slightly:

// async/await version
static async Task<int> Test1Async(Task<int> task)
{
if (task.IsCompleted)
return task.Result;
return await task;
}

// TPL version
static Task<int> Test2Async(Task<int> task)
{
if (task.IsCompleted)
return Task.FromResult(task.Result);

return task.ContinueWith(
t => t.Result,
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}

The output:


Test1Async: 1557ms
Test2Async: 429ms

Now the non-async version wins. In case with the async version, I believe this optimization has already been done internally by the async/await infrastructure.

Anyway, so far we've dealt only with completed tasks (Task.FromResult). Let's introduce the actual asynchrony (naturally, we'll do less iterations this time):

static Task<int> DoAsync()
{
var tcs = new TaskCompletionSource<int>();
ThreadPool.QueueUserWorkItem(_ => tcs.SetResult(0));
return tcs.Task;
}

static void Tester(string name, Func<Task<int>, Task<int>> func)
{
ThreadPool.SetMinThreads(200, 200);
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
for (int i = 0; i < 1000000; i++)
{
func(DoAsync()).Wait();
}
sw.Stop();
Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds);
}

The output:


Test1Async: 4207ms
Test2Async: 4734ms

Now the difference is very marginal, although the async version still performs slightly better. Yet I think such gain is really neglectable, comparable to the actual cost of the asynchronous operation or to the cost of restoring the captured context for when SynchronizationContext.Current != null.

The bottom line is, if you deal with asynchronous tasks, go for async/await if you have a choice, not for performance reason but for ease of use, readability and maintainability.

Async / await vs then which is the best for performance?

From a performance point of view, await is just an internal version of .then() (doing basically the same thing). The reason to choose one over the other doesn't really have to do with performance, but has to do with desired coding style or coding convenience. Certainly, the interpreter has a few more opportunities to optimize things internally with await, but its unlikely that should be how you decide which to use. If all else was equal, I would choose await for the reason cited above. But, I'd first choose which made the code simpler to write and understand and maintain and test.

Used properly, await can often save you a bunch of lines of code making your code simpler to read, test and maintain. That's why it was invented.

There's no meaningful difference between the two versions of your code. Both achieve the same result when the axios call is successful or has an error.

Where await could make more of a convenience difference is if you had multiple successive asynchronous calls that needed to be serialized. Then, rather than bracketing them each inside a .then() handler to chain them properly, you could just use await and have simpler looking code.

A common mistake with both await and .then() is to forget proper error handling. If your error handling desire in this function is to just return the rejected promise, then both of your versions do that identically. But, if you have multiple async calls in a row and you want to do anything more complex than just returning the first rejection, then the error handling techniques for await and .then()/.catch() are quite different and which seems simpler will depend upon the situation.

Async/await performance

Task.Delay isn't broken, but you're performing 100,000 tasks which each take some time. It's the call to Console.WriteLine that is causing the problem in this particular case. Each call is cheap, but they're accessing a shared resource, so they aren't very highly parallelizable.

If you remove the call to Console.WriteLine, all the tasks complete very quickly. I changed your code to return the elapsed time that each task observes, and then print just a single line of output at the end - the maximum observed time. On my computer, without any Console.WriteLine call, I see output of about 1.16 seconds, showing very little inefficiency:

using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

class Program
{
static void Main(string[] args)
{
ThreadPool.SetMinThreads(50000, 50000);
var tasks = Enumerable.Repeat(0, 100000)
.Select(_ => Task.Run(IoBoundWork))
.ToArray();
Task.WaitAll(tasks);
var maxTime = tasks.Max(t => t.Result);
Console.WriteLine($"Max: {maxTime}");
}

private static async Task<double> IoBoundWork()
{
var sw = Stopwatch.StartNew();
await Task.Delay(1000);
return sw.Elapsed.TotalSeconds;
}
}

You can then modify IoBoundWork to do different tasks, and see the effect. Examples of work to try:

  • CPU work (do something actively "hard" for the CPU, but briefly)
  • Synchronous sleeping (so the thread is blocked, but the CPU isn't)
  • Synchronous IO which doesn't have any shared bottlenecks (although that's generally hard, given that the disk or network is likely to end up being a shared resource bottleneck even if you're writing to different files etc)
  • Synchronous IO with a shared bottleneck such as Console.WriteLine
  • Asynchronous IO (await foo.WriteAsync(...) etc)

You can also try removing the call to Task.Delay(1000) or changing it. I found that by removing it entirely, the result was very small - whereas replacing it with Task.Yield was very similar to Task.Delay. It's worth remembering that as soon as your async method has to actually "pause" you're effectively doubling the task scheduling problem - instead of scheduling 100,000 operations, you're scheduling 200,000.

You'll see a different pattern in each case. Fundamentally, you're starting 100,000 tasks, asking them all to wait for a second, then asking them all to do something. That causes issues in terms of continuation scheduling that's async/await specific, but also plain resource management of "Performing 100,000 tasks each of which needs to write to the console is going to take a while."

Performance impact of using async await when its not necessary

In the first method compiler will generate "state machine" code around it and execution will be returned to the line return await service.GetAsync(); after task will be completed. Consider example below:

public async Task<Something> GetSomethingAsync()
{
var somethingService = new SomethingService();

// Here execution returns to the caller and returned back only when Task is completed.
Something value = await service.GetAsync();

DoSomething();

return value;
}

The line DoSomething(); will be executed only after service.GetAsync task is completed.

Second approach simply starts execution of service.GetAsync and return correspondent Task to the caller without waiting for completion.

public Task<Something> GetSomethingAsync()
{
var somethingService = new SomethingService();

Task<Something> valueTask = service.GetAsync();

DoSomething();

return valueTask;
}

So in the example above DoSomething() will be executed straight after line Task<Something> valueTask = service.GetAsync(); without waiting for completion of task.

Executing async method on the another thread depend on the method itself.

If method execute IO operation, then another thread will be only waste of the thread, which do nothing, only waiting for response. On my opinion async - await are perfect approach for IO operations.

If method GetAsync contains for example Task.Run then execution goes to the another thread fetched from thread pool.

Below is short example, not a good one, but it show the logic a tried to explain:

static async Task GetAsync()
{
for(int i = 0; i < 10; i++)
{
Console.WriteLine($"Iterate GetAsync: {i}");
await Task.Delay(500);
}
}

static Task GetSomethingAsync() => GetAsync();

static void Main(string[] args)
{
Task gettingSomethingTask = GetSomethingAsync();

Console.WriteLine("GetAsync Task returned");

Console.WriteLine("Start sleeping");
Thread.Sleep(3000);
Console.WriteLine("End sleeping");

Console.WriteLine("Before Task awaiting");
gettingSomethingTask.Wait();
Console.WriteLine("After Task awaited");

Console.ReadLine();
}

And output will be next:

Iterate GetAsync: 0
GetAsync Task returned
Start sleeping
Iterate GetAsync: 1
Iterate GetAsync: 2
Iterate GetAsync: 3
Iterate GetAsync: 4
Iterate GetAsync: 5
End sleeping
Before Task awaiting
Iterate GetAsync: 6
Iterate GetAsync: 7
Iterate GetAsync: 8
Iterate GetAsync: 9
After Task awaited

As you can see executing of GetAsync starts straight after calling it.

If GetSomethingAsync() will be changed to the:

static Task GetSomethingAsync() => new Task(async () => await GetAsync());

Where GetAsync wrapped inside another Task, then GetAsync() will not be executed at all and output will be:

GetAsync Task returned
Start sleeping
End sleeping
Before Task awaiting
After Task awaited

Of course you will need to remove line gettingSomethingTask.Wait();, because then application just wait for task which not even started.

When is async, await, promise.all needed in JavaScript?

Promises (and thus async/await) aren't a tool to take synchronous code and speed it up. When you create a promise it's usually because you're not calculating anything at all, but instead are waiting for something external to happen.

For example, you might be waiting for a network response to get back, or you might be waiting for a timer to elapse, or waiting for someone to press a key. You can't do any work while this is happening, so instead you create a Promise and then stop running any code. Since javascript is single threaded, stopping running your code is important to let other code start running, including the browser's normal page-painting code.

Promises are objects with .then function on it. You can call .then and pass in a function to tell it "hey, when you're done waiting, please call this function". async/await just simplifies the syntax for working with promises: an async function will automatically create a promise, and awaiting a promise will automatically call .then on it.


With that in mind: The examples you gave don't have anything to do with async await. You have a bunch of synchronous computations, and are not waiting for anything external. Slapping await on a map function or a for loop will have no effect, other than to confuse your coworkers and very slightly increase runtime.

Performance LINQ async await

To simplify, you're asking about the difference between this:

await Task.WhenAll(something.Select(async () => await DoSomethingAsync()).ToArray());

and this:

await Task.WhenAll(something.Select(() => DoSomethingAsync()).ToArray());

for me the correct code is the second because in the first its waiting each end of api?

No, you're misunderstanding the way it works. DoSomethingAsync() will still get called concurrently, and the resulting tasks will still be completed concurrently. The only difference with the added async/await is that the compiler adds some state machine code into the lambda function so that if an exception is thrown, that lambda will appear on your stack trace.

The performance difference coming from that additional state machine code will be negligible compared to any async operations you're using.

There is often value in having the stack trace show where the async method call happened. It's probably not a big deal in this case, since the await Task.WhenAll() is just a line away from the method call, so there's no ambiguity about how DoSomethingAsync was called. But you can imagine scenarios where DoSomethingAsync is called from various different parts of code, and the resulting tasks are awaited in a completely different method: it might be difficult to figure out what path led to the point where an exception was thrown if that line of code isn't included in the stack trace.



Related Topics



Leave a reply



Submit