Linuxの最新バージョンでのより高いTCPレイテンシ


8

私の研究グループでは、最近、マシンのOSをRed Hat 6.2からDebian 8.3にアップグレードし、マシン間の統合Intel 1G NICを介したTCPラウンドトリップ時間が約110µsから220µsに倍増したことを観察しました。

最初は構成の問題だと思ったので、tcp_low_latency=1アップグレードされていないRed HatマシンからDebianマシンにすべてのsysctl構成(など)をコピーしましたが、問題は解決しませんでした。次に、これはLinuxディストリビューションの問題であると考え、マシンにRed Hat 7.2をインストールしましたが、往復時間は約220µsのままでした。

最後に、Debian 8.3とRed Hat 7.2の両方がカーネル3.xを使用していて、Red Hat 6.2がカーネル2.6を使用していたため、問題はLinuxカーネルバージョンにあると考えました。これをテストするために、Linuxカーネル2.6とビンゴでDebian 6.0をインストールしました。時間は再び110µsで速くなりました。

他の人も、最新バージョンのLinuxでこれらの高いレイテンシを経験しましたか?既知の回避策はありますか?


最小作業例

以下は、レイテンシのベンチマークに使用できるC ++アプリケーションです。メッセージを送信し、応答を待ってから、次のメッセージを送信することにより、レイテンシを測定します。100バイトのメッセージでこれを100,000回行います。したがって、クライアントの実行時間を100,000で割ると、往復の待ち時間が得られます。これを使用するには、まずプログラムをコンパイルします。

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

次に、ホストでアプリケーションのサーバー側バージョンを実行します(たとえば、192.168.0.101)。IPを指定して、よく知られているインターフェイスでホストしていることを確認します。

socketpingpong 192.168.0.101

そして、Unixユーティリティtimeを使用して、クライアントの実行時間を測定します。

time socketpingpong 192.168.0.101 client

同一のハードウェアを備えた2つのDebian 8.3ホスト間でこの実験を実行すると、次の結果が得られます。

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

Debian 6.0の結果は

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

コード:

#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;
}

2
RedhatからDebianに移行したきっかけは何ですか?Redhat側には、このような問題の解決に役立つツールとユーティリティが他にもあります。
ewwhite 2016

1
Linuxカーネルのメーリングリストまたは(あれば)Red Hatサポートに連絡します。彼らは知っているかもしれませんし、そうでなければ、バグがどこから来たかを見つけるためにカーネルコードを「二等分」するように変更するすべての人がいるでしょう。
法律29 2016

コードのプロファイルには、いくつかのツール(gprof、Valgrind、またはgperftools)を使用する必要があると思います。
Jose Raul Barreras 2016

クライアント/サーバーの両方でnagleのアルゴリズムを無効にするとどうなりますか?int ndelay = 1; setsockopt(<ソケット>、IPPROTO_TCP、TCP_NODELAY、&flag、sizeof(int)); -違いは持続しますか?また-これはtcpだけですか?つまり、icmp / pingの場合も同じですか?
Kjetil Joergensen

1
また、「高速」と「低速」の合体またはオフロード設定に違いはありますか?ethtool -c <dev>およびethtool -k <dev>。ドライバのデフォルトが変更された可能性があります。
Kjetil Joergensen

回答:


1

これは答えではありませんが、レイテンシ/スループットの問題を厳密に調整することが重要です。それはあなたが答えに近づくのに役立つかもしれませんし、ここの他の人が根本的な原因となるプロセスについてより良い提案をするのを助けるかもしれません。

インターフェース上のwireshark / tsharkキャプチャでより正確なデータを取得してみてください、

  1. スループットが実際に半分になり、
  2. レイテンシの分散方法(txとrxの間)を特定します
    。テスト全体で均一ですか?
    b。どこかに集中露店はありますか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.