Самый быстрый (с низкой задержкой) метод для Inter Process Communication между Java и C/С++

У меня есть приложение Java, подключающееся через TCP-сокет к "серверу", разработанному на C/С++.

оба приложения и сервер работают на одном компьютере, в поле Solaris (но мы планируем перейти на Linux в конечном итоге). тип обмена данными - это простые сообщения (логин, логин ACK, затем клиент запрашивает что-то, ответы сервера). каждое сообщение длиной около 300 байт.

В настоящее время мы используем Sockets, и все в порядке, однако я ищу более быстрый способ обмена данными (более низкая латентность) с использованием методов IPC.

Я изучал сеть и придумывал ссылки на следующие технологии:

  • общая память
  • трубы
  • очереди
  • а также то, что называется DMA (прямой доступ к памяти)

но я не смог найти правильный анализ их соответствующих характеристик, ни как реализовать их как в JAVA, так и в C/С++ (чтобы они могли разговаривать друг с другом), за исключением, может быть, труб, которые я мог себе представить, как это сделать.

может ли кто-нибудь прокомментировать возможности и возможности каждого метода в этом контексте? любой указатель/ссылку на полезную информацию о реализации?


EDIT/UPDATE

следуя комментариям и ответам, которые я получил здесь, я нашел информацию о Unix Domain Sockets, которые, похоже, были построены только по трубам и сэкономили бы мне весь стек TCP. это определенная платформа, поэтому я планирую тестировать ее с помощью JNI или juds или junixsocket.

следующими возможными шагами будут прямая реализация труб, а затем разделяемая память, хотя я был предупрежден о дополнительном уровне сложности...


спасибо за помощь

Ответ 1

Просто протестированная латентность от Java на моем Corei5 2,8 ГГц, только один байт отправил/получил, 2 Java-процессы просто порождены, не назначая конкретные ядра процессора с набором задач:

TCP         - 25 microseconds
Named pipes - 15 microseconds

Теперь явно укажем основные маски, например taskset 1 java Srv или taskset 2 java Cli:

TCP, same cores:                      30 microseconds
TCP, explicit different cores:        22 microseconds
Named pipes, same core:               4-5 microseconds !!!!
Named pipes, taskset different cores: 7-8 microseconds !!!!

так

TCP overhead is visible
scheduling overhead (or core caches?) is also the culprit

В то же время Thread.sleep(0) (который, как показывает strace, вызывает один вызов ядра sched_yield() Linux, который должен быть выполнен) принимает 0,3 микросекунды - так называемые каналы, запланированные для одного ядра, все еще имеют большие накладные расходы

Некоторое разделение памяти:  14 сентября 2009 г. - Solace Systems объявила сегодня, что ее API платформы единой системы обмена сообщениями может достичь средней латентности менее 700 наносекунд, используя общий обмен памяти.  http://solacesystems.com/news/fastest-ipc-messaging/

P.S. - на следующий день попытался использовать общую память в виде файлов с отображением памяти, если ожидание ожидания приемлемо, мы можем уменьшить задержку до 0,3 микросекунды для передачи одного байта с кодом следующим образом:

MappedByteBuffer mem =
  new RandomAccessFile("/tmp/mapped.txt", "rw").getChannel()
  .map(FileChannel.MapMode.READ_WRITE, 0, 1);

while(true){
  while(mem.get(0)!=5) Thread.sleep(0); // waiting for client request
  mem.put(0, (byte)10); // sending the reply
}

Примечания: Thread.sleep(0) необходим, чтобы 2 процесса могли видеть друг друга. (Я еще не знаю другого пути). Если 2 процесса принудительно привязаны к одному ядру с набором задач, время ожидания составляет 1,5 микросекунды, - что задержка переключения контекста

P.P.S - и 0,3 микросекунды - хорошее число! Следующий код занимает ровно 0,1 микросекунды при выполнении только примитивной конкатенации строк:

int j=123456789;
String ret = "my-record-key-" + j  + "-in-db";

