최신 버전의 Linux에서 더 높은 TCP 대기 시간

필자의 연구 그룹에서는 최근 Red Hat 6.2에서 Debian 8.3으로 시스템의 OS를 업그레이드했으며 시스템간에 통합 Intel 1G NIC를 통한 TCP 왕복 시간이 약 110µs에서 220µs로 두 배로 증가한 것을 관찰했습니다.

처음에는 이것이 구성 문제라고 생각했기 때문에 tcp_low_latency=1업그레이드되지 않은 Red Hat 시스템에서 데비안 시스템으로 모든 sysctl 구성 (예 :)을 복사 했지만 문제가 해결되지 않았습니다. 다음으로, 이것이 Linux 배포 문제 일 것으로 생각되어 Red Hat 7.2를 머신에 설치했지만 왕복 시간은 약 220µs였습니다.

마지막으로, 데비안 8.3과 Red Hat 7.2가 모두 커널 3.x를 사용하고 Red Hat 6.2가 커널 2.6을 사용했기 때문에 Linux 커널 버전에 문제가있을 수 있다고 생각했습니다. 이를 테스트하기 위해 Linux 커널 2.6 및 빙고와 함께 데비안 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

하드웨어가 동일한 두 Debian 8.3 호스트간에이 실험을 실행하면 다음과 같은 결과가 나타납니다.

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

데비안 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;
}



답변

이것은 정답이 아니지만 대기 시간 / 처리량 문제를 엄격하게 교정하는 것이 중요합니다. 답변에 더 가까이 다가 가고 근본 원인 프로세스에 대한 더 나은 제안을 제공하는 다른 사람들을 도울 수도 있습니다.

인터페이스에서 wireshark / tshark 캡처로 보다 정확한 데이터를 얻으십시오 .

  1. 처리량이 실제로 절반인지 확인하고
  2. 대기 시간이 어떻게 분배되는지 식별합니다 (tx와 rx 사이)
    a. 시험에서 균일합니까?
    비. 어딘가에 마구간이 있습니까?

답변

변경 로그를 살펴본 결과 QFQ가 도입되었을 수 있습니다.

커널 3.0 네트워킹 변경 사항
https://kernelnewbies.org/Linux_3.0#head-96d40fb6f9c48e789386dbe59fd5b5acc9a9059d

QFQ 커미터 페이지
http://info.iet.unipi.it/~luigi/qfq/

매우 낮은 패킷 당 비용으로 엄격한 서비스 보장을 제공합니다.


답변