Latensi TCP lebih tinggi di versi terbaru Linux

8

Dalam grup riset saya, kami baru-baru ini memutakhirkan OS pada mesin kami dari Red Hat 6.2 ke Debian 8.3 dan mengamati bahwa waktu perjalanan TCP melalui Intel 1G NIC yang terintegrasi di antara mesin kami telah berlipat ganda dari sekitar 110μs menjadi 220μs.

Pada awalnya, saya pikir itu adalah masalah konfigurasi, jadi saya menyalin semua konfigurasi sysctl (seperti tcp_low_latency=1) dari mesin Red Hat yang tidak diupgrade ke mesin Debian dan itu tidak memperbaiki masalah. Selanjutnya, saya pikir ini mungkin masalah distribusi Linux dan menginstal Red Hat 7.2 pada mesin, tetapi waktu pulang pergi tetap sekitar 220μs.

Akhirnya, saya pikir mungkin masalahnya dengan versi kernel Linux sejak Debian 8.3 dan Red Hat 7.2 sama-sama menggunakan kernel 3.x sedangkan Red Hat 6.2 menggunakan kernel 2.6. Jadi untuk menguji ini, saya menginstal Debian 6.0 dengan kernel Linux 2.6 dan bingo! Waktu cepat lagi pada 110μs.

Adakah yang lain juga mengalami latensi yang lebih tinggi ini dalam versi terbaru Linux, dan adakah solusi yang diketahui?


Contoh Kerja Minimum

Di bawah ini adalah aplikasi C ++ yang dapat digunakan untuk benchmark latensi. Ini mengukur latensi dengan mengirim pesan, menunggu respons, dan kemudian mengirim pesan berikutnya. Ia melakukan ini 100.000 kali dengan pesan 100-byte. Dengan demikian, kita dapat membagi waktu eksekusi klien dengan 100.000 untuk mendapatkan latensi perjalanan pulang pergi. Untuk menggunakan ini, pertama kompilasi program:

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

Selanjutnya jalankan versi aplikasi sisi server pada host (katakanlah pada 192.168.0.101). Kami menentukan IP untuk memastikan bahwa kami hosting pada antarmuka yang terkenal.

socketpingpong 192.168.0.101

Dan kemudian gunakan utilitas Unix timeuntuk mengukur waktu eksekusi klien.

time socketpingpong 192.168.0.101 client

Menjalankan percobaan ini antara dua host Debian 8.3 dengan perangkat keras yang identik memberikan hasil sebagai berikut.

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

Hasil Debian 6.0 adalah

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

Kode:

#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
sumber
2
Apa yang mendorong perpindahan dari Redhat ke Debian ? Di sisi Redhat, ada lebih banyak alat dan utilitas untuk membantu mengatasi masalah seperti ini.
ewwhite
1
Saya akan menghubungi milis Linux Kernel atau (jika ada) dukungan Red Hat. Mereka mungkin tahu, dan jika tidak, akan ada orang yang siap untuk "membagi" perubahan kode kernel untuk mencari tahu dari mana bug berasal.
Law29
Saya pikir Anda harus menggunakan beberapa alat (gprof, Valgrind atau gperftools) untuk profil kode Anda.
Jose Raul Barreras
Apa yang terjadi jika Anda menonaktifkan algoritma nagle di kedua klien / server? int ndelay = 1; setsockopt (<socket>, IPPROTO_TCP, TCP_NODELAY, & flag, sizeof (int)); - apakah perbedaannya tetap ada? Juga - apakah ini hanya untuk tcp? yaitu untuk icmp / ping Anda amati hal yang sama?
Kjetil Joergensen
1
Juga - apakah ada perbedaan pengaturan penyatuan atau pembongkaran antara "cepat" dan "lambat"? ethtool -c <dev> dan ethtool -k <dev>. Default driver mungkin telah berubah.
Kjetil Joergensen

Jawaban:

1

Ini bukan jawaban tetapi penting untuk mengkalibrasi masalah latensi / throughput dengan ketat. Mungkin membantu Anda lebih dekat dengan jawaban dan bahkan membantu orang lain di sini memberi Anda saran yang lebih baik tentang proses penyebab root.

Coba dapatkan data yang lebih akurat dengan tangkapan wireshark / tshark pada antarmuka,

  1. Konfirmasikan bahwa throughput sebenarnya dibagi dua dan
  2. Identifikasi bagaimana latensi didistribusikan (antara tx dan rx)
    a. apakah seragam dalam ujian?
    b. apakah ada kios yang disatukan di suatu tempat?
nik
sumber