Felice Pollano
Felice Pollano

Reputation: 33252

ASP.NET Core, overkilling Task.Run()?

Lets say we have an ASP.NET Core receiving a string as a payload, size order of couple of megabytes. First method implementation:

[HttpPost("updateinfos")]
public async Task UpdateInfos()
{
    var len = (int)this.Request.ContentLength;
    byte[] b = new byte[len];
    await this.Request.Body.ReadAsync(b,0,len);
    var content = Encoding.UTF8.GetString(b);
    .....
}

Body is read with ReadAsync, this is good since we have I/O stuff on socket and having it asynchronous is for free due to the nature of the call itself. But if we have a look after, the GetString() method, is purely CPU, is blocking with linear complexity. Anyway this affect somehow the performance since other clients wait for my bytes to get converted in string. I think to avoid this the solution is to run GetString() on the thread pool, by this:

[HttpPost("updateinfos")]
public async Task UpdateInfos()
{
    var len = (int)this.Request.ContentLength;
    byte[] b = new byte[len];
    await this.Request.Body.ReadAsync(b,0,len);
    var content = await Task.Run(()=>ASCIIEncoding.UTF8.GetString(b));
    .....
}

please don't mind the return right now, something more has to be done in the function.

So the question, is the second approach overkilling? If so, what is the boundary to discriminate what could be run as blocking and what has to be moved to another thread?

Upvotes: 1

Views: 1288

Answers (1)

poke
poke

Reputation: 387547

You are very much abusing Task.Run there. Task.Run is used to off-load work onto a different thread and asynchronously wait for it to complete. So every Task.Run call will cause thread context switches. Of course, that is usually a very bad idea to do for things that should not run on their own thread.

Things like ASCIIEncoding.UTF8.GetString(b) are really fast. The overhead involved in creating and managing a thread that encapsulates this is much larger than just executing this directly on the same thread.

You should generally use Task.Run only to off-load (primarily synchronous) work that can benefit from running on its own thread. Or in cases, where you have work that would take a bit longer but block the current execution.

In your example, that is simply not the case, so you should just call those methods synchronously.


If you really want to reduce the work for that code, you should look at how to work properly streams. What you do in your code is read the request body stream completely and only then you work on it (trying to translate into a string).

Instead of separating the process of reading the binary stream and then translating it into a string, you could just read the stream as a string directly using a StreamReader. That way, you can read the request body directly, even asynchronously. So depending on what you actually do with it afterwards, that may be a lot more efficient.

Upvotes: 5

Related Questions