metode tercepat (latensi rendah) untuk Komunikasi Proses Antar antara Java dan C / C ++

100

Saya memiliki aplikasi Java, menghubungkan melalui soket TCP ke "server" yang dikembangkan dalam C / C ++.

baik aplikasi & server berjalan pada mesin yang sama, kotak Solaris (tetapi kami mempertimbangkan untuk bermigrasi ke Linux pada akhirnya). Jenis data yang dipertukarkan adalah pesan sederhana (login, login ACK, lalu klien meminta sesuatu, server membalas). setiap pesan berukuran sekitar 300 byte.

Saat ini kami menggunakan Sockets, dan semuanya baik-baik saja, namun saya mencari cara yang lebih cepat untuk bertukar data (latensi lebih rendah), menggunakan metode IPC.

Saya telah meneliti internet dan menemukan referensi ke teknologi berikut:

  • Berbagi memori
  • pipa
  • antrian
  • serta apa yang disebut sebagai DMA (Direct Memory Access)

tetapi saya tidak dapat menemukan analisis yang tepat untuk performanya masing-masing, juga cara mengimplementasikannya di JAVA dan C / C ++ (sehingga mereka dapat berbicara satu sama lain), kecuali mungkin pipa yang dapat saya bayangkan bagaimana melakukannya.

Adakah yang bisa berkomentar tentang penampilan & kelayakan setiap metode dalam konteks ini? ada penunjuk / tautan ke informasi implementasi yang berguna?


EDIT / PERBARUI

mengikuti komentar & jawaban yang saya dapatkan di sini, saya menemukan info tentang Soket Domain Unix, yang tampaknya dibangun di atas pipa, dan akan menghemat seluruh tumpukan TCP. itu khusus platform, jadi saya berencana mengujinya dengan JNI atau juds atau junixsocket .

Langkah selanjutnya yang memungkinkan adalah implementasi langsung dari pipa, kemudian memori bersama, meskipun saya telah diperingatkan tentang tingkat kerumitan ekstra ...


terima kasih atas bantuan Anda

Bastien
sumber
7
Ini mungkin berlebihan dalam kasus Anda, tetapi pertimbangkan zeromq.org
jfs
itu menarik, namun idenya adalah menggunakan metode "generik" (seperti dalam OS yang disediakan atau disediakan bahasa) terlebih dahulu, itulah mengapa saya menyebutkan antrian & memori bersama.
Bastien
2
Lihat juga stackoverflow.com/questions/904492
MSalters
Jangan lupa file yang dipetakan atau hanya UDP.
10
UDP lebih lambat dari TCP ??? hmmm ... tolong
buktikan

Jawaban:

103

Baru saja menguji latensi dari Java pada Corei5 2.8GHz saya, hanya mengirim / menerima satu byte, 2 proses Java baru saja muncul, tanpa menetapkan inti CPU tertentu dengan set tugas:

TCP         - 25 microseconds
Named pipes - 15 microseconds

Sekarang secara eksplisit menentukan masker inti, seperti tasket 1 java Srv atau tasket 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 !!!!

begitu

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

Pada saat yang sama Thread.sleep (0) (yang ditunjukkan strace menyebabkan panggilan kernel Linux sched_yield () tunggal dijalankan) membutuhkan 0,3 mikrodetik - jadi pipa bernama yang dijadwalkan ke inti tunggal masih memiliki banyak overhead

Beberapa pengukuran memori bersama: 14 September 2009 - Solace Systems hari ini mengumumkan bahwa API Platform Perpesanan Terpadu dapat mencapai latensi rata-rata kurang dari 700 nanodetik menggunakan transportasi memori bersama. http://solacesystems.com/news/fastest-ipc-messaging/

PS - mencoba memori bersama keesokan harinya dalam bentuk file yang dipetakan memori, jika menunggu sibuk dapat diterima, kami dapat mengurangi latensi menjadi 0,3 mikrodetik untuk melewatkan satu byte dengan kode seperti ini:

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
}

