gpt4 book ai didi

c - 一种在 C 中非常有效(快速)的网络轮询方法

转载 作者:行者123 更新时间:2023-12-03 11:51:50 25 4
gpt4 key购买 nike

短版 :我不想使用第三方库或框架,如 Netmap 或 DPDK,有什么比 poll()select() 更快,或者我可以让这些调用更高效吗?

完整版 :我有一个单线程应用程序,它使用单个套接字尽可能快地发送数据,我正在使用 valgrind/cachegrind/callgrind 来尝试提高效率(从而提高应用程序的吞吐量)。

目前接收主机站点处于无限循环中,试图尽可能快地检查接收到的数据(这需要是非阻塞的,当没有数据包来处理“其他东西”时)。我在接收主机上使用 select() 因为 select() 使用提供微秒轮询频率的时间值,而 poll() 使用毫秒值。 Callgrind 向我展示了 FD_SET() 的指令执行次数几乎是 select() 操作的两倍,而 select() 的一个缺点是我必须在接收循环的每次迭代中运行 FD_SET():

         .    .    .          .     .    .          .     .    .              // Poll for incoming frames
4,361,236 1 1 2,180,618 0 0 2,180,618 0 0 TEST_INTERFACE->TV_SELECT_DELAY.tv_sec = 0;
4,361,236 0 0 2,180,618 0 0 2,180,618 0 0 TEST_INTERFACE->TV_SELECT_DELAY.tv_usec = 000000;
56,696,068 2 2 15,264,326 0 0 2,180,618 0 0 FD_SET(TEST_INTERFACE->SOCKET_FD, &TEST_INTERFACE->FD_READS);
. . . . . . . . .
. . . . . . . . . TEST_INTERFACE->SELECT_RET_VAL = select(TEST_INTERFACE->SOCKET_FD_COUNT,
. . . . . . . . . &TEST_INTERFACE->FD_READS,
. . . . . . . . . NULL, NULL,
30,528,652 1 1 10,903,090 0 0 15,264,326 0 0 &TEST_INTERFACE->TV_SELECT_DELAY);
. . . . . . . . .
. . . . . . . . .
9,432,052 0 0 4,361,236 0 0 0 0 0 if (TEST_INTERFACE->SELECT_RET_VAL > 0 &&
7,450,590 1 1 2,128,740 0 0 0 0 0 FD_ISSET(TEST_INTERFACE->SOCKET_FD, &TEST_INTERFACE->FD_READS))
. . . . . . . . . {
. . . . . . . . .
. . . . . . . . . RX_LEN = recvfrom(TEST_INTERFACE->SOCKET_FD,
. . . . . . . . . FRAME_HEADERS->RX_BUFFER,
. . . . . . . . . TEST_PARAMS->F_SIZE_TOTAL,
5,321,850 1 1 2,128,740 0 0 2,838,320 0 0 0, NULL, NULL);
. . . . . . . . .

我使用 select() 获得平均 130-140Mpbs 的接收吞吐量。使用 poll() 我得到的平均速度为 150-160Mbps。
         .    .    .         .     .    .         .     .    .              // Poll for incoming frames
7,347,032 2 2 1,836,758 0 0 4,591,895 0 0 TEST_INTERFACE->SELECT_RET_VAL = poll(TEST_INTERFACE->fds, 1, 0);
. . . . . . . . .
. . . . . . . . .
3,673,516 0 0 1,836,758 0 0 0 0 0 if (TEST_INTERFACE->SELECT_RET_VAL > 0)
. . . . . . . . . {
. . . . . . . . .
2,142,186 0 0 714,062 0 0 0 0 0 if ( TEST_INTERFACE->fds[0].revents & POLLIN )
714,062 0 0 357,031 0 0 357,031 0 0 TEST_INTERFACE->fds[0].revents = 0;
. . . . . . . . .
. . . . . . . . . RX_LEN = recvfrom(TEST_INTERFACE->SOCKET_FD,
. . . . . . . . . FRAME_HEADERS->RX_BUFFER,
. . . . . . . . . TEST_PARAMS->F_SIZE_TOTAL,
5,355,465 1 1 2,142,186 0 0 2,856,248 0 0 0, NULL, NULL);

使用 poll() 我传递了 0 的超时值,所以希望这不会在任何时候阻塞,但我不确定这是否正确,它实际上比 select() 快。

在实验室中,我有几台服务器背靠背使用 10Gbps NIC。使用 select() 和 Tx 主机以 10Gbps 的线速发送数据,Rx 主机可以处理大约 9.5Gbps 的流量。上面的结果只是在我的笔记本电脑上的虚拟机之间(实验室目前停止运行),表明 poll() 给了我在 Rx 主机上的轻微增加。在上面的结果中,我们可以看到 poll() 更快,但是 10Gbps 是每几十纳秒一个数据包,所以我不确定(当实验室再次工作时)我是否会看到任何改进。

所以我想我有两个问题:
  • 在我的 poll()select() 测试中,因为我使用的是单个套接字,所以 poll() 稍微快一点,但是这适用于更快的连接,10/20/30/40Gbps,或者在那些速度下 select() 会更快吗?
  • 由于我只使用一个套接字,如果我从 select()/poll() 切换到 epoll() 是否会提高速度?或者我可以使用其他一些“内置”方法,比 poll()select() 更快,或者我可以做些什么来减少它们的执行时间?
  • 最佳答案

    如果要选择的 fd(s) 没有更改,您可以(通常)作弊并使用预先计算的 int。

    int   fds = 1 << fd;
    int fdp;

    do {
    fdp = fds;
    n = select(fd+1, &fdp, ...

    如果 FD_SET 是瓶颈,这应该会有所帮助。

    select 的低效率之一是将大的 fd_set 复制进/出内核空间——确保 fd 足够低以适合 int 有帮助。

    我将假设线程不在桌面上。

    您还可以通过使用非阻塞套接字并仅在显示 EAGAIN 时调用 poll/select 获得一些帮助(并且您没有任何“其他工作”要做)。

    关于c - 一种在 C 中非常有效(快速)的网络轮询方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35386770/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com