测量Unix域套接字的延迟

8 c unix sockets performance benchmarking

我想比较两个进程之间的Unix域套接字的性能与另一个进程的性能.

我有一个基本程序,它创建一个套接字对,然后调用fork.然后,它测量RTT以将8192个字节发送到另一个进程并返回(每次迭代都不同).

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, char **argv) {
    int i, pid, sockpair[2];
    char buf[8192];
    struct timespec tp1, tp2;

    assert(argc == 2);

    // Create a socket pair using Unix domain sockets with reliable,
    // in-order data transmission.
    socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair);

    // We then fork to create a child process and then start the benchmark.
    pid = fork();

    if (pid == 0) { // This is the child process.
        for (i = 0; i < atoi(argv[1]); i++) {
            assert(recv(sockpair[1], buf, sizeof(buf), 0) > 0);
            assert(send(sockpair[1], buf, sizeof(buf), 0) > 0);
        }
    } else { // This is the parent process.
        for (i = 0; i < atoi(argv[1]); i++) {
            memset(buf, i, sizeof(buf));
            buf[sizeof(buf) - 1] = '\0';
            assert(clock_gettime(CLOCK_REALTIME, &tp1) == 0);
            assert(send(sockpair[0], buf, sizeof(buf), 0) > 0);
            assert(recv(sockpair[0], buf, sizeof(buf), 0) > 0);
            assert(clock_gettime(CLOCK_REALTIME, &tp2) == 0);
            printf("%lu ns\n", tp2.tv_nsec - tp1.tv_nsec);
        }
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是,我注意到,对于每次重复测试,第一次运行(i = 0)的经过时间总是异常值:

79306 ns
18649 ns
19910 ns
19601 ns
...
Run Code Online (Sandbox Code Playgroud)

我想知道内核是否必须在第一次调用时做一些最终设置send()- 例如,在内核中分配8192个字节来缓冲调用send()和之间的数据recv()

Wil*_*eau 2

并不是数据复制需要额外花费 80 微秒,这会非常慢(仅 100 MB/s),而是事实上您正在使用两个进程,并且当父进程第一次发送数据时,这些数据需要等待子进程完成 fork 并开始执行。

如果你绝对想使用两个进程,你应该首先在另一个方向执行发送,以便父进程可以等待子进程准备好后再开始发送。

例如: 孩子:

  send();
  recv();
  send();
Run Code Online (Sandbox Code Playgroud)

家长:

  recv();
  gettime();
  send();
  recv();
  gettime();
Run Code Online (Sandbox Code Playgroud)

此外,您还需要认识到,您的测试在很大程度上取决于各个 CPU 核心上的进程放置,如果在同一核心上运行,将导致任务切换。

因此,我强烈建议您使用单一流程执行测量。即使没有轮询也没有任何东西,只要您保留适合套接字缓冲区的相当小的块,您就可以这样做:

gettime();
send();
recv();
gettime();
Run Code Online (Sandbox Code Playgroud)

您应该首先执行非测量往返以确保分配缓冲区。我很确定你在这里的时间会少得多。