104

C/C++で開発された「サーバー」にTCPソケットを介して接続するJavaアプリがあります。

アプリとサーバーの両方が同じマシン、Solaris ボックスで実行されています (ただし、最終的には Linux への移行を検討しています)。交換されるデータのタイプは単純なメッセージです (ログイン、ログイン ACK、クライアントが何かを要求し、サーバーが応答します)。各メッセージの長さは約 300 バイトです。

現在、ソケットを使用しており、すべて問題ありませんが、IPC メソッドを使用して、データを交換するためのより高速な方法 (低レイテンシー) を探しています。

私はネットを調査しており、次のテクノロジーへの参照を思いつきました。

  • 共有メモリ
  • パイプ
  • キュー
  • DMA (ダイレクト メモリ アクセス) と呼ばれるものと同様に

しかし、それぞれのパフォーマンスの適切な分析を見つけることができず、JAVA と C/C++ の両方でそれらを実装する方法 (相互に通信できるようにするため) も見つかりませんでした。

このコンテキストでの各方法のパフォーマンスと実現可能性について誰でもコメントできますか? 有用な実装情報へのポインタ/リンクはありますか?


編集・更新

ここで得たコメントと回答に続いて、Unix Domain Sockets に関する情報を見つけました。これは、パイプのすぐ上に構築されているようで、TCP スタック全体を節約してくれます。これはプラットフォーム固有なので、JNI またはjudsまたはjunixsocketでテストする予定です。

次の可能なステップは、パイプの直接実装、次に共有メモリですが、余分なレベルの複雑さについて警告されています...


ご協力いただきありがとうございます

4

10 に答える 10

106

Corei5 2.8GHz で Java からのレイテンシをテストしました。単一バイトの送受信のみで、2 つの Java プロセスが生成されました。特定の CPU コアをタスクセットに割り当てていません。

TCP         - 25 microseconds
Named pipes - 15 microseconds

taskset 1 java Srvtaskset 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 マイクロ秒かかるため、シングルコアにスケジュールされた名前付きパイプにはまだ多くのオーバーヘッドがあります

共有メモリの測定結果: 2009 年 9 月 14 日 – Solace Systems は本日、ユニファイド メッセージング プラットフォーム API が共有メモリ トランスポートを使用して 700 ナノ秒未満の平均遅延を達成できることを発表しました。 http://solacesystems.com/news/fastest-ipc-messaging/

PS - 翌日、メモリ マップされたファイルの形式で共有メモリを試しました。ビジー状態の待機が許容される場合、次のようなコードで 1 バイトを渡すためのレイテンシを 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) は、2 つのプロセスが互いの変更を確認できるようにするために必要です (別の方法はまだ知りません)。2 つのプロセスがタスクセットで同じコアに強制された場合、レイテンシは 1.5 マイクロ秒になります。これはコンテキスト スイッチの遅延です。

PPS - 0.3 マイクロ秒は適切な数値です! 次のコードは、プリミティブ文字列の連結のみを実行している間、正確に 0.1 マイクロ秒かかります。

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

PPPS - これがあまり話題から外れていないことを願っていますが、最終的に Thread.sleep(0) を static volatile int 変数のインクリメントに置き換えてみました (そうすると、JVM はたまたま CPU キャッシュをフラッシュします) - 記録! - 72 ナノ秒の遅延 Java 間プロセス通信!

ただし、強制的に同じ CPU コアを使用すると、揮発性インクリメント JVM が互いに制御を譲ることはなく、正確に 10 ミリ秒のレイテンシが発生します - Linux のタイム クォンタムは 5 ミリ秒のようです...したがって、これは予備のコアがある場合にのみ使用する必要があります -それ以外の場合は、sleep(0) の方が安全です。

于 2011-06-20T13:55:39.993 に答える
10

DMA は、ハードウェア デバイスが CPU を中断することなく物理 RAM にアクセスできる方法です。たとえば、一般的な例は、ディスクから RAM に直接バイトをコピーできるハードディスク コントローラです。そのため、IPC には適用されません。

共有メモリとパイプはどちらも最新の OS で直接サポートされています。そのため、それらは非常に高速です。キューは通常、ソケット、パイプ、および/または共有メモリの上に実装されるなどの抽象化です。これは遅いメカニズムのように見えるかもしれませんが、そのような抽象化を作成することもできます。

于 2010-04-14T08:58:10.923 に答える
10

この質問は少し前に出されましたが、200 ns の典型的なレイテンシーと 20 M メッセージ/秒のスループットをサポートするhttps://github.com/peter-lawrey/Java-Chronicleに興味があるかもしれません。プロセス間で共有されるメモリ マップ ファイルを使用します (また、データを永続化する最速の方法となるデータを永続化します)。

于 2012-07-15T06:48:56.743 に答える
8

さまざまなIPCトランスポートのパフォーマンステストを含むプロジェクトは次のとおりです。

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

于 2010-04-15T04:54:00.253 に答える
6

ネイティブ アクセスの使用を検討する場合 (アプリケーションと「サーバー」の両方が同じマシン上にあるため)、 JNAを検討してください。対処するボイラープレート コードが少なくなります。

于 2010-04-14T07:10:18.050 に答える
6

到着が遅れましたが、 Java NIO を使用した ping レイテンシの測定に特化したオープン ソース プロジェクトを指摘したいと思います。

このブログ投稿でさらに調査/説明されています。結果は次のとおりです(ナノ単位の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

これは、受け入れられた回答に沿っています。System.nanotime() エラー (何も測定しないことで推定) は約 40 ナノ秒で測定されるため、IPC の場合、実際の結果はこれよりも低くなる可能性があります。楽しみ。

于 2013-07-03T14:08:12.860 に答える
2

ネイティブのプロセス間通信についてはよくわかりませんが、JNI メカニズムを使用してアクセスできるネイティブ コードを使用して通信する必要があると思います。したがって、Java から、他のプロセスと対話するネイティブ関数を呼び出します。

于 2010-04-14T06:52:54.263 に答える
1

私の以前の会社では、このプロジェクトhttp://remotetea.sourceforge.net/を使用していましたが、これは非常に理解しやすく、統合も簡単です。

于 2010-04-14T06:33:18.067 に答える
0

接続を再利用できるように、ソケットを開いたままにしておくことを検討しましたか?

于 2010-04-15T05:22:44.983 に答える
-1

JNI パフォーマンスに関する Oracle バグ レポート: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4096069

JNI は低速のインターフェースであるため、Java TCP ソケットがアプリケーション間の通知の最速の方法ですが、ソケットを介してペイロードを送信する必要があるという意味ではありません。LDMA を使用してペイロードを転送しますが、以前の質問で指摘したように、メモリ マッピングの Java サポートは理想的ではないため、JNI ライブラリを実装して mmap を実行する必要があります。

于 2010-11-30T08:20:03.673 に答える