Maiores latências de TCP nas versões mais recentes do Linux

8

Em meu grupo de pesquisa, recentemente atualizamos o sistema operacional em nossas máquinas do Red Hat 6.2 para o Debian 8.3 e observamos que o tempo de ida e volta TCP através das placas de rede integradas Intel 1G entre nossas máquinas dobrou de 110µs para 220µs.

Inicialmente, achei que era um problema de configuração, então copiei todas as configurações de sysctl (como tcp_low_latency=1 ) das máquinas Red Hat não atualizadas para as máquinas Debian e isso não resolveu o problema. Em seguida, pensei que isso poderia ter sido um problema de distribuição do Linux e instalei o Red Hat 7.2 nas máquinas, mas os tempos de ida e volta permaneceram em torno de 220µs.

Por último, imaginei que talvez o problema estivesse nas versões do kernel do Linux, uma vez que o Debian 8.3 e o Red Hat 7.2 usaram o kernel 3.x enquanto o Red Hat 6.2 usava o kernel 2.6. Então, para testar isso, instalei o Debian 6.0 com o kernel Linux 2.6 e o bingo! Os tempos foram rápidos novamente em 110µs.

Outras pessoas também experimentaram essas latências mais altas nas versões mais recentes do Linux e existem soluções conhecidas?

Exemplo Mínimo de Trabalho

Abaixo está um aplicativo C ++ que pode ser usado para avaliar a latência. Ele mede a latência enviando uma mensagem, aguardando uma resposta e enviando a próxima mensagem. Ele faz isso 100.000 vezes com mensagens de 100 bytes. Assim, podemos dividir o tempo de execução do cliente em 100.000 para obter as latências de ida e volta. Para usar este primeiro compilar o programa:

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

Em seguida, execute a versão do aplicativo do lado do servidor em um host (digamos em 192.168.0.101). Nós especificamos o IP para garantir que estamos hospedando em uma interface bem conhecida.

socketpingpong 192.168.0.101

E, em seguida, use o utilitário Unix time para medir o tempo de execução do cliente.

time socketpingpong 192.168.0.101 client

A execução desta experiência entre dois hosts Debian 8.3 com hardware idêntico fornece os seguintes resultados.

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

Os resultados do Debian 6.0 são

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

Código:

#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;
}
    
por Stephen 23.07.2016 / 03:28

2 respostas

0

Isso não é uma resposta, mas é importante calibrar rigorosamente os problemas de latência / taxa de transferência. Isso pode ajudá-lo a se aproximar da resposta e até mesmo ajudar os outros a dar sugestões melhores sobre o processo causador da raiz.

Tente obter dados mais precisos com uma captura de wireshark / tshark na interface para

  1. Confirme que a taxa de transferência é reduzida pela metade e
  2. Identifique como a latência é distribuída (entre tx e rx)
    uma. é uniforme sobre o teste?
    b. há uma barraca aglomerada em algum lugar?
por 24.09.2016 / 19:46
0

Eu olhei através dos changelogs, poderia ser a introdução do QFQ

Registro de alterações de rede do Kernel 3.0 link

Página do committer do QFQ link

It provides tight service guarantees at an extremely low per-packet cost.

    
por 23.02.2017 / 00:13