Высокая загрузка ЦП в потоках 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 нс до нескольких мс. Я понимаю, что метод блокирует, поэтому время включает в себя как блокировку времени, так и время получения.

У меня есть несколько вопросов:

  1. Прав ли я подозревать что-то странное?
  2. Я думаю, что "receive()" блокирует, он не должен "тратить" циклы процессора, поэтому ожидающая часть не должна отвечать за высокую загрузку процессора, верно?
  3. Будет ли способ разделить измерение блокировки времени и времени получения дейтаграммы в методе приема?
  4. что может быть причиной такой высокой загрузки процессора?

РЕДАКТИРОВАТЬ: я играл с параметрами объединения прерываний, поместив 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.

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