Höhere TCP-Latenzen in den neuesten Linux-Versionen

8

In meiner Forschungsgruppe haben wir kürzlich das Betriebssystem unserer Computer von Red Hat 6.2 auf Debian 8.3 aktualisiert und festgestellt, dass sich die TCP-Roundtrip-Zeit durch die integrierten Intel 1G-Netzwerkkarten zwischen unseren Computern von etwa 110 µs auf 220 µs verdoppelt hat.

Zuerst dachte ich, es sei ein Konfigurationsproblem, also habe ich alle sysctl-Konfigurationen (z. B. tcp_low_latency=1) von den nicht aktualisierten Red Hat-Computern auf die Debian-Computer kopiert, wodurch das Problem nicht behoben wurde. Als nächstes dachte ich, dass dies ein Linux-Distributionsproblem gewesen sein könnte und installierte Red Hat 7.2 auf den Maschinen, aber die Roundtrip-Zeiten blieben bei 220µs.

Schließlich stellte ich fest, dass das Problem möglicherweise bei Linux-Kernelversionen lag, da Debian 8.3 und Red Hat 7.2 beide Kernel 3.x verwendet hatten, während Red Hat 6.2 Kernel 2.6 verwendete. Um dies zu testen, habe ich Debian 6.0 mit Linux Kernel 2.6 und Bingo installiert! Die Zeiten waren bei 110 us wieder schnell.

Haben andere diese höheren Latenzen auch in den neuesten Linux-Versionen erlebt, und gibt es bekannte Problemumgehungen?


Minimum Arbeitsbeispiel

Unten finden Sie eine C ++ - Anwendung, mit der Sie die Latenz messen können. Es misst die Latenz, indem es eine Nachricht sendet, auf eine Antwort wartet und dann die nächste Nachricht sendet. Dies geschieht 100.000 Mal mit 100-Byte-Nachrichten. Somit können wir die Ausführungszeit des Clients durch 100.000 teilen, um die Roundtrip-Latenzen zu erhalten. Um dies zu verwenden, kompilieren Sie zuerst das Programm:

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

Führen Sie als Nächstes die serverseitige Version der Anwendung auf einem Host aus (z. B. 192.168.0.101). Wir geben die IP an, um sicherzustellen, dass wir auf einer bekannten Schnittstelle hosten.

socketpingpong 192.168.0.101

Verwenden Sie dann das Unix-Dienstprogramm time, um die Ausführungszeit des Clients zu messen.

time socketpingpong 192.168.0.101 client

Das Ausführen dieses Experiments zwischen zwei Debian 8.3-Hosts mit identischer Hardware führt zu den folgenden Ergebnissen.

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

Debian 6.0 Ergebnisse sind

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;
}
Stephen
quelle
2
Was veranlasste den Wechsel von Redhat zu Debian ? Auf der Redhat-Seite gibt es weitere Tools und Dienstprogramme, mit denen Sie solche Probleme lösen können.
ewwhite
1
Ich würde mich an die Linux Kernel Mailingliste oder (falls vorhanden) an den Red Hat Support wenden. Sie wissen es vielleicht, und wenn sie es nicht tun, wird es Leute geben, die alle darauf eingestellt sind, Kernel-Code-Änderungen zu "halbieren", um herauszufinden, woher Fehler kommen.
Law29
Ich denke, Sie sollten ein Tool (gprof, Valgrind oder gperftools) verwenden, um Ihren Code zu profilieren.
Jose Raul Barreras
Was passiert, wenn Sie den Nagle-Algorithmus auf Client / Server deaktivieren? int ndelay = 1; setsockopt (<socket>, IPPROTO_TCP, TCP_NODELAY & flag, sizeof (int)); - Bleiben die Unterschiede bestehen? Auch - ist das nur für TCP? dh für icmp / ping beobachten Sie das gleiche?
Kjetil Joergensen
1
Auch - gibt es einen oder mehrere Unterschiede in den Koaleszenz- oder Offload-Einstellungen zwischen "schnell" und "langsam"? ethtool -c <dev> und ethtool -k <dev>. Möglicherweise haben sich die Treiberstandards geändert.
Kjetil Joergensen

Antworten:

1

Dies ist keine Antwort, aber es ist wichtig, Latenz- / Durchsatzprobleme genau zu kalibrieren. Es könnte Ihnen helfen, näher an die Antwort heranzukommen und sogar anderen hier zu helfen, bessere Vorschläge für den Grundverursachungsprozess zu machen.

Versuchen Sie, genauere Daten mit einem Wireshark / Tshark-Capture auf der Schnittstelle zu erhalten, um

  1. Bestätigen Sie, dass der Durchsatz tatsächlich halbiert ist und
  2. Identifizieren Sie, wie die Latenz verteilt ist (zwischen tx und rx)
    . A. ist es über den Test einheitlich?
    b. Gibt es irgendwo einen Klumpenstand?
nik
quelle