Я пишу серверное приложение для Windows 7 visual c ++, которое должно принимать дейтаграммы UDP с 3,6 МБ / с.
У меня есть основной поток, где recvfrom () получает данные. Сокет является неблокирующим и имеет приемный буфер 64 КБ. Если данные не были получены в сокете, поток выполняет режим ожидания (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;
}
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 sleep ():
Эта функция заставляет поток освобождать оставшееся время
нарезать и стать неуправляемым в течение интервала на основе значения
dwMilliseconds. Системные часы «тикают» с постоянной скоростью. Если
dwMilliseconds меньше, чем разрешение системных часов,
поток может спать меньше указанного промежутка времени.
Поэтому, если вы проводите опрос, вам нужно либо использовать гораздо большее время ожидания (может быть, 20 Мс, что, как правило, немного превышает частоту тиков Windows), либо использовать более точный мультимедийный таймер.
Похоже, что в большинстве случаев ваш вызов recvfrom не возвращает данные. Спать 1 мс не много. Вам следует подумать об увеличении времени ожидания (дешевое, но не лучшее решение) или, что лучше, подумать об использовании подхода, управляемого событиями. Используйте select () или Windows API для блокировки до тех пор, пока не будет сигнализирован сокет или не произойдет другое интересующее вас событие, а затем вызовите recvfrom. Для этого вам может понадобиться изменить основной цикл вашей программы.
Я бы порекомендовал использовать boost :: asio :: io_service. Мы получаем около 200 МБ / с многоадресного UDP-трафика при максимальной загрузке современного процессора. Это включает в себя полный протокол надежности и отправку данных в приложение. Узким местом в профилировании является обработка, а не повышение :: asio receive. Код здесь