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


8

필자의 연구 그룹에서는 최근 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;
}

2
Redhat에서 Debian으로 이동 한 이유는 무엇입니까 ? Redhat 측에는 이와 같은 문제를 해결하는 데 도움이되는 더 많은 도구와 유틸리티가 있습니다.
ewwhite

1
나는 리눅스 커널 메일 링리스트 나 (있는 경우) Red Hat 지원에 연락 할 것입니다. 그들은 알고있을 수도 있고, 그렇지 않다면 버그가 어디에서 왔는지 알아 내기 위해 커널 코드 변경을 "거의"바꾸는 사람들이있을 것입니다.
법률 29

코드를 프로파일 링하려면 도구 (gprof, Valgrind 또는 gperftools)를 사용해야한다고 생각합니다.
Jose Raul Barreras

클라이언트 / 서버에서 nagle의 알고리즘을 비활성화하면 어떻게됩니까? int ndelay = 1; setsockopt (<소켓>, IPPROTO_TCP, TCP_NODELAY, & flag, sizeof (int)); -차이가 지속됩니까? 또한-이것은 단지 tcp입니까? 즉, icmp / ping의 경우에도 마찬가지입니까?
Kjetil Joergensen

1
또한 "fast"와 "slow"사이의 병합 또는 오프로드 설정에 차이가 있습니까? ethtool -c <dev> 및 ethtool -k <dev>. 드라이버 기본값이 변경되었을 수 있습니다.
Kjetil Joergensen

답변:


1

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

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

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.