简短版本:我不想使用第三方库或帧,如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()
与以10Gbps线速发送数据的Tx主机,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_sets复制到/复制出内核空间 - 确保 fd 足够低以适应 int 帮助。
我将假设线程不在桌面上。
您还可以通过使用非阻塞套接字以及仅在显示 EAGAIN 时才调用 poll/select 来获得一些帮助(并且您没有"其他工作"要做(。