Question Latences TCP plus élevées dans les dernières versions de Linux


Dans mon groupe de recherche, nous avons récemment mis à niveau le système d’exploitation de nos machines de Red Hat 6.2 à Debian 8.3 et avons constaté que le temps de transmission aller-retour TCP via les cartes NIC Intel 1G intégrées entre nos machines avait doublé d’environ 110 µs à 220 µs.

Au début, je pensais que c’était un problème de configuration, alors j’ai copié toutes les configurations sysctl (telles que tcp_low_latency=1) des machines Red Hat non mises à niveau aux machines Debian et cela n'a pas résolu le problème. Ensuite, je pensais que c'était peut-être un problème de distribution Linux et j'installais Red Hat 7.2 sur les machines, mais les temps d'aller-retour restaient autour de 220µs.

Enfin, j’ai pensé que le problème venait peut-être des versions du noyau Linux puisque Debian 8.3 et Red Hat 7.2 utilisaient tous les deux le noyau 3.x, tandis que Red Hat 6.2 utilisait le noyau 2.6. Pour tester cela, j’ai installé Debian 6.0 avec le noyau Linux 2.6 et le bingo! Les temps étaient encore rapides à 110µs.

D'autres personnes ont-elles également expérimenté ces latences plus élevées dans les dernières versions de Linux et existe-t-il des solutions de contournement connues?


Exemple de travail minimum

Vous trouverez ci-dessous une application C ++ qui peut être utilisée pour évaluer la latence. Il mesure la latence en envoyant un message, en attendant une réponse, puis en envoyant le message suivant. Il le fait 100 000 fois avec des messages de 100 octets. Ainsi, nous pouvons diviser le temps d'exécution du client par 100 000 pour obtenir les latences aller-retour. Pour utiliser cette première compilation du programme:

g++ -o socketpingpong -O3 -std=c++0x Server.cpp

Exécutez ensuite la version côté serveur de l’application sur un hôte (par exemple, 192.168.0.101). Nous spécifions l'IP pour nous assurer que nous hébergeons une interface connue.

socketpingpong 192.168.0.101

Et puis utilisez l'utilitaire Unix time mesurer le temps d'exécution du client.

time socketpingpong 192.168.0.101 client

L'exécution de cette expérience entre deux hôtes Debian 8.3 avec un matériel identique donne les résultats suivants.

real  0m22.743s
user  0m0.124s
sys     0m1.992s

Les résultats de Debian 6.0 sont

real    0m11.448s 
user    0m0.716s  
sys     0m0.312s  

Code:

#include <unistd.h>
#include <limits.h>
#include <string.h>

#include <linux/futex.h>
#include <arpa/inet.h>

#include <algorithm>

using namespace std;

static const int PORT = 2444;
static const int COUNT = 100000;

// Message sizes are 100 bytes
static const int SEND_SIZE = 100;
static const int RESP_SIZE = 100;

void serverLoop(const char* srd_addr) {
    printf("Creating server via regular sockets\r\n");
    int sockfd, newsockfd;
    socklen_t clilen;
    char buffer[SEND_SIZE];
    char bufferOut[RESP_SIZE];
    struct sockaddr_in serv_addr, cli_addr;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
       perror("ERROR opening socket");

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(srd_addr);
    serv_addr.sin_port = htons(PORT);

    fflush(stdout);
    if (bind(sockfd, (struct sockaddr *) &serv_addr,
             sizeof(serv_addr)) < 0) {
             perror("ERROR on binding");
    }

    listen(sockfd, INT_MAX);
    clilen = sizeof(cli_addr);
    printf("Started listening on %s port %d\r\n", srd_addr, PORT);
    fflush(stdout);

    while (true) {
        newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
        if (newsockfd < 0)
             perror("ERROR on accept");
        printf("New connection\r\n");

        int status = 1;
        while (status > 0) {
            // Read
            status = read(newsockfd, buffer, SEND_SIZE);
            if (status < 0) {
                perror("read");
                break;
            }

            if (status == 0) {
                printf("connection closed");
                break;
            }

            // Respond
            status = write(newsockfd, bufferOut, RESP_SIZE);
            if (status < 0) {
                perror("write");
                break;
            }
        }

        close(newsockfd);
    }


    close(sockfd);
}

