Mam aplikację Java, łączącą się przez gniazdo TCP z „serwerem” opracowanym w C / C ++.
zarówno aplikacja, jak i serwer działają na tej samej maszynie, na Solarisie (ale rozważamy ostateczną migrację do Linuksa). typ wymienianych danych to proste komunikaty (login, login ACK, potem klient o coś pyta, serwer odpowiada). każda wiadomość ma około 300 bajtów długości.
Obecnie używamy gniazd i wszystko jest w porządku, jednak szukam szybszego sposobu na wymianę danych (mniejsze opóźnienie), przy użyciu metod IPC.
Przeszukałem sieć i znalazłem odniesienia do następujących technologii:
- pamięć współdzielona
- Rury
- kolejki
- a także tak zwane DMA (bezpośredni dostęp do pamięci)
ale nie mogłem znaleźć odpowiedniej analizy ich występów, ani tego, jak zaimplementować je zarówno w JAVA, jak i C / C ++ (żeby mogli ze sobą rozmawiać), może poza rurami, które mogłem sobie wyobrazić.
Czy ktoś może wypowiedzieć się na temat wydajności i wykonalności każdej metody w tym kontekście? jakiś wskaźnik / link do przydatnych informacji o implementacji?
EDYTUJ / AKTUALIZUJ
podążając za komentarzem i odpowiedziami, które tutaj otrzymałem, znalazłem informacje o gniazdach domeny Unix, które wydają się być zbudowane tuż nad potokami i zaoszczędzą mi cały stos TCP. jest specyficzny dla platformy, więc planuję przetestować go z JNI lub z judsami lub junixsocket .
następnymi możliwymi krokami byłaby bezpośrednia implementacja potoków, a następnie pamięć współdzielona, chociaż ostrzegano mnie o dodatkowym poziomie złożoności ...
dzięki za pomoc
Odpowiedzi:
Właśnie przetestowałem opóźnienie z Javy na moim Corei5 2,8 GHz, tylko wysłanie / odebranie jednego bajtu, 2 procesy Java właśnie uruchomione, bez przypisywania określonych rdzeni procesora do zestawu zadań:
Teraz wyraźnie określamy podstawowe maski, takie jak zestaw zadań 1 java Srv lub zestaw zadań 2 java Cli :
więc
W tym samym czasie Thread.sleep (0) (co, jak pokazuje strace, powoduje wykonanie pojedynczego wywołania jądra Linuksa schedule_yield ()) zajmuje 0,3 mikrosekundy - więc nazwane potoki zaplanowane na pojedynczy rdzeń nadal mają dużo narzutów
Niektóre pomiary pamięci współdzielonej: 14 września 2009 r. - Solace Systems ogłosiło dzisiaj, że jego interfejs API Unified Messaging Platform może osiągnąć średnie opóźnienie poniżej 700 nanosekund przy użyciu transportu pamięci współdzielonej. http://solacesystems.com/news/fastest-ipc-messaging/
PS - wypróbowałem pamięć współdzieloną następnego dnia w postaci plików mapowanych w pamięci, jeśli oczekiwanie zajęte jest akceptowalne, możemy zmniejszyć opóźnienie do 0,3 mikrosekundy na przekazanie pojedynczego bajtu kodem takim jak ten:
Uwagi: Thread.sleep (0) jest potrzebny, aby 2 procesy mogły zobaczyć swoje zmiany (nie znam jeszcze innego sposobu). Jeśli 2 procesy są zmuszone do tego samego rdzenia z zestawem zadań, opóźnienie wynosi 1,5 mikrosekundy - to jest opóźnienie przełączania kontekstu
PPS - a 0,3 mikrosekundy to dobra liczba! Poniższy kod zajmuje dokładnie 0,1 mikrosekundy, wykonując tylko konkatenację pierwotnych ciągów:
PPPS - mam nadzieję, że to nie jest zbyt odbiegające od tematu, ale w końcu próbowałem zastąpić Thread.sleep (0) inkrementacją statycznej, lotnej zmiennej int (JVM opróżnia pamięci podręczne procesora) i otrzymałem - nagraj! - 72 nanosekund opóźnienia komunikacji procesu java-to-java !
Jednak, gdy są zmuszone do pracy z tym samym rdzeniem procesora, maszyny JVM z przyrostem lotnym nigdy nie przekazują sobie kontroli, powodując w ten sposób dokładnie 10 milisekund opóźnienia - kwant czasu w Linuksie wydaje się wynosić 5 ms ... Więc powinno to być używane tylko wtedy, gdy jest zapasowy rdzeń - w przeciwnym razie sleep (0) jest bezpieczniejsze.
źródło
DMA to metoda, za pomocą której urządzenia sprzętowe mogą uzyskać dostęp do fizycznej pamięci RAM bez przerywania pracy procesora. Np. Typowym przykładem jest kontroler dysku twardego, który może kopiować bajty bezpośrednio z dysku do pamięci RAM. W związku z tym nie ma zastosowania do IPC.
Współdzielona pamięć i potoki są obsługiwane bezpośrednio przez nowoczesne systemy operacyjne. Jako takie są dość szybkie. Kolejki to zazwyczaj abstrakcje, np. Zaimplementowane na gniazdach, rurach i / lub pamięci współdzielonej. Może to wyglądać wolniejszym mechanizmu, ale alternatywą jest to, że Ci stworzyć taką abstrakcję.
źródło
Pytanie zostało zadane jakiś czas temu, ale możesz być zainteresowany https://github.com/peter-lawrey/Java-Chronicle, który obsługuje typowe opóźnienia 200 ns i przepustowość 20 M wiadomości / sekundę. Wykorzystuje pliki mapowane w pamięci współdzielone między procesami (utrwala również dane, co stanowi najszybszy sposób na utrwalenie danych)
źródło
Oto projekt zawierający testy wydajności dla różnych transportów IPC:
http://github.com/rigtorp/ipc-bench
źródło
Jeśli kiedykolwiek rozważasz korzystanie z dostępu natywnego (ponieważ zarówno aplikacja, jak i „serwer” znajdują się na tej samej maszynie), weź pod uwagę JNA , ma mniej standardowego kodu, z którym musisz sobie poradzić.
źródło
Spóźniony, ale chciał zwrócić uwagę na projekt open source poświęcony mierzeniu opóźnienia pingów przy użyciu Java NIO.
Dalsze zbadanie / wyjaśnienie w tym poście na blogu . Wyniki to (RTT w nanos):
Jest to zgodne z przyjętą odpowiedzią. Błąd System.nanotime () (szacowany przez nic nie mierzenie) jest mierzony na poziomie około 40 nanos, więc dla IPC rzeczywisty wynik może być niższy. Cieszyć się.
źródło
Nie wiem zbyt wiele o natywnej komunikacji między procesami, ale myślę, że musisz komunikować się za pomocą natywnego kodu, do którego można uzyskać dostęp za pomocą mechanizmów JNI. Tak więc z Java można wywołać natywną funkcję, która komunikuje się z innym procesem.
źródło
W mojej poprzedniej firmie pracowaliśmy nad tym projektem http://remotetea.sourceforge.net/ , bardzo łatwym do zrozumienia i integracji.
źródło
Czy rozważałeś pozostawienie otwartych gniazd, aby połączenia można było ponownie wykorzystać?
źródło
Raport o błędzie Oracle dotyczący wydajności JNI: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4096069
JNI to powolny interfejs, dlatego gniazda Java TCP są najszybszą metodą powiadamiania między aplikacjami, jednak nie oznacza to, że musisz przesyłać ładunek przez gniazdo. Użyj LDMA do przeniesienia ładunku, ale jak wskazywały poprzednie pytania , obsługa mapowania pamięci w Javie nie jest idealna i dlatego będziesz chciał zaimplementować bibliotekę JNI do uruchamiania mmap.
źródło