Search code examples
c#socketsc10k

Can Socket.ReceiveAsync head to C10K?


Can a TCP socket that uses Socket.ReceiveAsync or even Socket.SendAsync experience the C10K problem ?


Solution

  • I don't think this is at all a duplicate of this other question, as has been suggested: Async-Await vs ThreadPool vs MultiThreading on High-Performance Sockets (C10k Solutions?) That question is more a discussion of various threading models, which don't always apply to Socket.****Async (which ALL use SocketAsyncEventArgs).

    The answer to this question is HIGHLY dependent upon your runtime and your OS. I have just recently gone down this road while developing a solution for Unity, so I will share what I know even though the question is a bit old.

    Let's first talk about Windows and MS .NET (anything from 3.5 when those methods were implemented to present). You can definitely break c10k when combining those socket methods on this runtime and platform. The ****Async methods that take a SocketAsyncEventArgs parameter go straight to native calls in the runtime, which map to IO Completion Ports in Windows. This is a special path through the runtime, which does more than prevent allocating IAsyncResult objects. I assume using .NET Standard on Linux is also very fast, but I cannot speak directly to that.

    Next, let's talking about Mono on Linux. Specifically, I am currently building with: Mono C# compiler version 4.6.2.0 This will also break c10k, but it works differently. From what I can see, the *****Async methods take the same path through the runtime as all other async methods, except ****Async calls take the shortest path and avoid an IAsyncResult allocation. The runtime submits your requests as an IOSelectorJob via IOSelector.Add. For this reason it appears that none of the ****Async methods will actually ever return false in Mono, though I wouldn't count on that behaviour always being true. I assume using Mono on Windows is equally fast, but I cannot speak to that.

    Finally, let's talk about Unity. I am using 2018.3.2f1 which has the Mono 5.11.0 runtime, set to .NET 4.x compat. I'm not sure it is possible to break c10k in Unity. I don't know why, but I know that identical implementations that handily break c10k in Mono and .NET will not even come close. I assume this is because somehow their thread pools are set up differently ... perhaps to accomodate their job system, but that's merely a shot-in-the-dark guess. Very strange things happen, such as synchronous accept outperforming async. AcceptAsync requests that never get a callback, and so on.

    Some tips to break c10k outside of Unity: - Do as little as possible in your IO completion callbacks. Don't put calls to other Async methods inside them like the MSDN example - If you can manage it, don't make calls to SendAsync and ReceiveAsync block each other due to your design. You can't have 2 outstanding calls with the same arg, but you can have both multiple and separate args (and therefore buffers) for send/recv. But be aware that things can get complicated for ordering, for example in the case of multiple outstanding recvs on the same socket, and concurrent queues dispatching the returned args from the callbacks - If you have to share resources between threads then System.Collections.Concurrent is your friend. Don't roll your own as these containers are not why you aren't breaking c10k, and you will never be able to match the amount of testing these babies have undergone

    Good luck and have fun :) And don't forget to tell me if you find the secret to success in Unity. I'll surely update this if I do.