Java ve C / C ++ arasında İşlemler Arası İletişim için en hızlı (düşük gecikme) yöntem


100

C / C ++ ile geliştirilmiş bir "sunucuya" TCP soketi üzerinden bağlanan bir Java uygulamam var.

hem uygulama hem de sunucu aynı makinede, bir Solaris kutusunda çalışıyor (ancak sonunda Linux'a geçmeyi düşünüyoruz). değiş tokuş edilen veri türü basit mesajlardır (oturum açma, ACK oturum açma, sonra müşteri bir şey sorar, sunucu yanıtları). her mesaj yaklaşık 300 bayt uzunluğundadır.

Şu anda Soketler kullanıyoruz ve her şey yolunda, ancak IPC yöntemlerini kullanarak veri alışverişi yapmanın daha hızlı bir yolunu (daha düşük gecikme süresi) arıyorum.

Ağı araştırıyorum ve aşağıdaki teknolojilere referanslar buldum:

  • paylaşılan hafıza
  • borular
  • kuyruklar
  • yanı sıra DMA (Doğrudan Bellek Erişimi) olarak adlandırılan

ancak performanslarının doğru analizini bulamadım, onları hem JAVA'da hem de C / C ++ 'da nasıl uygulayacağımı (böylece birbirleriyle konuşabilirler), belki de nasıl yapılacağını hayal edebildiğim borular dışında.

Bu bağlamda her yöntemin performansı ve fizibilitesi hakkında yorum yapan var mı? kullanışlı uygulama bilgilerine herhangi bir işaretçi / bağlantı var mı?


DÜZENLEME / GÜNCELLEME

Burada aldığım yorum ve cevapları takiben, Unix Etki Alanı Soketleri hakkında, boruların hemen üzerine inşa edilmiş gibi görünen ve bana tüm TCP yığınını kurtaracak bilgiler buldum. platforma özeldir, bu yüzden onu JNI veya juds veya junixsocket ile test etmeyi planlıyorum .

Sonraki olası adımlar, boruların doğrudan uygulanması, ardından paylaşılan bellek olabilir, ancak ekstra karmaşıklık düzeyi konusunda uyarıldım ...


yardımınız için teşekkürler


7
Sizin durumunuzda aşırı olabilir ama zeromq.org'u
jfs

bu ilginç, ancak fikir önce "genel" (işletim sistemi tarafından sağlanan veya dil tarafından sağlanan) yöntemleri kullanmak olacaktır, bu yüzden kuyruklardan ve paylaşılan bellekten bahsetmiştim.
Bastien


Eşlenmiş dosyaları veya sadece UDP'yi unutmayın.

10
UDP, TCP'den daha yavaş ??? hmmm ... kanıt lütfen
Boppity Bop

Yanıtlar:


103

Corei5 2.8GHz'de Java'nın gecikmesini test etti, yalnızca tek bayt gönderme / alma, 2 Java işlemi, görev setine belirli CPU çekirdekleri atamadan ortaya çıktı:

TCP         - 25 microseconds
Named pipes - 15 microseconds

Görev seti 1 java Srv veya tasket 2 java Cli gibi temel maskeleri artık açıkça belirtiyoruz :

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 !!!!

yani

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

Aynı zamanda Thread.sleep (0) (strace'in gösterdiği gibi, tek bir sched_yield () Linux çekirdeği çağrısının yürütülmesine neden olur) 0,3 mikrosaniye sürer - bu nedenle tek çekirdekte programlanan adlandırılmış kanalların hala çok fazla ek yükü vardır

Bazı paylaşılan bellek ölçümleri: 14 Eylül 2009 - Solace Systems, Birleşik Mesajlaşma Platformu API'sinin paylaşılan bir bellek aktarımını kullanarak ortalama 700 nanosaniyeden daha az bir gecikme süresi sağlayabileceğini bugün duyurdu. http://solacesystems.com/news/fastest-ipc-messaging/

PS - Ertesi gün paylaşılan bellek, bellek eşlemeli dosyalar biçiminde denendi, meşgul bekleme kabul edilebilirse, aşağıdaki gibi bir kodla tek bir baytı geçirmek için gecikmeyi 0,3 mikrosaniyeye düşürebiliriz:

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
}

Notlar: Thread.sleep (0) gereklidir, böylece 2 işlem birbirinin değişikliklerini görebilir (henüz başka bir yol bilmiyorum). Görev kümesiyle aynı çekirdeğe zorlanan 2 işlem varsa, gecikme 1,5 mikrosaniye olur - bu bir bağlam değiştirme gecikmesidir

PPS - ve 0,3 mikrosaniye iyi bir sayıdır! Aşağıdaki kod, yalnızca ilkel bir dize birleştirme yaparken tam olarak 0,1 mikrosaniye sürer:

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

PPPS - umarım bu konu dışı değildir, ama sonunda Thread.sleep (0) 'ı statik bir uçucu int değişkenini artırarak değiştirmeyi denedim (JVM bunu yaparken CPU önbelleklerini temizler) ve elde ettim - kaydedin! - 72 nanosaniye gecikme java-java süreç iletişimi !

Bununla birlikte, aynı CPU Çekirdeğine zorlandıklarında, uçucu artan JVM'ler asla birbirlerine kontrol sağlamaz, böylece tam olarak 10 milisaniye gecikme üretir - Linux zaman kuantumu 5ms gibi görünüyor ... Bu nedenle, bu yalnızca yedek bir çekirdek varsa kullanılmalıdır - aksi takdirde uyku (0) daha güvenlidir.


teşekkürler Andriy, çok bilgi çalışması ve TCP ölçümlerimle aşağı yukarı eşleşiyor, bu yüzden bu iyi bir referans. Sanırım adlandırılmış borulara bakacağım.
Bastien

Öyleyse, İpliği (Uyku), uçucu statik int'i artırarak değiştirmek, yalnızca bir işlemi farklı çekirdeklere sabitleyebiliyorsanız yapılmalıdır? Ayrıca, bunu yapabileceğinin farkında değildim? İşletim sisteminin karar vereceğini sanıyordum?
mezamorfik

3
LockSupport.parkNanos'u (1) deneyin, aynı şeyi yapmalısınız.
31'de yanıt veriyor

Çok hoş. TCP pingi için daha iyi (5-7us RTT gecikmesinde olduğu gibi) yapabilirsiniz. Buraya bakın: psy-lob-saw.blogspot.com/2012/12/…
Nitsan Wakart

1
Java'da IPC kuyruğunu desteklemek için paylaşılan bellek olarak bellek eşlemeli dosyanın kullanımıyla ilgili daha fazla araştırma: psy-lob-saw.blogspot.com/2013/04/lock-free-ipc-queue.html saniyede 135 milyon mesaj elde etme. Ayrıca, yönteme göre gecikmenin karşılaştırmalı çalışması için aşağıdaki cevabıma bakın.
Nitsan Wakart

10

DMA, donanım cihazlarının CPU'yu kesintiye uğratmadan fiziksel RAM'e erişebildiği bir yöntemdir. Örneğin, yaygın bir örnek, baytları doğrudan diskten RAM'e kopyalayabilen bir sabit disk denetleyicisidir. Bu nedenle IPC için geçerli değildir.

Paylaşılan bellek ve borular, modern işletim sistemleri tarafından doğrudan desteklenir. Bu nedenle, oldukça hızlılar. Kuyruklar tipik olarak soyutlamalardır, örneğin soketler, borular ve / veya paylaşılan bellek üzerine uygulanır. Bu daha yavaş bir mekanizma gibi görünebilir, ancak alternatif olmasıdır Eğer böyle bir soyutlama oluşturun.


DMA için, neden RDMA ile ilgili (Uzaktan Doğrudan Bellek Erişimi olarak) ağ genelinde (özellikle InfiniBand ile) geçerli olan birçok şeyi okuyabiliyorum ve aynı şeyi yapabiliyorum. Aslında ağ OLMADAN eşdeğerini elde etmeye çalışıyorum (hepsi aynı kutuda olduğu gibi).
Bastien

RDMA da aynı konsepttir: Bir ağda her iki taraftaki CPU'ları kesmeden baytları kopyalamak. Hâlâ süreç düzeyinde çalışmıyor.
MSalters

10

Soru bir süre önce sorulmuştu, ancak 200 ns'lik tipik gecikmeleri ve 20 M mesaj / saniye aktarım hızlarını destekleyen https://github.com/peter-lawrey/Java-Chronicle ilginizi çekebilir . İşlemler arasında paylaşılan bellek eşlemeli dosyaları kullanır (aynı zamanda verileri kalıcı hale getirerek verileri kalıcı hale getirmenin en hızlı yolunu yapar)



6

Yerel erişimi kullanmayı düşünürseniz (hem uygulamanız hem de "sunucu" aynı makinede olduğundan), JNA'yı düşünün, uğraşmanız gereken daha az standart kod vardır.


6

Geç geldi, ancak Java NIO kullanarak ping gecikmesini ölçmeye adanmış açık kaynaklı bir projeye işaret etmek istedi .

Bu blog gönderisinde daha ayrıntılı incelendi / açıklandı . Sonuçlar (nano cinsinden 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

Bu, kabul edilen cevabın çizgisindedir. System.nanotime () hatası (hiçbir şey ölçülerek tahmin edilir) yaklaşık 40 nanos'ta ölçülür, bu nedenle IPC için gerçek sonuç daha düşük olabilir. Zevk almak.


2

Yerel süreçler arası iletişim hakkında pek bir şey bilmiyorum, ancak JNI mekanizmalarını kullanarak erişebileceğiniz yerel kodu kullanarak iletişim kurmanız gerektiğini tahmin ediyorum. Yani, Java'dan diğer süreçle konuşan yerel bir işlevi çağırırsınız.



0

Bağlantıların yeniden kullanılabilmesi için soketleri açık tutmayı düşündünüz mü?


prizler açık kalıyor. bağlantı, uygulamanın çalıştığı tüm süre boyunca canlıdır (yaklaşık 7 saat). mesajlar aşağı yukarı sürekli olarak değiş tokuş edilir (saniyede yaklaşık 5 ila 10 diyelim). mevcut gecikme yaklaşık 200 mikrosaniyedir, amaç 1 veya 2 büyüklük sırasını tıraş etmektir.
Bastien

2 ms gecikme mi? Hırslı. C-öğelerini JNI kullanarak arabirim oluşturabileceğiniz paylaşılan bir kitaplığa yeniden yazmak mümkün müdür?
Thorbjørn Ravn Andersen

2ms, 200 değil, 2000 mikrosaniyedir. Bu, 2ms'yi çok daha az iddialı kılar.
thewhiteambit

-1

JNI performansı hakkında Oracle hata raporu: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4096069

JNI yavaş bir arayüzdür ve bu nedenle Java TCP soketleri uygulamalar arasında bildirim için en hızlı yöntemdir, ancak bu, yükü bir soket üzerinden göndermeniz gerektiği anlamına gelmez. Yükü aktarmak için LDMA kullanın, ancak önceki soruların da işaret ettiği gibi, bellek eşleme için Java desteği ideal değildir ve bu nedenle mmap'i çalıştırmak için bir JNI kitaplığı uygulamak isteyeceksiniz.


3
JNI neden yavaş? Java'daki düşük seviyeli TCP katmanının nasıl çalıştığını bir düşünün, Java bayt koduyla yazılmaz! (Örneğin, bunun yerel ana bilgisayardan geçmesi gerekir.) Bu nedenle, Java TCP soketlerinin JNI'den daha hızlı olduğu iddiasını reddediyorum. (JNI, ancak IPC değildir.)

4
Yalnızca ilkelleri kullanırsanız, tek bir JNI araması size 9ns'ye (Intel i5'te) mal olur. Yani o kadar yavaş değil.
Martin Kersten
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.