P.P.P.S - надеюсь, что это не слишком много вне темы, но, наконец, я попробовал заменить Thread.sleep(0) с добавлением статической изменчивой переменной int (JVM случайно пытается сбросить кеши процессора при этом) и получен - запись! - 72-секундная латентная обработка сообщений java-to-java!

Однако, когда они принудительно подключены к одному и тому же процессору Core, VLAN с изменчивым ростом никогда не будут работать друг с другом, создавая ровно 10 миллисекундную задержку - квант времени Linux составляет 5 мс... Поэтому это следует использовать, только если есть запасное ядро ​​- в противном случае сон (0) безопаснее.

Ответ 2

Вопрос был задан некоторое время назад, но вас может заинтересовать https://github.com/peter-lawrey/Java-Chronicle, который поддерживает типичные задержки 200 нс и пропускную способность 20 M сообщений/секунд. Он использует файлы с отображением памяти, совместно используемые между процессами (он также сохраняет данные, которые делают его самым быстрым способом сохранения данных)

Ответ 3

DMA - это метод, с помощью которого аппаратные устройства могут обращаться к физической памяти без прерывания процессора. Например. общим примером является контроллер жесткого диска, который может копировать байты прямо с диска на ОЗУ. Поэтому он не применим к IPC.

Общая память и каналы поддерживаются непосредственно современными ОС. Таким образом, они довольно быстро. Очереди обычно представляют собой абстракции, например. реализован поверх сокетов, труб и/или разделяемой памяти. Это может выглядеть как более медленный механизм, но альтернативой является то, что вы создаете такую ​​абстракцию.

Ответ 4

Здесь представлен проект, содержащий тесты производительности для различных транспортов IPC:

http://github.com/rigtorp/ipc-bench

Ответ 5

Если вы когда-либо рассматриваете использование собственного доступа (поскольку ваше приложение и "сервер" находятся на одной машине), рассмотрите JNA, у него меньше кода шаблонов для вас.

Ответ 6

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

Дальнейшее исследование/объяснение в этом сообщении в блоге. Результаты (RTT в наносах):

Implementation, Min,   50%,   90%,   99%,   99.9%, 99.99%,Max
IPC busy-spin,  89,    127,   168,   3326,  6501,  11555, 25131
UDP busy-spin,  4597,  5224,  5391,  5958,  8466,  10918, 18396
TCP busy-spin,  6244,  6784,  7475,  8697,  11070, 16791, 27265
TCP select-now, 8858,  9617,  9845,  12173, 13845, 19417, 26171
TCP block,      10696, 13103, 13299, 14428, 15629, 20373, 32149
TCP select,     13425, 15426, 15743, 18035, 20719, 24793, 37877

Это соответствует принятому ответу. Ошибка System.nanotime() (оцениваемая путем измерения нуля) измеряется на уровне около 40 нанометров, поэтому для IPC фактический результат может быть ниже. Наслаждайтесь.

Ответ 7

Я не очень разбираюсь в родной межпроцессной коммуникации, но я бы предположил, что вам нужно обмениваться данными с помощью собственного кода, к которому вы можете получить доступ, используя механизмы JNI. Итак, с Java вы бы назвали родную функцию, которая говорит о другом процессе.

Ответ 8

В моей прежней компании мы работали с этим проектом, http://remotetea.sourceforge.net/, очень легко понять и интегрировать.

Ответ 9

Считаете ли вы, что розетки открыты, поэтому соединения можно использовать повторно?

Ответ 10

Отчет об ошибке Oracle по производительности JNI: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4096069

JNI - медленный интерфейс, и поэтому сокеты Java TCP являются самым быстрым способом уведомления между приложениями, однако это не означает, что вы должны отправлять полезную нагрузку через сокет. Используйте LDMA для передачи полезной нагрузки, но как предыдущие вопросы отметили, что поддержка Java для сопоставления памяти не идеальна, и вы так захотите реализовать библиотеку JNI для запуска mmap.