Задержка сокета Java TCP/IP - застрял на 50 мкс (микросекунды)? (используется для Java IPC)

Мы провели профилирование и профилирование нашего приложения, чтобы как можно больше уменьшить задержку. Наше приложение состоит из 3 отдельных процессов Java, работающих на одном сервере, которые передают сообщения друг другу через сокеты TCP/IP.

Мы сократили время обработки в первом компоненте до 25 мкс, но мы видим, что запись сокета TCP/IP (на localhost) в следующий компонент неизменно занимает около 50 мкс. Мы видим еще одно аномальное поведение, заключающееся в том, что компонент, который принимает соединение, может писать быстрее (т.е. < 50 мкс). Прямо сейчас все компоненты работают < 100 мкс, за исключением связи сокета.

Не будучи экспертом по TCP/IP, я не знаю, что можно сделать, чтобы ускорить это. Разве Unix-доменные сокеты будут быстрее? MemoryMappedFiles? какие другие механизмы могли бы быть более быстрым способом передачи данных из одного Java-процесса в другой?

ОБНОВЛЕНИЕ 6/21/2011 Мы создали 2 тестовых приложения, один на Java и один на С++, чтобы более точно сравнивать TCP/IP и сравнивать. Приложение Java использовало NIO (режим блокировки), а С++ - библиотеку Boost ASIO tcp. Результаты были более или менее эквивалентными: приложение С++ примерно на 4 мкс быстрее, чем Java (но в одном из тестов Java beat С++). Кроме того, обе версии проявили большую изменчивость в течение времени на сообщение.

Я думаю, мы согласны с основным заключением, что реализация общей памяти будет самой быстрой. (Хотя мы также хотели бы оценить продукт Informatica, если он соответствует бюджету.)

Ответ 1

Если использовать собственные библиотеки через JNI, это вариант, я бы рассмотрел возможность внедрения IPC как обычно (поиск IPC, mmap, shm_open и т.д..).

Там много накладных расходов связано с использованием JNI, но, по крайней мере, это немного меньше, чем полные системные вызовы, необходимые для чего-либо с сокетами или трубами. Вероятнее всего, вы сможете перейти на одну задержку в 3 микросекунды, используя реализацию IPC для IPO опроса с использованием JNI. (Обязательно используйте параметр -Xcomp JVM или отрегулируйте порог компиляции, иначе ваши первые 10 000 образцов будут ужасными. Это имеет большое значение.)

Я немного удивлен, что запись в сокет TCP занимает 50 микросекунд - большинство операционных систем в некоторой степени оптимизируют петлю TCP. Solaris действительно неплохо справляется с чем-то вроде TCP Fusion. И если была какая-либо оптимизация для обратной связи, она обычно была для TCP. UDP, как правило, игнорируется, поэтому я бы не стал с этим беспокоиться. Я также не буду беспокоиться о трубах (stdin/stdout или ваши собственные именованные каналы и т.д.), Потому что они будут еще медленнее.

И, как правило, большая часть задержек, которые вы видите, скорее всего, поступает из сигнализации - либо ожидает выбора IO-селектора, например select() в случае сокетов, либо ждет семафора, либо ждет чего-то. Если вы хотите, чтобы минимальная латентность была возможной, вам нужно будет записать ядро, сидящее в опросе с плотным циклом для новых данных.

Конечно, всегда существует коммерческий готовый маршрут, который, я знаю, наверняка поможет решить вашу проблему в торопитесь - но, конечно, это стоит денег. И в интересах полного раскрытия: я работаю в Informatica в своем программном обеспечении для обмена сообщениями с низкой задержкой. (И мое честное мнение, как инженер, заключается в том, что это довольно фантастическое программное обеспечение - безусловно, стоит проверить этот проект.)

Ответ 2

"Книга O'Reilly о NIO (Java NIO, стр. 84), кажется, смутно сохраняется ли отображение памяти в памяти. Может быть, это просто говорит что, как и в другой памяти, если у вас закончилось физическое, это будет заменено обратно на диск, но в противном случае нет?"

Linux. вызов mmap() выделяет страницы в области кеша страницы ОС (которые периодически очищаются на диск и могут быть выведены на основе Clock-PRO, который является приближением алгоритма LRU?). Таким образом, ответ на ваш вопрос - да. Буфер с отображением памяти может быть выведен (теоретически) из памяти, если только он не установлен (mlock()). Это теоретически. На практике я думаю, что вряд ли возможно, если ваша система не будет меняться. В этом случае первыми жертвами являются буферы страниц.

Ответ 4

MemoryMappedFiles не является жизнеспособным решением для IPC с малой задержкой - если обновленный сегмент памяти обновляется, он, в конце концов, будет синхронизирован с диском, тем самым внедряя непредсказуемую задержку, которая измеряется как минимум в миллисекундах. При низкой задержке можно попробовать комбинации либо очередей сообщений с общей памятью + (уведомления), либо разделяемой памяти + семафоров. Это работает во всех Unix-системах, в особенности версии System V (не POSIX), но если вы запускаете приложение в Linux, вы довольно безопасны с POSIX IPC (большинство функций доступны в ядре 2.6). Да, для этого вам понадобится JNI.

UPD: Я забыл, что это JVM - JVM IPC, и у нас уже есть GC, которые мы не можем полностью контролировать, поэтому введение дополнительных пауз в несколько ms из-за флэш файлов на флеш-диске может быть приемлемым.

Ответ 5

Отъезд https://github.com/pcdv/jocket

Это замена с низкой задержкой для локальных сокетов Java, использующих разделяемую память.

Задержка RTT между двумя процессами значительно ниже 1us на современном процессоре.