UDP接收线程占用太多CPU资源

UDP recvfrom thread use too much CPU resources

本文关键字:太多 CPU 资源 线程 UDP      更新时间:2023-10-16

我正在写一个Windows 7 visual c++服务器应用程序,它应该以3.6 MB/s的速度接收UDP数据报。我有一个主线程,其中recvfrom()接收数据。套接字是非阻塞套接字,具有64kB的接收缓冲区。如果在套接字上没有接收到数据,线程执行睡眠(1)。

我的问题是线程占用了我的双核处理器的近50%,我不知道如何减少它。Wireshark只使用了20%,所以我的主要目标是达到类似的百分比。

你有什么想法吗?

比起轮询,您可以使用类似选择的方法来等待数据到达套接字或客户端决定关闭:

首先使你的套接字非阻塞:

u_long nonBlocking = 0;
WSAEventSelect(sock, NULL, 0);
ioctlsocket(sock, FIONBIO, &nonBlocking);

然后使用WSAWaitForMultipleEvents等待,直到数据到达或您想要取消接收:

int32_t MyRecv(THandle sock, WSAEVENT* recvCancelEvt,
               uint8_t* buffer, uint32_t bufferBytes)
{
    int32_t bytesReceived;
    WSAEVENT evt;
    DWORD ret;
    HANDLE handles[2];
    event = WSACreateEvent();
    if (NULL == evt) {
        return -1;
    }
    if (0 != WSAEventSelect(handle->iSocket, evt, FD_READ|FD_CLOSE)) {
        WSACloseEvent(evt);
        return -1;
    }
    bytesReceived = recv(sock, (char*)buffer, bufferBytes, 0);
    if (SOCKET_ERROR==received && WSAEWOULDBLOCK==WSAGetLastError()) {
        handles[0] = evt;
        handles[1] = *recvCancelEvt;
        ret = WSAWaitForMultipleEvents(2, handles, FALSE, INFINITE, FALSE);
        if (WAIT_OBJECT_0 == ret) {
            bytesReceived = recv(handle->iSocket, (char*)buffer, bufferBytes, 0);
        }
    }
    WSACloseEvent(evt);
    return bytesReceived;
}

客户端代码在recvCancelEvt上调用WSASetEvent,如果它想取消一个recv

虽然基于选择或阻塞套接字的解决方案是正确的方法,但您在100%运行一个核心的原因是由于Sleep的行为:-

查看WinAPI sleep():

这个函数使线程放弃剩余的时间的值在一段时间内变得不可运行dwMilliseconds。系统时钟以恒定的速率"滴答"。dwMilliseconds小于系统时钟的分辨率

因此,如果您正在轮询,您要么需要使用更长的睡眠时间(可能是20Ms,这通常比Windows的滴答率大一点),要么使用更精确的多媒体计时器。

我建议使用boost::asio::io_service。我们收到大约200MB/s的UDP多播流量,同时最大限度地利用现代CPU。这包括一个完整的可靠性协议和应用程序的数据调度。性能分析的瓶颈是处理,而不是boost::asio接收。代码

看起来大多数时候对recvfrom的调用都不返回数据。睡1毫秒并不多。您应该考虑增加睡眠时间(便宜,但不是最好的解决方案),或者,更好的解决方案是考虑使用事件驱动的方法。使用select()或Windows API阻塞,直到套接字信号或其他一些您感兴趣的事件发生,然后调用recvfrom。您可能需要为此重新设计程序的主循环。