async/await for high performance server applications?
the new async/await keywords in C# 5 look very promising but I read an article about the performance impact on those applications since the compiler will generate a quite complex state machine for async methods.
Async-programming using these keywords is so much easier but is it as good as say SocketAsyncEventArgs for Sockets ?
Second question: Are asynchronous IO methods like Stream.WriteAsync really asynchronous (Completion Ports on .Net or epoll/poll on Mono) or are these methods cheap wrappers for pushing a write call to a threadpool ?
Third question: Beside the SynchronizationContext of an UI application, is there a way to implement some kind of sinlge-threaded context ? Something like an event loop so that finished tasks continues on the main thread ? I discovered the Nito.AsyncEx library, but I'm not quite sure whether or not this is what i need.
async
itself is quite performant. A ton of work went into this.
In general, on the server side you're concerned about async
I/O. I'm going to ignore async
CPU-bound methods because the async
overhead will get lost in the noise anyway.
Asynchronous I/O will increase your memory usage per request, but it'll reduce your thread usage per request. So you end up winning (except borderline pathological corner cases). This is true for all asynchronous I/O, including async
.
await
was designed with a pattern - not just the Task
type - so if you need to squeeze out as much performance as possible, you can.
I read an article about the performance impact on those applications since the compiler will generate a quite complex state machine for async methods.
The article you read by Stephen Toub is excellent. I also recommend the Zen of Async video (also by Stephen Toub).
Async-programming using these keywords is so much easier but is it as good as say SocketAsyncEventArgs for Sockets ?
First, understand that SocketAsyncEventArgs
is more scalable because it reduces memory garbage. The simpler way to use async
sockets will generate more memory garbage, but since await
is pattern-based you can define your own async
-compatible wrappers for the SocketAsyncEventArgs
API (as seen on Stephen Toub's blog... I'm sensing a pattern here ;). This allows you to squeeze every ounce of performance out.
Though it's usually better in the long run to design a scale-out system rather than twisting the code to avoid a few memory allocations. IMHO.
Second question: Are asynchronous IO methods like Stream.WriteAsync really asynchronous (Completion Ports on .Net or epoll/poll on Mono) or are these methods cheap wrappers for pushing a write call to a threadpool ?
I don't know about Mono. On .NET, most asynchronous I/O methods are based on a completion port. The Stream
class is a notable exception. The Stream
base class will do a "cheap wrapper" by default, but allows derived classes to override this behavior. Stream
s that come from network communications always override this to provide truly asynchronous I/O. Stream
s that deal with files only override this if the stream was constructed explicitly for asynchronous I/O.
Third question: Beside the SynchronizationContext of an UI application, is there a way to implement some kind of single-threaded context ?
ASP.NET also has a SynchronizationContext
, so if you're using ASP.NET you're already set.
If you are doing your own socket-based server (eg, a Win32 service), then you could use the AsyncContext
type in my AsyncEx library. But it doesn't sound like this is what you'd actually want. AsyncContext
will create a single-threaded context on the current thread. But the true power of async
for server applications comes from scaling requests instead of threads.
Consider how the ASP.NET SynchronizationContext
works: as each request comes in, it grabs a thread pool thread and constructs a SynchronizationContext
(for that request). When that request has asynchronous work to do, it registers with the SynchronizationContext
and the thread running that request returns to the thread pool. Later, when the asynchronous work completes, it grabs a thread pool thread (any thread), installs the existing SynchronizationContext
on it, and continues processing that request. When the request is finally completed, its SynchronizationContext
is disposed.
The key in that process is that when the request is waiting ( await
) asynchronous operations, there are no threads dedicated to that request. Since a request is considerably lightweight compared to a thread, this enables the server to scale better.
If you gave each of your requests a single-threaded SynchronizationContext
such as AsyncContext
, this would bind a thread to each request even when it has nothing to do. That's hardly any better than a synchronous multithreaded server.
You may find my MSDN article on SynchronizationContext
useful if you want to tackle inventing your own SynchronizationContext
. I also cover in that article how asynchronous methods "register" and "install" the context; this is done mostly-automatically by async void
and await
so you won't have to do it explicitly.
If you're using it in the context of async IO it's a moot point. The time spent on your database operation, file/network IO, etc. will be milliseconds at best. The overhead of async
will be microseconds at worst, if no nanoseconds. Where you need to be careful is when you have a lot of operations that you are awaiting (as in thousands, tens of thousands, or more) and those operations are very quick. When those async operations represent CPU bound work it's certainly possible for the overhead of using await
to be at least noticeable. Note that the code generated for the state machine is somewhat complex, in terms of human understand-ability, but state machines in general tend to perform rather well as a whole.
The methods are not just wrappers that block a thread pool thread, no. That would defeat the purpose of what await
represents. Those methods don't block any thread and rely on OS hooks to complete the task.
Sure, you can create your own SynchronizationContext
instead of relying entirely on an existing one provided by your UI framework. Here is a great example. Just be careful when using something like this; it's a good tool for the right task, but can be abused to find a more creating way of blocking when you should just be doing everything asynchronously.
上一篇: 关于任务,异步和等待的多线程
下一篇: 异步/等待高性能服务器应用程序?