Высокая загрузка ЦП в потоках UDP DatagramSocket в Java
Я использую многопоточное приложение на Java-сервере, которое, помимо прочего, получает UDP-пакеты от 3 разных многоадресных источников (портов) в 3 разных потоках.
Он работает на недавнем RedHat с двумя сокетами (всего 8 ядер (4 x 2 процессора), без гиперпоточности).
Команда "top" показывает использование процессора на уровне 250~300%. shift-H показывает 2 потока при использовании 99%, 1 при 70%. Быстрый анализ потока jstack показывает, что эти потоки соответствуют моим потокам обработки UDP.
Я немного удивлен уровнем использования ЦП, учитывая скорость ЦП и частоту сообщений UDP (около 300 мс / с при полезной нагрузке около 250 байт), и я исследую это. Интересно отметить, что третий поток (соответствующий более низкому использованию процессора) имеет более низкую скорость передачи данных (50~100 мсг / с)
Я включил некоторый отладочный код, чтобы измерить, на что тратится больше всего времени, и он, кажется, находится в методе "receive()" DatagramSocket:
_running = true;
_buf = new byte[300];
_packet = new DatagramPacket(_buf, _buf.length);
while(_running) {
try {
long t0 = System.nanoTime();
_inSocket.receive(_packet);
long t1 = System.nanoTime();
this.handle(_packet);
long t2 = System.nanoTime();
long waitingAndReceiveTime = t1-t0;
long handleTime = t2-t1;
_logger.info("{} : {} : update : {} : {}", t1, _port, waitingAndReceiveTime, handleTime);
}
catch(Exception e) {
_logger.error("Exception while receiving multicast packet", e);
}
}
handleTime в среднем составляет 4000 нс, что очень быстро и не может нести ответственность за использование процессора. waitAndReceiveTime намного выше, примерно от 30 000 нс до нескольких мс. Я понимаю, что метод блокирует, поэтому время включает в себя как блокировку времени, так и время получения.
У меня есть несколько вопросов:
- Прав ли я подозревать что-то странное?
- Я думаю, что "receive()" блокирует, он не должен "тратить" циклы процессора, поэтому ожидающая часть не должна отвечать за высокую загрузку процессора, верно?
- Будет ли способ разделить измерение блокировки времени и времени получения дейтаграммы в методе приема?
- что может быть причиной такой высокой загрузки процессора?
РЕДАКТИРОВАТЬ: я играл с параметрами объединения прерываний, поместив rx-usecs в 0 и rx-кадров в 10. Теперь я вижу следующее:
- Сообщения UPD действительно появляются в группах по 10: для каждой группы первое сообщение имеет длительный waitAndReceiveTime (>= 1 мс), а следующие 9 waitAndReceiveTime значительно короче (~2000 нс). (handleTime такой же)
- Использование процессора снижено! уменьшается до 55% для 2 первых потоков.
до сих пор не знаю, как решить эту проблему
2 ответа
НЕ ДЕЙСТВИТЕЛЬНО ОТВЕТИТЬ, НО:
Я могу вас заверить, это не код Java. Я сделал многопоточный UDP-сервер на Python, и он делает то же самое, загрузка процессора увеличивается до 100% в течение 3-4 секунд. Я предполагаю, что это действительно имеет отношение к самому UDP, так как я также создал многопоточный TCP-сервер, и он едва достигает 10% загрузки ЦП.
Вот код:
import socket
from _thread import*
import threading
import time
def threaded(s,serverIP,serverPort):
while True:
try:
d = s.recvfrom(128)
data = d[0]
addr = d[1]
message= str(data)
if (message== "b'1'"):
time.sleep(5)
s.sendto(str.encode(message) , addr)
print(message)
except:
break
s.close()
def Main():
serverPort = 11000
serverIP= "127.0.0.1"
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((serverIP, serverPort))
while True:
start_new_thread(threaded, (s,serverIP,serverPort))
s.close)
if __name__ == '__main__':
Main()
Замечания:
Если вы нашли ответ, пожалуйста, скажите мне. Удачи.
Я бил себя по голове об этом в течение 11 лет. Я написал приложение для домашней автоматизации, которое использует UDP для связи с отдельными комнатными контроллерами. Чтобы получать дейтаграммы UDP, независимо от того, что я делаю, тратится все ядро процессора (100% использование процессора). Я пошел и провел полное исследование профилирования, и похоже, что именно так прием UDP реализован в Java. С тех пор я обновлял Java неисчислимое количество раз (прошло буквально 11 лет), а недавно также перенес ее с ПК на Raspberry Pi 4. Даже на Raspberry он по-прежнему использует одно целое ядро только для получения UDP-пакетов ... Это может быть просто ошибка, которая оставалась незамеченной все эти годы, или, может быть, есть причина, по которой вы не можете более эффективно получать UDP-пакеты. Я не являюсь надлежащим разработчиком, поэтому я бы не осмелился опубликовать правильный отчет об ошибке в Oracle.