Dalam grup riset saya, kami baru-baru ini memutakhirkan OS pada mesin kami dari Red Hat 6.2 ke Debian 8.3 dan mengamati bahwa waktu perjalanan TCP melalui Intel 1G NIC yang terintegrasi di antara mesin kami telah berlipat ganda dari sekitar 110μs menjadi 220μs.
Pada awalnya, saya pikir itu adalah masalah konfigurasi, jadi saya menyalin semua konfigurasi sysctl (seperti tcp_low_latency=1
) dari mesin Red Hat yang tidak diupgrade ke mesin Debian dan itu tidak memperbaiki masalah. Selanjutnya, saya pikir ini mungkin masalah distribusi Linux dan menginstal Red Hat 7.2 pada mesin, tetapi waktu pulang pergi tetap sekitar 220μs.
Akhirnya, saya pikir mungkin masalahnya dengan versi kernel Linux sejak Debian 8.3 dan Red Hat 7.2 sama-sama menggunakan kernel 3.x sedangkan Red Hat 6.2 menggunakan kernel 2.6. Jadi untuk menguji ini, saya menginstal Debian 6.0 dengan kernel Linux 2.6 dan bingo! Waktu cepat lagi pada 110μs.
Adakah yang lain juga mengalami latensi yang lebih tinggi ini dalam versi terbaru Linux, dan adakah solusi yang diketahui?
Contoh Kerja Minimum
Di bawah ini adalah aplikasi C ++ yang dapat digunakan untuk benchmark latensi. Ini mengukur latensi dengan mengirim pesan, menunggu respons, dan kemudian mengirim pesan berikutnya. Ia melakukan ini 100.000 kali dengan pesan 100-byte. Dengan demikian, kita dapat membagi waktu eksekusi klien dengan 100.000 untuk mendapatkan latensi perjalanan pulang pergi. Untuk menggunakan ini, pertama kompilasi program:
g++ -o socketpingpong -O3 -std=c++0x Server.cpp
Selanjutnya jalankan versi aplikasi sisi server pada host (katakanlah pada 192.168.0.101). Kami menentukan IP untuk memastikan bahwa kami hosting pada antarmuka yang terkenal.
socketpingpong 192.168.0.101
Dan kemudian gunakan utilitas Unix time
untuk mengukur waktu eksekusi klien.
time socketpingpong 192.168.0.101 client
Menjalankan percobaan ini antara dua host Debian 8.3 dengan perangkat keras yang identik memberikan hasil sebagai berikut.
real 0m22.743s
user 0m0.124s
sys 0m1.992s
Hasil Debian 6.0 adalah
real 0m11.448s
user 0m0.716s
sys 0m0.312s
Kode:
#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;
}