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 do TCP através das NICs Intel 1G integradas entre nossas máquinas dobrou de cerca de 110µs para 220µs.
No começo, eu pensei que era um problema de configuração, então copiei todas as configurações 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.
Finalmente, imaginei que talvez o problema estivesse nas versões do kernel Linux, já que o Debian 8.3 e o Red Hat 7.2 haviam usado 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 2.6 do Linux e o bingo! Os tempos foram rápidos novamente a 110µs.
Outros também tiveram essas latências mais altas nas versões mais recentes do Linux e existem soluções alternativas conhecidas?
Exemplo Mínimo de Trabalho
Abaixo está um aplicativo C ++ que pode ser usado para comparar a latência. Ele mede a latência enviando uma mensagem, aguardando uma resposta e enviando a próxima mensagem. Faz isso 100.000 vezes com mensagens de 100 bytes. Assim, podemos dividir o tempo de execução do cliente por 100.000 para obter as latências de ida e volta. Para usar isso, primeiro compile 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 (por exemplo, 192.168.0.101). Especificamos o IP para garantir que estamos hospedando em uma interface 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 deste experimento 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;
}