самый быстрый (с низкой задержкой) метод для межпроцессного взаимодействия между Java и C / C ++ - PullRequest
98 голосов
/ 14 апреля 2010

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

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

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

Я исследовал сеть и нашел ссылки на следующие технологии:

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

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

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


РЕДАКТИРОВАТЬ / ОБНОВИТЬ

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

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


спасибо за вашу помощь

Ответы [ 10 ]

99 голосов
/ 20 июня 2011

Только что протестировал задержку от 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) на увеличение статической переменной volatile int (при этом JVM сбрасывает кэш процессора), и получил - запись! - 72 наносекунды латентное взаимодействие Java-процесса с Java !

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

10 голосов
/ 15 июля 2012

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

10 голосов
/ 14 апреля 2010

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

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

7 голосов
/ 15 апреля 2010

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

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

6 голосов
/ 03 июля 2013

Позднее прибытие, но хотелось указать на проект с открытым исходным кодом , предназначенный для измерения задержки пинга с помощью 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 фактический результат может быть ниже. Наслаждайтесь.

6 голосов
/ 14 апреля 2010

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

2 голосов
/ 14 апреля 2010

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

1 голос
/ 14 апреля 2010

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

0 голосов
/ 30 ноября 2010

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

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

0 голосов
/ 15 апреля 2010

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

...