最新版本的Linux中更高的TCP延迟


8

在我的研究小组中,我们最近将机器上的操作系统从Red Hat 6.2升级到Debian 8.3,并观察到机器之间通过集成的Intel 1G NIC进行的TCP往返时间从大约110µs翻了一番,达到了220µs。

最初,我认为这是一个配置问题,因此我将所有sysctl配置(例如tcp_low_latency=1)从未升级的Red Hat计算机复制到了Debian计算机,但这并不能解决问题。接下来,我认为这可能是Linux发行问题,并在计算机上安装了Red Hat 7.2,但是往返时间保持在220µs左右。

最后,我发现问题可能出在Linux内核版本上,因为Debian 8.3和Red Hat 7.2都使用了内核3.x,而Red Hat 6.2使用了内核2.6。因此,要进行测试,我安装了带有Linux内核2.6和Bingo的Debian 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

Debian 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
我会联系Linux Kernel邮件列表或(如果有)Red Hat支持。他们可能知道,如果没有,他们将全都设置成“平分”内核代码更改以找出错误的来源。
Law 2016年

我认为您应该使用某些工具(gprof,Valgrind或gperftools)来分析代码。
何塞·劳尔·巴雷拉斯

如果在两个客户端/服务器上都禁用nagle的算法会怎样?int ndelay = 1; setsockopt(<套接字>,IPPROTO_TCP,TCP_NODELAY,&flag,sizeof(int)); -差异是否仍然存在?另外-这仅用于tcp吗?即对于icmp / ping,您是否也观察到相同?
Kjetil Joergensen

1
另外-“快速”和“慢速”之间的合并或卸载设置是否有差异?ethtool -c <dev>和ethtool -k <dev>。驱动程序默认值可能已更改。
Kjetil Joergensen

Answers:


1

这不是答案,但是严格校准延迟/吞吐量问题很重要。它可能会帮助您更接近答案,甚至可以帮助此处的其他人为您提供有关根本原因过程的更好建议。

尝试通过接口上的wireshark / tshark捕获获取更准确的数据,

  1. 确认吞吐量实际上减半,并且
  2. 确定延迟的分布方式(在tx和rx之间)
    a。在测试中是否统一?
    b。某处有一个集结摊位吗?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.