найшвидший (низька затримка) метод міжпроцесорної комунікації між Java та C / C ++


100

У мене є програма Java, яка підключається через сокет TCP до "сервера", розробленого в C / C ++.

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

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

Я досліджував мережу і придумав посилання на наступні технології:

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

але я не міг знайти належного аналізу їхніх виступів, а також як їх впровадити як у JAVA, так і в C / C ++ (щоб вони могли поговорити один з одним), за винятком хіба що труби, яку я міг би уявити, як це зробити.

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


РЕДАКТУВАННЯ / ОНОВЛЕННЯ

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

Наступними можливими кроками буде пряма реалізація труб, а потім спільна пам'ять, хоча мене попередили про додатковий рівень складності ...


спасибі за вашу допомогу


7
У вашому випадку це може бути надмірним, але врахуйте zeromq.org
jfs

це цікаво, однак ідея полягала б у тому, щоб спочатку використовувати "загальні" (як у ОС чи надані мовою) методи, тому я згадав черги та спільну пам'ять.
Бастієн

2
Дивіться також stackoverflow.com/questions/904492
MSalters

Не забудьте зіставлені файли або просто UDP.

10
UDP повільніше, ніж TCP ??? хммм ... доказ будь ласка
Boppity Bop

Відповіді:


103

Щойно перевірена затримка від Java на моєму Corei5 2,8 ГГц, лише одне байтове надсилання / отримання, 2 Java-процеси просто породилися, не призначаючи конкретні ядра процесора з набором завдань:

TCP         - 25 microseconds
Named pipes - 15 microseconds

Тепер чітко вказані основні маски, як набір завдань 1 java Srv або набір завдань 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 Unified Messaging Platform може досягти середньої затримки менше 700 наносекунд за допомогою спільного транспорту пам’яті. http://solacesystems.com/news/fastest-ipc-messaging/

PS - спробувала спільна пам'ять наступного дня у вигляді файлів, відображених у пам'яті, якщо зайняте очікування прийнятне, ми можемо зменшити затримку до 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) потрібен, щоб два процеси побачили зміни один одного (я ще не знаю іншого способу). Якщо два процеси змушені до того ж ядра з набором завдань, затримка стає 1,5 мікросекунди - це затримка контекстного перемикання

PPS - і 0,3 мікросекунди - це хороше число! Наступний код займає рівно 0,1 мікросекунди, виконуючи лише примітивне з'єднання рядків:

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

PPPS - сподіваюся, це не надто поза темою, але, нарешті, я спробував замінити Thread.sleep (0) збільшенням статичної летючої змінної int (JVM трапляється, щоб промити кеші процесора при цьому) і отримав - записуйте! - затримка 72-наносекундних затримок зв’язку між процесом java-java !

Однак, коли його змушують використовувати той самий процесорний ядро, JVM, що змінюються, не змінюються, ніколи не піддаються контролю один одному, тим самим виробляючи рівно 10 мілісекундних затримок - квант часу Linux, здається, становить 5 мс ... Тому це слід використовувати лише за наявності запасного ядра - інакше сон (0) безпечніший.


дякую Андрію, дуже інформаційне дослідження, і воно відповідає більш-менш моїм вимірам для TCP, тому це хороша довідка. Я думаю, я загляну в названі труби.
Бастієн

Отже, замінювати Нитку (Сон) на приріст летючого статичного int слід робити лише в тому випадку, якщо ви можете прив’язати процес до різних ядер? Крім того, я не розумів, що ти можеш це зробити? Я думав, що ОС вирішує?
мезаморфний

3
Спробуйте LockSupport.parkNanos (1), слід робити те саме.
згадує

Дуже хороша. Ти можеш краще (як у затримці RTT 5-7us) для TCP-пінгу. Дивіться тут: psy-lob-saw.blogspot.com/2012/12/…
Nitsan Wakart

1
Подальше дослідження використання картографічного файлу пам'яті як спільної пам'яті для підтримки черги IPC на Java: psy-lob-saw.blogspot.com/2013/04/lock-free-ipc-queue.html, досягаючи 135M повідомлень в секунду. Також дивіться мою відповідь нижче для порівняльного вивчення затримок за методом.
Nitsan Wakart

10

DMA - метод, за допомогою якого апаратні пристрої можуть отримувати доступ до фізичної оперативної пам’яті, не припиняючи процесор. Наприклад, поширений приклад - контролер жорсткого диска, який може копіювати байти прямо з диска в оперативну пам'ять. Як такий, він не застосовується до IPC.

Спільна пам'ять та труби підтримуються безпосередньо сучасними ОС. Як такі, вони досить швидкі. Черги, як правило, є абстракціями, наприклад, реалізованими поверх розеток, труб та / або спільної пам'яті. Це може виглядати як повільніший механізм, але альтернативою є те, що ви створюєте таку абстракцію.


для DMA, чому саме тоді я можу прочитати багато речей, пов'язаних з RDMA (як віддалений прямий доступ до пам'яті), які застосовуватимуться в мережі (особливо з InfiniBand), і робити це те саме. Я насправді намагаюся досягти еквівалента БЕЗ мережі (оскільки всі знаходяться в одній коробці).
Бастієн

RDMA - це те саме поняття: копіювання байтів через мережу, не перериваючи процесори на будь-якій стороні. Він все ще не працює на рівні процесу.
MSalters

10

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



6

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


6

Запізнення, але хотілося вказати на проект з відкритим кодом, присвячений вимірюванню затримки пінг за допомогою Java NIO.

Далі вивчено / пояснено в цій публікації блогу . Результати (RTT in nanos):

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 фактичний результат може бути нижчим. Насолоджуйтесь.


2

Я мало знаю про внутрішнє міжпроцесорне спілкування, але я б припустив, що вам потрібно спілкуватися за допомогою нативного коду, до якого ви можете отримати доступ за допомогою механізмів JNI. Отже, з Java ви б назвали натисну функцію, яка спілкується з іншим процесом.



0

Чи обдумали ви тримати розетки відкритими, щоб з'єднання можна було повторно використовувати?


розетки залишаються відкритими. підключення живе протягом усього часу роботи програми (близько 7 годин). повідомлення обмінюються більш-менш постійно (скажімо, від 5 до 10 за секунду). Затримка струму становить близько 200 мікросекунд, мета - поголити 1 або 2 порядки.
Бастієн

Затримка в 2 мс? Амбітний. Чи можливо зробити перезапис C-матеріалів у спільну бібліотеку, яку ви можете інтерфейсувати за допомогою JNI?
Thorbjørn Ravn Andersen

2 мс - 2000 мікросекунд, а не 200. Це робить 2 мс набагато менш амбітними.
thewhiteambit

-1

Звіт про помилку Oracle про продуктивність JNI: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4096069

JNI - це повільний інтерфейс, і тому Java TCP-розетки - це найшвидший метод оповіщення між додатками, однак це не означає, що вам потрібно надсилати корисну навантаження через сокет. Використовуйте LDMA для передачі корисного навантаження, але, як вказували попередні запитання , підтримка Java для відображення пам'яті не є ідеальною, і ви так хочете реалізувати бібліотеку JNI для запуску mmap.


3
Чому JNI повільний? Поміркуйте, як працює низькорівневий рівень TCP в Java, він не написаний в байт-коді Java! (Наприклад, це має переходити через основний хост.) Таким чином, я відкидаю твердження, що сокети Java TCP швидші, ніж JNI. (JNI, однак, не IPC.)

4
Один дзвінок JNI обійдеться вам у 9нс (на Intel i5), якщо ви використовуєте лише примітиви. Так це не так повільно.
Мартін Керстен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.