2010-04-14 14 views
89

Mam aplikacja Java, łącząc poprzez gniazda TCP do „serwer” w języku C/C++.najszybsza metoda (małe opóźnienie) dla Inter proces komunikacji między Java i C/C++

zarówno serwer aplikacji & są uruchomione na tej samej maszynie, pudełku Solaris (ale rozważamy przejście na Linuksa). typ danych wymienianych jest proste komunikaty (login, logowanie ACK, wtedy klient pyta o coś, odpowiedzi serwera). każda wiadomość ma około 300 bajtów.

Obecnie używamy gniazda, i wszystko jest w porządku, jednak szukam szybszy sposób wymiany danych (mniejsze opóźnienia), stosując metody IPC.

Byłem badania netto i podszedł z odniesieniami do następujących technologii:

  • wspólną pamięcią
  • rur
  • kolejek
  • , jak również to, co określane jako DMA (Direct Memory Dostęp)

, ale nie mogłem znaleźć odpowiedniej analizy ich poszczególnych występów, ani jak wdrożyć Nt ich w obu JAVA i C/C++ (aby mogli ze sobą rozmawiać), z wyjątkiem może rur, które mogę sobie wyobrazić, jak to zrobić.

może ktoś komentarz na temat występów & wykonalności każdej metody w tym kontekście? dowolny wskaźnik/link do przydatnych informacji o implementacji?


EDIT/UPDATE

następujący komentarz & odpowiedzi Mam tu znalazłem info o Unix domen gniazda, które wydają się być zbudowany tylko na rurach, i wybaw mnie cały stos TCP. to platforma specyficzny, więc mam zamiar sprawdzając je za pomocą JNI lub obu juds lub junixsocket.

następne możliwe kroki byłaby bezpośrednia implementacja rur, a następnie dzielona pamięć, chociaż byłem ostrzeżony o dodatkowy poziom złożoności ...


dzięki za pomoc

+7

Może to być przesada w twojej sprawie, ale rozważmy http://www.zeromq.org/ – jfs

+0

, co jest interesujące, ale pomysł będzie polegał na zastosowaniu metod "ogólnych" (jak w przypadku dostarczanych przez system OS lub dostarczonych przez język), właśnie dlatego wspomniałem o kolejkach i pamięci współdzielonej. – Bastien

+2

Zobacz także http://stackoverflow.com/questions/904492/ – MSalters

Odpowiedz

94

Wystarczy przetestowany latency z Java na moim Corei5 2.8GHz, tylko jeden bajt send/odebranych 2 procesy Java właśnie zrodził, bez przypisywania konkretnych rdzeni procesora z taskset:

TCP   - 25 microseconds 
Named pipes - 15 microseconds 

Teraz wyraźnie określające maski Core jak taskset 1 Java Srv lub 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 !!!! 

tak

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

Jednocześnie Thread.Sleep (0) (który jak pokazuje strace powoduje pojedynczy sched_yield() jądra Linuksa wezwanie do wykonania) zajmuje 0,3 mikrosekundy - tak nazwane potoki zaplanowane do pojedynczego rdzenia jeszcze wiele napowietrznych

Niektóre pomiary pamięci współużytkowanej: 14 września 2009 r. - Firma Solace Systems ogłosiła dzisiaj, że jej interfejs API platformy Unified Messaging może osiągnąć średnie opóźnienie poniżej 700 nanosekund przy użyciu współdzielonego transportu pamięci. http://solacesystems.com/news/fastest-ipc-messaging/

P.S. - próbowała pamięci współdzielonej następny dzień w postaci pamięci mapowane pliki jeśli zajęty oczekiwania jest do zaakceptowania, możemy zmniejszyć opóźnienia do 0,3 mikrosekundy przekazywania jednego bajta z kodem tak:

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 
} 

Uwagi: wątek. sleep (0) jest potrzebny, więc 2 procesy widzą nawzajem swoje zmiany (jeszcze nie znam innej drogi). Jeśli 2 procesy wymuszone na ten sam rdzeń z zestawem zadań, , opóźnienie wynosi 1,5 mikrosekundy - to jest opóźnienie przełączania kontekstu

P.P.S - i 0,3 mikrosekundy to dobra liczba! Poniższy kod zajmuje dokładnie 0,1 mikrosekundy, podczas wykonywania tylko prostego konkatenacji ciągów:

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

P.P.P.S - mam nadzieję, że to nie jest zbyt wiele poza tematem, ale w końcu próbowałem zastąpić Thread.sleep (0) rosnącą zmienną statyczną zmienną int (JVM zdarza się, aby opróżnić pamięć podręczną procesora) i uzyskać - nagrać! - 72 nanosekundy opóźnienie java-to-java komunikacja procesowa!

W przypadku wymuszania na tym samym rdzeniu procesora, JVM o zmiennej inaktywacji nigdy nie tracą kontroli względem siebie, dając w ten sposób dokładnie 10 milisekundową zwłokę - czas kwantowy Linuxa wydaje się wynosić 5 ms ... Więc to powinno być używane tylko wtedy, gdy istnieje zapasowy rdzeń - w przeciwnym razie sen (0) jest bezpieczniejszy.

+0

