Độ trễ TCP cao hơn trong các phiên bản mới nhất của Linux


8

Trong nhóm nghiên cứu của tôi, gần đây chúng tôi đã nâng cấp HĐH trên các máy của chúng tôi từ Red Hat 6.2 lên Debian 8.3 và nhận thấy rằng thời gian khứ hồi TCP thông qua các NIC Intel 1G tích hợp giữa các máy của chúng tôi đã tăng gấp đôi từ khoảng 110 đấm lên 220.

Lúc đầu, tôi nghĩ đó là vấn đề về cấu hình, vì vậy tôi đã sao chép tất cả các cấu hình sysctl (chẳng hạn như tcp_low_latency=1) từ các máy Red Hat chưa được nâng cấp sang các máy Debian và điều đó không khắc phục được sự cố. Tiếp theo, tôi nghĩ rằng đây có thể là sự cố phân phối Linux và đã cài đặt Red Hat 7.2 trên các máy, nhưng thời gian khứ hồi vẫn còn khoảng 220 Lời.

Cuối cùng, tôi nhận ra rằng có lẽ vấn đề xảy ra với các phiên bản kernel của Linux vì Debian 8.3 và Red Hat 7.2 đều sử dụng kernel 3.x trong khi Red Hat 6.2 sử dụng kernel 2.6. Vì vậy, để kiểm tra điều này, tôi đã cài đặt Debian 6.0 với Linux kernel 2.6 và bingo! Thời gian đã nhanh chóng trở lại ở mức 110.

Những người khác cũng đã trải qua những độ trễ cao hơn này trong các phiên bản mới nhất của Linux và có cách giải quyết nào không?


Ví dụ làm việc tối thiểu

Dưới đây là một ứng dụng C ++ có thể được sử dụng để đánh giá độ trễ. Nó đo độ trễ bằng cách gửi tin nhắn, chờ phản hồi và sau đó gửi tin nhắn tiếp theo. Nó thực hiện điều này 100.000 lần với các tin nhắn 100 byte. Do đó, chúng tôi có thể chia thời gian thực hiện của khách hàng cho 100.000 để có được độ trễ chuyến đi khứ hồi. Để sử dụng lần đầu tiên biên dịch chương trình này:

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

Tiếp theo chạy phiên bản ứng dụng phía máy chủ trên máy chủ (giả sử trên 192.168.0.101). Chúng tôi chỉ định IP để đảm bảo rằng chúng tôi đang lưu trữ trên một giao diện nổi tiếng.

socketpingpong 192.168.0.101

Và sau đó sử dụng tiện ích Unix timeđể đo thời gian thực hiện của máy khách.

time socketpingpong 192.168.0.101 client

Chạy thử nghiệm này giữa hai máy chủ Debian 8.3 có phần cứng giống hệt nhau cho kết quả như sau.

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

Kết quả Debian 6.0 là

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

Mã số:

#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
Điều gì đã thúc đẩy việc chuyển từ Redhat sang Debian ? Về phía Redhat, có nhiều công cụ và tiện ích hơn để giúp giải quyết các vấn đề như thế này.
ewwhite

1
Tôi sẽ liên hệ với danh sách gửi thư Linux Kernel hoặc (nếu bạn có) hỗ trợ Red Hat. Họ có thể biết, và nếu họ không có, những người được thiết lập để thay đổi mã nhân "bisect" để tìm ra lỗi xuất phát từ đâu.
Luật29

Tôi nghĩ bạn nên sử dụng một số công cụ (gprof, Valgrind hoặc gperftools) để hồ sơ mã của bạn.
Jose Raul Barrera

Điều gì xảy ra nếu bạn tắt thuật toán của nagle trên cả máy khách / máy chủ? int ndelay = 1; setsockopt (<socket>, IPPROTO_TCP, TCP_NODELAY, & flag, sizeof (int)); - sự khác biệt có tồn tại không? Ngoài ra - đây chỉ là cho tcp? tức là đối với icmp / ping bạn có quan sát giống nhau không?
Kjetil Joergensen

1
Ngoài ra - có sự khác biệt nào trong cài đặt kết hợp hoặc giảm tải giữa "nhanh" và "chậm" không? ethtool -c <dev> và ethtool -k <dev>. Trình điều khiển mặc định (s) có thể đã thay đổi.
Kjetil Joergensen

Câu trả lời:


1

Đây không phải là một câu trả lời nhưng điều quan trọng là phải hiệu chỉnh các vấn đề về độ trễ / thông lượng một cách chặt chẽ. Nó có thể giúp bạn đến gần hơn với câu trả lời và thậm chí giúp những người khác ở đây đưa ra cho bạn những đề xuất tốt hơn về quy trình gây ra root.

Hãy thử lấy dữ liệu chính xác hơn bằng cách chụp wireshark / tshark trên giao diện để,

  1. Xác nhận rằng thông lượng thực sự giảm một nửa và
  2. Xác định độ trễ được phân phối (giữa tx và rx)
    a. nó có thống nhất trong bài kiểm tra không?
    b. Có một gian hàng gộp ở đâu đó?

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.