¿Qué tan difícil es escribir un programa en Linux que reciba 1 millón de paquetes UDP por segundo?
Medir paquetes por segundo (pps) es más interesante que medir bytes por segundo (Bps). Puede obtener Bps más altos canalizando mejor y enviando paquetes más largos, mientras que aumentar los pps es mucho más difícil en comparación.
Dado que estamos interesados en pps, nuestros experimentos utilizarán paquetes UDP más cortos. Para ser precisos, una carga útil UDP de 32 bytes equivale a 74 bytes en la capa Ethernet.
En el laboratorio utilizaremos dos servidores físicos: un "receptor" y un "remitente".
Ambos cuentan con dos procesadores Xeon de seis núcleos a 2 GHz. Cada servidor tiene 24 procesadores con Hyper-Threading (HT) habilitado, equipados con la NIC multicola 10G de Solarflare y tiene una configuración de 11 colas de recepción. Más sobre esto más adelante.
El código fuente del programa de prueba es: udpsender, udpreceiver.
Conocimientos preparatorios
Utilizamos 4321 como puerto para paquetes UDP. Antes de comenzar, debemos asegurarnos de que la transmisión no se vea interferida por iptables:
Shell<. /p> p>
receptor$ iptables -I ENTRADA 1 -p udp --dport 4321 -j ACEPTAR
receptor$ iptables -t raw -I PREROUTING 1 -p udp --dport 4321 - j NOTRACK
Para comodidad de futuras pruebas, definimos claramente la dirección IP:
Shell
receiver$ for i in `seq 1 20`;
dirección IP agregar 192.168.254.$i/24 dev eth2;
hecho
remitente$ dirección IP agregar 192.168.254.30/24 dev eth3 p>
1. Método simple
Primero, realizamos algunos de los experimentos más simples. Simplemente enviando y recibiendo, ¿cuántos paquetes se transmitirán?
Pseudocódigo para simular el remitente:
Python
fd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
fd .bind(("0.0.0.0", 65400))# Seleccione el puerto de origen para reducir el no determinismo
fd.connect((("192.168.254.1", 4321))
mientras es cierto:
fd.sendmmsg(["x00" * 32] * 1024)
Dado que utilizamos llamadas al sistema ordinarias para enviar, el cambio de contexto del kernel no es muy eficiente, por lo que es mejor. Para evitar esto, Linux agregó recientemente una conveniente llamada al sistema llamada sendmmsg que nos permite enviar una gran cantidad de paquetes en una sola llamada, lo que nos permite enviar 1024 paquetes a la vez.
Pseudocódigo para simular el receptor:
Python
fd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
fd .bind(("0.0.0.0", 4321))
mientras que Verdadero:
paquetes = [Ninguno] * 1024
fd.recvmmsg(paquetes, MSG_WAITFORONE)
Del mismo modo, recvmmsg es una versión más eficiente de la llamada al sistema que recv normal.
Probemos esto:
Shell
remitente$ ./udpsender 192.168.254.1:4321
receptor$ ./udpreceiver1 0.0 . 0.0:4321
0,352 Mpp 10,730 MiB / 90,010 Mb
0,284 M pp 8,655 MiB / 72,603 Mb
0,262 M pp 7,991 MiB / 67,033 Mb
p>0.199M pps 6.0.081MiB / 51.013Mb
0.195M pps 5.956MiB / 49.966Mb
0.199M pps 6.060MiB / 50.836Mb
0.200Mpps 6.097MiB / 51.147Mb