UDP-поток из потока использует слишком много ресурсов процессора

Я пишу серверное приложение для Windows 7 visual C++, которое должно получать дейтаграммы UDP с 3,6 МБ / с. У меня есть основной поток, где recvfrom() получает данные. Сокет является неблокирующим и имеет приемный буфер 64 КБ. Если данные не были получены в сокете, поток выполняет режим сна (1).

Моя проблема в том, что поток использует почти 50% моего двухъядерного процессора, и я понятия не имею, как его уменьшить. Wireshark использует только 20%, поэтому моя главная цель - добиться такого же процента.

Есть ли у вас какие-либо идеи?

4 ответа

Решение

Вместо опроса вы можете использовать подход, подобный выбору, чтобы дождаться, пока данные поступят в ваш сокет или клиент решит завершить работу:

Сначала сделайте ваш сокет неблокирующим:

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

затем используйте WSAWaitForMultipleEvents, чтобы дождаться получения данных или отмены recv:

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;
    }
    if (0 != WSAEventSelect(handle->iSocket, evt, FD_READ|FD_CLOSE)) {
        WSACloseEvent(event);
        return;
    }

    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;
}

Код клиента будет звонить WSASetEvent на recvCancelEvt если он хотел отменить recv.

В то время как решения, основанные на Select или блокирующих сокетах, являются правильным подходом, причина того, что одно ядро ​​работает на 100%, связана с режимом Sleep:

Посмотрите документы для сна WinAPI ():

Эта функция заставляет поток освобождать оставшуюся часть своего временного интервала и становится недоступным в течение интервала, основанного на значении dwMilliseconds. Системные часы "тикают" с постоянной скоростью. Если dwMilliseconds меньше, чем разрешение системных часов, поток может находиться в спящем режиме меньше указанного промежутка времени.

Поэтому, если вы опрашиваете, вам нужно либо использовать гораздо большее время ожидания (может быть, 20 Мс, что обычно немного больше, чем частота тиков Windows), либо использовать более точный мультимедийный таймер.

Я бы порекомендовал использовать boost::asio::io_service. Мы получаем около 200 МБ / с многоадресного UDP-трафика при максимальной загрузке современного процессора. Это включает в себя полный протокол надежности и отправку данных в приложение. Узким местом в профилировании является обработка, а не повышение::asio receive. Код здесь

Похоже, что в большинстве случаев ваш вызов recvfrom не возвращает данные. Спать 1 мс не много. Вам следует подумать об увеличении времени ожидания (дешевое, но не лучшее решение) или, что лучше, подумать об использовании подхода, управляемого событиями. Используйте select() или Windows API для блокировки до тех пор, пока сокет не получит сигнал или не произойдет какое-либо другое интересующее вас событие, а затем вызовите recvfrom. Для этого вам может понадобиться изменить основной цикл вашей программы.

Другие вопросы по тегам