Catatan: Thread.sleep (0) diperlukan agar 2 proses dapat melihat perubahan satu sama lain (saya belum tahu cara lain). Jika 2 proses dipaksa ke inti yang sama dengan kumpulan tugas, latensi menjadi 1,5 mikrodetik - itu adalah penundaan pengalihan konteks

PPS - dan 0,3 mikrodetik adalah angka yang bagus! Kode berikut membutuhkan tepat 0,1 mikrodetik, saat melakukan penggabungan string primitif saja:

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

PPPS - harap ini tidak terlalu banyak di luar topik, tetapi akhirnya saya mencoba mengganti Thread.sleep (0) dengan menambahkan variabel int volatil statis (JVM kebetulan membersihkan cache CPU saat melakukannya) dan diperoleh - rekam! - 72 nanodetik latensi komunikasi proses java-ke-java !

Namun, ketika dipaksa menggunakan CPU Core yang sama, JVM yang meningkatkan volatil tidak pernah menghasilkan kontrol satu sama lain, sehingga menghasilkan latensi tepat 10 milidetik - kuantum waktu Linux tampaknya 5ms ... Jadi ini harus digunakan hanya jika ada inti cadangan - kalau tidak tidur (0) lebih aman.

Andriy
sumber
terima kasih Andriy, sangat mempelajari informasi, dan kurang lebih cocok dengan pengukuran saya untuk TCP, jadi itu referensi yang bagus. Saya kira saya akan melihat ke pipa bernama.
Bastien
Jadi mengganti Thread (Tidur) dengan menambahkan int statis volatil hanya boleh dilakukan jika Anda dapat menyematkan proses ke inti yang berbeda? Juga, saya tidak menyadari Anda bisa melakukan ini? Saya pikir OS memutuskan?
mezamorphic
3
Coba LockSupport.parkNanos (1), harus melakukan hal yang sama.
Reccles
Sangat bagus. Anda dapat melakukan lebih baik (seperti dalam 5-7us RTT latency) untuk ping TCP. Lihat di sini: psy-lob-saw.blogspot.com/2012/12/…
Nitsan Wakart
1
Eksplorasi lebih lanjut menggunakan file yang dipetakan memori sebagai memori bersama untuk mendukung antrian IPC di Java: psy-lob-saw.blogspot.com/2013/04/lock-free-ipc-queue.html mencapai 135 juta pesan per detik. Juga lihat jawaban saya di bawah ini untuk studi perbandingan latensi dengan metode.
Nitsan Wakart
10

DMA adalah metode di mana perangkat keras dapat mengakses RAM fisik tanpa mengganggu CPU. Misalnya contoh umum adalah pengontrol harddisk yang dapat menyalin byte langsung dari disk ke RAM. Karena itu, ini tidak berlaku untuk IPC.

Memori dan pipa bersama keduanya didukung langsung oleh OS modern. Karena itu, mereka cukup cepat. Antrian biasanya merupakan abstraksi, misalnya diimplementasikan di atas soket, pipa dan / atau memori bersama. Ini mungkin terlihat seperti mekanisme yang lebih lambat, tetapi alternatifnya adalah Anda membuat abstraksi semacam itu.

MSalters
sumber
untuk DMA, mengapa kemudian saya bisa membaca banyak hal yang berkaitan dengan RDMA (sebagai Remote Direct Memory Access) yang akan berlaku di seluruh jaringan (terutama dengan InfiniBand) dan melakukan hal yang sama. Saya sebenarnya mencoba untuk mencapai yang setara TANPA jaringan (karena semuanya ada di kotak yang sama).
Bastien
Konsep RDMA adalah sama: menyalin byte melalui jaringan tanpa mengganggu CPU di kedua sisi. Itu masih tidak beroperasi pada tingkat proses.
MSalters
10