int clientLoop(const char* srd_addr) {
    // This example is copied from http://www.binarytides.com/server-client-example-c-sockets-linux/
    int sock;
    struct sockaddr_in server;
    char message[SEND_SIZE] , server_reply[RESP_SIZE];

    //Create socket
    sock = socket(AF_INET , SOCK_STREAM , 0);
    if (sock == -1)
    {
        printf("Could not create socket");
    }
    puts("Socket created");

    server.sin_addr.s_addr = inet_addr(srd_addr);
    server.sin_family = AF_INET;
    server.sin_port = htons( PORT );

    //Connect to remote server
    if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0)
    {
        perror("connect failed. Error");
        return 1;
    }

    printf("Connected to %s on port %d\n", srd_addr, PORT);

    // Fill buffer
    for (int i = 0; i < SEND_SIZE; ++i) {
        message[i] = 'a' + (i % 26);
    }

    for (int i = 0; i < COUNT; ++i) {
        if (send(sock, message, SEND_SIZE, 0) < 0) {
            perror("send");
            return 1;
        }

        if ( recv(sock, server_reply, RESP_SIZE, 0) < 0) {
            perror("recv");
            return 1;
        }
    }

    close(sock);

    printf("Sending %d messages of size %d bytes with response sizes of %d bytes\r\n",
            COUNT, SEND_SIZE, RESP_SIZE);
    return 0;
}

int main(int argc, char** argv) {
    if (argc < 2) {
        printf("\r\nUsage: socketpingpong <ipaddress> [client]\r\n");
        exit(-1);
    }
    if (argc == 2)
        serverLoop(argv[1]);
    else
        clientLoop(argv[1]);
    return 0;
}

8
2017-07-23 01:28


origine


Ce qui a motivé le passage de Redhat à Debian? Du côté de Redhat, il existe plus d'outils et d'utilitaires pour vous aider à résoudre des problèmes de ce type. - ewwhite
Je contacterais la liste de diffusion Linux Kernel ou (si vous en avez une) le support Red Hat. Ils le sauront peut-être, et s’ils ne le savent pas, il y aura des personnes prêtes à «bifecter» les modifications du code du noyau pour déterminer l’origine des bogues. - Law29
Je pense que vous devriez utiliser un outil (gprof, Valgrind ou gperftools) pour profiler votre code. - Jose Raul Barreras
Que se passe-t-il si vous désactivez l'algorithme de nagle sur le client / serveur? int ndelay = 1; setsockopt (<socket>, IPPROTO_TCP, TCP_NODELAY, & flag, sizeof (int)); - la ou les différences persistent-elles? Aussi - est-ce juste pour TCP? c.-à-d. pour icmp / ping, observez-vous la même chose? - Kjetil Joergensen
De plus, existe-t-il une différence dans les réglages de fusion ou de fusion entre "rapide" et "lent"? ethtool -c <dev> et ethtool -k <dev>. Les valeurs par défaut du pilote ont peut-être changé. - Kjetil Joergensen


Réponses:


Ce n'est pas une réponse, mais il est important de calibrer les problèmes de latence / débit de manière rigoureuse. Cela pourrait vous aider à vous rapprocher de la réponse et même à aider les autres ici à vous donner de meilleures suggestions sur le processus qui en est à l'origine.

Essayez d’obtenir des données plus précises avec un Wirehark/ tshark capture sur l'interface pour,

  1. Confirmez que le débit est effectivement divisé par deux et
  2. Identifier comment la latence est distribuée (entre tx et rx)
    une. est-ce uniforme au cours du test?
    b. y a-t-il une stalle fixe quelque part?

0
2017-09-24 17:46





J'ai regardé à travers les changelogs, cela pourrait être l'introduction de QFQ

Changelog de réseau de noyau 3.0 https://kernelnewbies.org/Linux_3.0#head-96d40fb6f9c48e789386dbe59fd5b5acc9a9059d

Page de QFQ committer http://info.iet.unipi.it/~luigi/qfq/

Il fournit des garanties de service serrées à un prix extrêmement bas par paquet   Coût.


0
2018-02-22 23:13