dziękuję Andriyowi, bardzo informacyjnemu badaniu, i to pasuje mniej więcej do moich pomiarów dla TCP, więc to jest dobre odniesienie. Sądzę, że zajrzę do nazwanych potoków. – Bastien

+0

Zastąpienie wątku (uśpienia) zwiększaniem niestabilności statycznej int powinno być wykonywane tylko wtedy, gdy można przypiąć proces do różnych rdzeni? Ponadto, nie zdawałem sobie sprawy, że możesz to zrobić? Myślałem, że OS decyduje? – mezamorphic

+3

Spróbuj LockSupport.parkNanos (1), powinien zrobić to samo. – reccles

2

nie wiem zbyt wiele o natywnej komunikacji między procesami, ale myślę, że trzeba się komunikować za pomocą natywnego kodu, który można uzyskać dostęp za pomocą mechanizmów JNI. Tak więc z poziomu Java nazwałbyś natywną funkcję, która rozmawia z innym procesem.

+0

+1 dla JNI. działa całkiem dobrze. – Jack

5

Jeśli kiedykolwiek zastanawiasz się nad używaniem natywnego dostępu (ponieważ zarówno twoja aplikacja, jak i "serwer" znajdują się na tym samym komputerze), rozważ JNA, ma mniej kodu standardowego, z którym możesz sobie poradzić.

7

DMA to metoda, za pomocą której urządzenia sprzętowe mogą uzyskać dostęp do fizycznej pamięci RAM bez przerywania pracy procesora. Na przykład. typowym przykładem jest kontroler dysku twardego, który może kopiować bajty bezpośrednio z dysku na pamięć RAM. Jako takie nie ma zastosowania do IPC.

Wspólna pamięć i potoki są obsługiwane bezpośrednio przez nowoczesne systemy operacyjne. W związku z tym są dość szybkie. Kolejki są zazwyczaj abstrakcjami, np. zaimplementowane na wierzchu gniazd, rur i/lub pamięci współdzielonej. Może to wyglądać na wolniejszy mechanizm, ale alternatywą jest to, że tworzysz taką abstrakcję.

+0

dla DMA, dlaczego to jest to, że mogę przeczytać wiele rzeczy związanych z RDMA (jako Remote Direct Memory Access), które miałyby zastosowanie w całej sieci (szczególnie w InfiniBand) i zrobić to samo. W rzeczywistości staram się osiągnąć równowartość BEZ sieci (ponieważ wszystko jest w tym samym polu). – Bastien

+0

RDMA to ta sama koncepcja: kopiowanie bajtów w sieci bez przerywania procesorów po obu stronach. Nadal nie działa na poziomie procesu. – MSalters

0

Czy za utrzymywanie gniazda otwarte, więc połączenia mogą być ponownie wykorzystane?

+0

gniazda pozostają otwarte. połączenie jest aktywne przez cały czas działania aplikacji (około 7 godzin). wiadomości są wymieniane mniej więcej w sposób ciągły (powiedzmy około 5 do 10 na sekundę). obecne opóźnienie wynosi około 200 mikrosekund, celem jest golenie o 1 lub 2 rzędy wielkości. – Bastien

+0

Opóźnienie 2 ms? Ambitny. Czy byłoby możliwe przepisanie C-stuff na współdzieloną bibliotekę, do której można podłączyć JNI? –

+0

2ms to 2000 mikrosekund, a nie 200. to sprawia, że ​​2 ms jest znacznie mniej ambitne. – thewhiteambit

0

Oracle raport o błędzie na JNI wydajność: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4096069

JNI jest powolny interfejs i tak Gniazda Java TCP są najszybszym sposobem powiadamiania między aplikacjami, jednak to nie znaczy, trzeba wysłać ładunek nad gniazdem . Użyj LDMA do przeniesienia ładunku, ale jak zauważyła previous questions, obsługa Java dla mapowania pamięci nie jest idealna i będziesz chciał zaimplementować bibliotekę JNI do uruchomienia mmap.

+2

Dlaczego JNI jest wolny? Zastanów się, jak działa warstwa TCP niskiego poziomu w Javie, nie jest to napisane w bajtowym kodzie Java! (Na przykład.to musi przejść przez macierzysty host). W związku z tym odrzucam twierdzenie, że gniazda Java TCP są szybsze niż JNI. (JNI to jednak nie IPC.) –

+3

Pojedyncza rozmowa JNI kosztuje 9ns (na Intel i5), jeśli używasz tylko prymitywów. Więc nie jest to takie powolne. –

9

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ści 20 M wiadomości/sekundę. Wykorzystuje pliki mapowane w pamięci pomiędzy procesami (utrzymuje również dane, które są najszybszym sposobem utrwalania danych).

4

Późne przybycie, ale chciałem zwrócić uwagę na open source project poświęconą pomiarowi opóźnień pingów za pomocą Java NIO.

Dalsze badania/wyjaśnienia w tym blog post. Wyniki są (RTT w nano):

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 

Jest to wzdłuż linii przyjętej odpowiedź. Błąd System.nanotime() oszacowany przez nic nie mierzenie jest mierzony przy około 40 nanosach, więc dla IPC faktyczny wynik może być niższy. Cieszyć się.

Powiązane problemy