Search code examples
c#linuxnetwork-programming.net-coredatagram

How to increase UDPClient throughput


I am looking into performance of .NET Core on a linux box. Specifically making sure what kind of limitations may be from tools available within the framework itself.

I've been hitting the box with ~ 50,000 pps. So far, it seems ~ 20,000 pps is what the UDPClient has been able to achieve before a fair bit of packets get dropped. Using another tool (syslog-ng) there's a rare/low packet loss rate.

If I'm looking to handle upwards of 50K pps, is UdpClient able to handle this with proper tuning?

using (UdpClient udpListener = new UdpClient(_sysLogPort))
{
    udpListener.Client.ReceiveBufferSize = _bufferSize;

    while (!_cts.IsCancellationRequested)
    {
        try
        {
            UdpReceiveResult result = await udpListener.ReceiveAsync();
        }
        catch (Exception ex)
        {

        }
    }
}

Solution

  • Even if your app launches a new thread with udpListener.ReceiveAsync();, it waits for its termination before trying to receive a new packet. So there is only one thread at a time that handles a new received UDP packet to create an object of type UdpReceiveResult. So, it is rather similar to a single-threaded app: you do not make use of the opportunity to run on a multi-core system.

    You may get better rates (depending on your hardware, obviously), with the following way to write your program. In this example, there is a pool of 5 threads that run in parallel to create multiple instances of UdpReceiveResult at the same time. Even if packets are handled by the kernel one at a time, the userland process of creating instances of UdpReceiveResult is done in parallel, with this way of programming.

    // example of multithreaded UdpClient with .NET core on Linux
    // works on Linux OpenSuSE LEAP 42.1 with .NET Command Line Tools (1.0.4)
    // passed tests with "time nping --udp -p 5555 --rate 2000000 -c 52000 -H localhost > /dev/null"
    
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    
    namespace hwapp {
      class Program {
        // listen to port 5555
        UdpClient udpListener = new UdpClient(5555);
    
        static void Main(string[] args) {
          Program p = new Program();
          // launch 5 threads
          Task t0 = p.listen("thread 0");
          Task t1 = p.listen("thread 1");
          Task t2 = p.listen("thread 2");
          Task t3 = p.listen("thread 3");
          Task t4 = p.listen("thread 4");
          t0.Wait(); t1.Wait(); t2.Wait(); t3.Wait(); t4.Wait();
        }
    
        public async Task listen(String s) {
          Console.WriteLine("running " + s);
          using (udpListener) {
            udpListener.Client.ReceiveBufferSize = 2000;
            int n = 0;
            while (n < 10000) {
              n = n + 1;
              try {
                UdpReceiveResult result = udpListener.Receive();
              } catch (Exception ex) {}
            }
          }
        }
      }
    }