Pertanyaan itu telah diajukan beberapa waktu lalu, tetapi Anda mungkin tertarik dengan https://github.com/peter-lawrey/Java-Chronicle yang mendukung latensi umum 200 ns dan throughput 20 M pesan / detik. Ini menggunakan file yang dipetakan memori yang dibagikan antar proses (itu juga menyimpan data yang menjadikannya cara tercepat untuk menyimpan data)

Peter Lawrey
sumber
7

Berikut adalah proyek yang berisi uji kinerja untuk berbagai transpor IPC:

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

menopang
sumber
Ini tidak termasuk 'faktor Java', tapi memang terlihat menarik.
6

Jika Anda pernah mempertimbangkan untuk menggunakan akses asli (karena aplikasi Anda dan "server" berada di mesin yang sama), pertimbangkan JNA , karena JNA memiliki kode boilerplate yang lebih sedikit untuk Anda tangani.

bakkal
sumber
6

Kedatangan terlambat, tetapi ingin menunjukkan proyek open source yang didedikasikan untuk mengukur latensi ping menggunakan Java NIO.

Lebih lanjut dieksplorasi / dijelaskan dalam posting blog ini . Hasilnya adalah (RTT dalam 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

Ini sesuai dengan jawaban yang diterima. Kesalahan System.nanotime () (diperkirakan dengan mengukur apa-apa) diukur pada sekitar 40 nanos sehingga untuk IPC hasil aktual mungkin lebih rendah. Nikmati.

Nitsan Wakart
sumber
2

Saya tidak tahu banyak tentang komunikasi antar-proses asli, tetapi saya rasa Anda perlu berkomunikasi menggunakan kode asli, yang dapat Anda akses menggunakan mekanisme JNI. Jadi, dari Java Anda akan memanggil fungsi native yang berbicara dengan proses lain.

ikan
sumber
1

Di perusahaan saya sebelumnya, kami dulu bekerja dengan proyek ini, http://remotetea.sourceforge.net/ , sangat mudah dipahami dan diintegrasikan.

Seffi
sumber
0

Pernahkah Anda mempertimbangkan untuk membuka soket agar sambungan dapat digunakan kembali?

Thorbjørn Ravn Andersen
sumber
soket tetap terbuka. koneksi tetap hidup selama aplikasi berjalan (sekitar 7 jam). pesan dipertukarkan kurang lebih terus menerus (katakanlah sekitar 5 hingga 10 per detik). latensi saat ini sekitar 200 mikrodetik, tujuannya adalah untuk memotong 1 atau 2 kali lipat.
Bastien
Latensi 2 md? Ambisius. Apakah layak untuk menulis ulang C-stuff ke pustaka bersama yang dapat Anda antarmuka untuk menggunakan JNI?
Thorbjørn Ravn Andersen
2ms adalah 2000 mikrodetik, bukan 200. ini membuat 2ms kurang ambisius.
thewhiteambit
-1

Laporan bug Oracle pada kinerja JNI: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4096069

JNI adalah antarmuka yang lambat sehingga soket TCP Java adalah metode tercepat untuk pemberitahuan antar aplikasi, namun itu tidak berarti Anda harus mengirim muatan melalui soket. Gunakan LDMA untuk mentransfer payload, tetapi seperti yang telah ditunjukkan oleh pertanyaan sebelumnya , dukungan Java untuk pemetaan memori tidak ideal dan Anda akan ingin menerapkan pustaka JNI untuk menjalankan mmap.

Steve-o
sumber
3
Mengapa JNI lambat? Pertimbangkan cara kerja lapisan TCP tingkat rendah di Java, yang tidak ditulis dalam kode byte Java! (Misalnya, ini harus disalurkan melalui host asli.) Jadi, saya menolak pernyataan bahwa soket TCP Java lebih cepat daripada JNI. (JNI, bagaimanapun, bukan IPC.)
4
Satu panggilan JNI dikenakan biaya 9ns (pada Intel i5) jika Anda hanya menggunakan primitif. Jadi tidak terlalu lambat.
Martin Kersten