BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル バーチャルパネル: 低レイテンシ環境でJavaを使う

バーチャルパネル: 低レイテンシ環境でJavaを使う

原文(投稿日:2013/8/1)へのリンク

これまでCとC++が事実上の選択肢であった低レイテンシ作業にも、Javaが使われるようになってきた。

InfoQはこの分野の専門家4人を集めて、最近のトレンドについて、またこうした作業にJavaを使うときのベストプラクティスについて議論した。

参加者:

Peter Lawrey氏。低レイテンシと高スループットシステムに関心を持っているJavaコンサルタント。多くのヘッジファンド、商社、投資銀行のために仕事をしてきた。

Martin Thompson氏。高パフォーマンスと低レイテンシに関するスペシャリスト。20年以上、自動車、ゲーム、ファイナンシャル、モバイル、コンテンツマネジメントといった領域で、大規模トランザクションシステムおよびビッグデータシステムを使って仕事をしてきた。

Todd L. Montgomery氏。Informatica Ultra Messagingのアーキテクチャ担当副社長で29Westの低遅延メッセージング製品の主任設計者および実装者。

Dr Andy Piper氏。最近、OracleからPush TechnologyにCTOとしてジョイン。

質問:

  1. 低レイテンシとは何を意味するのですか? リアルタイムと同じ意味でしょうか? 一般的に、高パフォーマンスなコードにどう関係しているのですか?
  2. Javaを使う利点として、豊富なライブラリ、フレームワーク、アプリケーションサーバなどへのアクセスや、使えるプログラマが多いことが挙げられます。こうした利点は低レイテンシコードにも当てはまるのでしょうか? もし当てはまらないなら、C++よりも何か利点はあるのでしょうか?
  3. JVMは並行プログラムをどのようにサポートしていますか? 
  4. ガベージコレクションは少し無視して、Javaで低レイテンシなコードを書くのに、特別なテクニック(C++を使うときには当てはまらないもの)はありますか? たとえば、JVMのウォームアップ、IOを避けるためにすべてのクラスをPermGenに置くこと、キャッシュミスを避けるためのJava固有のテクニックなど。
  5. GCの挙動を管理することは、Javaで低レイテンシなコードを書くのに、どんな影響を及ぼしますか?
  6. 低レイテンシアプリケーションを解析するとき、パフォーマンスの「スパイク」や異常の裏でよく見られる原因やパターンはありますか?
  7. Java 7でInfiniBand fabric上でのSockets Direct Protocol (SDP)がサポートされました。これがプロダクションシステムで活用されるのを見たことはありますか? もし使われているのを見たことがなければ、ほかにどんなソリューションを見たことがありますか?
  8. Java固有の質問ではないかもしれませんが、なぜ競合を避けようとする必要があるのでしょうか?  競合が避けられない場合、どう管理するのがよいでしょうか?
  9. Javaによる低レイテンシソフトウェア開発のアプローチは、この数年で変わりましたか?
  10. ほかのパフォーマンス重視な作業にJavaはふさわしいのでしょうか? たとえば、HFTシステムでJavaは使えるでしょうか、それとも、まだC++を選ぶべきでしょうか?

Q1: 低レイテンシとは何を意味するのですか? リアルタイムと同じ意味でしょうか? 一般的に、高パフォーマンスなコードにどう関係しているのですか?

Lawrey氏: 目に見えないくらい高速なレイテンシ要件のあるシステムの場合、100ナノ秒から100ミリ秒のレベルになるでしょう。

Montgomery氏: リアルタイムと低レイテンシは大きく異なります。「リアルタイム」というのは、純粋なスピードよりも、緻密に制御されている(境界や異常値があることもある)ことで決まるというのが多数意見でしょう。これに対して、「低レイテンシ」は通常、純粋なスピードにプライオリティがあり、異常値はあっても少し許容できることを意味します。ハードリアルタイムの場合、これは確かに当てはまります。低レイテンシにおける重要な事前条件の1つは、効率への鋭い目です。システム観点から、アプリケーションスタック全体、OS、ネットワークにまで、効率が浸透している必要があります。つまり、低レイテンシシステムには、これらコンポーネントすべてに対して高度な機械的共感を備えている必要があるということです。この数年間、低レイテンシシステムで生まれたテクニックの多くは、OS、言語、VM、プロトコル、その他システム開発分野、ハードウェア設計における高パフォーマンス技術から生まれています。

Thompson氏: パフォーマンスというのは、2つのことを意味しています。1つはスループットで、たとえば毎秒といった単位になります。もう1つが応答時間で、これはレイテンシとして知られています。ただ「速くなきゃ」と言うだけでなく、単位を定義することが重要です。リアルタイムには非常に具体的な定義があり、よく誤用されています。リアルタイムというのは、システムの負荷によらず、入力イベントから応答するまでの実時間制約のあるシステムに関係しています。ハードリアルタイムシステムの場合、この制約を守れないと、システムエラーになるでしょう。心臓のペースメーカーやミサイル制御システムはその好例です。

取引システムでは、リアルタイムを別の意味でとらえているように見えます。システムは高いスループットを持ち、イベントに対してできるだけ素早く反応する必要があります。これは「低レイテンシ」だと考えることができます。通常、取引機会の損失はシステムエラーにはならないので、実際にはリアルタイムとは呼べません。

すぐれた取引システムとは、応答時間に偏りがほとんどなく、低レイテンシで反応することに高い品質を持つものです。

Piper氏: レイテンシとは、単に決定と行動の間にある遅延です。ハイパフォーマンスコンピューティングの分野において、低レイテンシは通常、ネットワーク伝送遅延が小さいこと、あるいは、要求から応答までの全体の遅延が小さいことを意味しています。「低」レイテンシの定義は、そのコンテキストに依存します。インターネットにおける低レイテンシは200msかもしれませんが、取引アプリケーションにおける低レイテンシは2μsになるでしょう。技術的に言えば、低レイテンシはリアルタイムと同一のものではありません。通常、低レイテンシはパーセンタイル値として測定され、異常値(レイテンシが低くない状況)を知ることは極めて重要です。リアルタイムの場合、システムの動作について保証します。そのため、パーセンタイル遅延を計測する代わりに、最大遅延を強制しているのです。実のところ、リアルタイムシステムは低レイテンシシステムでもある可能性が高いですが、逆は必ずしも真ではありません。ところが強制という考えが徐々に失われ、今では多くの人が同じような意味で使っています。

レイテンシを要求から応答までの遅延全体だとすると、これには様々なものが関係します。CPU、ネットワーク、OS、アプリケーション、物理法則までもです。そのため、低レイテンシシステムには通常、ハイパフォーマンスなコードが必要になります。それによって、レイテンシのソフトウェア要因を減らせるためです。

Q2: Javaを使う利点として、豊富なライブラリ、フレームワーク、アプリケーションサーバなどへのアクセスや、使えるプログラマが多いことが挙げられます。こうした利点は低レイテンシコードにも当てはまるのでしょうか? もし当てはまらないなら、C++よりも何か利点はあるのでしょうか?

Lawrey氏: あなたのアプリケーションで、コードの10%が時間の90%を費やしているとしましょう。Javaを使うことで、その10%を最適化するのは難しくなり、残り90%を書いてメンテナンスするのは簡単になります。混成の場合は特にそうです。

Montgomery氏: 資本市場、特にアルゴリズムによる取引では、いろいろな要因が作用します。アルゴリズムを市場にいち早く投入すればするほど有利になります。多くのアルゴリズムには寿命があり、すばやく登場させることが、アルゴリズムを活用する上で重要になります。Javaをとりまくコミュニティと利用可能な選択肢のおかげで、間違いなくJavaはCやC++よりも有利です。こうしたユースケースの場合、CやC++の選択肢はそれほど広くはないでしょう。ただし、純粋な低レイテンシが他の要因を排除するようなケースもあります。私は今や、JavaとC++のパフォーマンスの差は、単なるスピードでははっきり判断できないほど近くなっていると思っています。GC技術、JIT最適化、マネージドランタイムの改善によって、これまでのパフォーマンスに関するjavaの弱みは、無視できないほど説得力のある強みとなっています。

Thompson氏: Javaで書かれた低レイテンシシステムは、サードパーティ製ライブラリや、標準ライブラリすら使っていないことが多いです。これには大きく2つの理由があります。1つは、多くのライブラリはパフォーマンスを考慮して書かれておらず、スループットや応答時間が不十分であるためです。もう1つは、並行動作にロックを使っていることが多く、大量のGCを引き起こすためです。ロックの競合とGCのために、応答時間はかなりばらついてしまいます。

Javaは言語のなかでもツールのサポートがすぐれており、生産性を大きく高めてくれます。取引システムを構築する場合、市場に出すまでの時間が重要な要件であることが多く、Javaを使うことで開発期間を短縮することができるでしょう。

Piper氏: いろいろな意味で、逆もまた真です。すぐれた低レイテンシコードをJavaで書くのは、比較的難しいことです。なぜなら、JVMそのものによって、開発者はハードウェアの保証から隔離されているためです。とはいえ、よい知らせがあります。状況は変わってきていて、JVMは着実に高速化され、予測可能になっています。そして、開発者はJavaの動作、特にJavaメモリモデルと、それが下位にあるハードウェアにどのようにマップされるか、を深く理解することで、ハードウェア保証を利用できるようになっています。(実際のところ、Javaはプログラマが頼りにできるメモリモデルを提供した最初の人気のある言語でした。C++はあとになってそれを提供しました。)Martin Thompson氏が推奨してきたロックフリー、ウェイトフリー手法はよい例です。私たちの会社Pushでは、これをうまく開発に取り入れています。こうした手法がもっと主流になれば、標準ライブラリにも取り込まれていくでしょう(たとえば、Disruptor)。これにより、開発者は中身を詳しく理解しなくても、こうした手法を導入できるようになるでしょう。

こうした手法を使わなくても、Javaの安全面での利点(メモリ管理、スレッド管理など)は、C++で認識されているパフォーマンスの利点を上回ることが多々あります。JVMベンダーはもちろん、こうしたアプリケーションに適用できる最適化のおかげで、最近のJVMはカスタムのC++よりも高速なケースが多いと主張しています。

Q3: JVMは並行プログラムをどのようにサポートしていますか? 

Lawrey氏: Javaにはもともと組み込みのマルチスレッドサポートがあり、この10年ほどハイレベルな並行処理をサポートしています。

Montgomery氏: JVMは並行プログラムにとって、すばらしいプラットフォームです。メモリモデルのおかげで、ハードウェアを横断したロックフリーテクニックを使うための一貫性のあるモデルを開発者に与えます。ハードウェアを最大限活用するために、これは大きなメリットです。ロックフリーとウェイトフリーのテクニックは、開発コミュニティが喉から手が出るほど必要としている効率的なデータ構造を作るのに重要です。さらに、並行処理に関連する標準ライブラリは非常に便利で、弾力性のあるアプリケーションを開発するのに役立ちます。特性はさておき、C++11を考えると、Javaはこうしたものを手に入れる唯一の選択肢ではありません。C++11のメモリモデルは、開発者にとって大きな前進です。

Thompson氏: Java (1.5) は具体的なメモリモデルを備えた最初のメジャーな言語でした。言語レベルのメモリモデルがあるおかげで、プログラマはハードウェア上の抽象化によって並列コードを考えることができます。これは極めて重要です。ハードウェアとコンパイラはコードの順序を積極的に変えることで、スレッド間でよく見られる問題のあるパフォーマンスを改善することができます。Javaを使うことで、ロックフリーなアルゴリズムを書くことができ、うまくいけば、レイテンシが低くて予測可能な驚くべきスループットが得られるでしょう。Javaはロックのサポートも充実しています。でもロックが競合すると、OSが調停に関与する必要があり、大きなパフォーマンスコストになります。競合ロックと非競合ロックとの間のレイテンシの差は、通常3桁にもなります。

Piper氏: Javaの並行プログラムのサポートは、Java言語仕様そのものからきています。JLSには、並行処理をサポートした多数のJavaプリミティブおよび構成要素が書かれています。その基本レベルにあるのが、スレッドの生成管理を行うjava.lang.Threadクラスと、別のスレッドが共有リソースにアクセスするのを仲介するsynchronizedキーワードです。その上に、並行ハッシュテーブルからロックの種類ごとのタスクスケジューラまで、並行プログラムに最適化したデータ構造のパッケージ (java.util.concurrent) を提供しています。最大のサポートの1つが、JDK 5の一部としてJLSに導入された、Javaメモリモデル(JMM)です。これは、複数スレッドとそのインタラクションを扱うときに開発者が期待することを保証してくれます。こうした保証は、ハイパフォーマンスでスレッドセーフなコードを書くのを、非常に簡単にしてくれます。Diffusionの開発では、最大限のパフォーマンスを実現するのに、Javaメモリモデルを大いに頼りにしています。

Q4: ガベージコレクションは少し無視して、Javaで低レイテンシなコードを書くのに、特別なテクニック(C++を使うときには当てはまらないもの)はありますか? たとえば、JVMのウォームアップ、IOを避けるためにすべてのクラスをPermGenに置くこと、キャッシュミスを避けるためのJava固有のテクニックなど。

Lawrey氏: Javaを使うことで、限られたリソースで効率よくプログラムを書き、テストし、プロファイルすることができます。おかげで、「全体像」を把握するための時間ができます。低レベルなところまで深追いするのに時間をかけて、結局、レイテンシが大きくなってしまったC/C++プロジェクトをいくつも見てきました。

Montgomery氏: それは気の毒なことですね。唯一はっきりしているのは、適切な最適化をするために、JVMをウォームアップするということでしょう。実行時にクラス階層分析をすることで、クラスやメソッド呼び出しを最適化することの一部は、今のところC++ではできないません。でもそれ以外のほとんどのテクニックは、C++でもやれますし、そもそもやる必要がないものもあります。言語によらない低レイテンシのためのテクニックの多くは、最大のインパクトを及ぼすおそれのある「してはいけないこと」に関係があります。Javaには、低レイテンシアプリケーションにとって望ましくない副作用のある、避けるべきことがいくつかあります。1つは、Reflection APIといった特定のAPIの利用です。ありがたいことに、たいていの場合、同じ最終結果が得られる、もっと良い方法があります。

Thompson氏: あなたの質問にはたくさんの問題が含まれています :-) 基本的に、Javaではランタイムを安定した状態にするためにウォームアップする必要があります。安定した状態になると、Javaはネイティブ言語と同じくらい、場合によってはそれ以上、速くなります。Javaにとってのアキレス腱は、メモリレイアウトがコントロールできないことです。最近のプロセッサでは、キャッシュミスは約500インストラクションの実行機会の喪失になります。キャッシュミスを避けるためには、メモリレイアウトをコントロールする必要があり、キャッシュミスを避けるために予測しながらアクセスする必要があります。このレベルまでコントロールして、GC実行を減らすためには、DirectByteBuffersにデータ構造を構築したり、ヒープをなくしてUnsafeを使う必要があります。どちらも、データ構造を正確にレイアウトすることができます。もしJavaが構造体の配列をサポートしていれば、この必要性はなかったでしょう。これは言語変更なしに、いくつかの新しいintrinsicsを導入することで実現されました。

Piper氏: この質問は間違った前提に基づいているように思います。結局のところ、低レイテンシプログラムを書くことは、パフォーマンスに関心のあるプログラムを書くことと非常によく似ています。(C++でもJavaでも)その入力は開発者が書くコードであり、あるレベルの間接的中間物を伴うハードウェアプラットフォーム上で実行されます(たとえば、JVMを通したり、C++ではライブラリ、コンパイラ最適化などを通して)。特性が変化するという点において、ほとんど違いはありません。これは基本的に最適化における課題であり、最適化のルールはいつも次のようになります。

1. やらない。
2. まだやらない(エキスパートだけ)。

そして、もしそれだと、あなたが必要とするのに満たなければ、
1. 実際に高速化する必要があるか調べる。
2. コードをプロファイルして、どこに実際に時間がかかっているか調べる。
3. 効果の高い一部の場所に注力して、残りはそのままにする。

もちろん、これをやるために必要なツールや潜在的ホットスポットは、JavaとC++とでは違います。でもそれは、違うものだからです。確かに、あなたは平均的なJavaプログラマよりも、もう少し詳しく理解する必要がありますが、それはC++にも言えることです。もちろん、Javaを使うことで、あまり理解しなくても済むこともたくさんあります。ランタイムがうまく生成してくれるおかげです。最適化を必要とするものには、コードパス、データ構造、ロックなどが挙げられます。Diffusionでは、ベンチマーク駆動のアプローチを導入して、アプリケーションをたえずプロファイリングし、最適化の機会を探しました。

Q5: GCの挙動を管理することは、Javaで低レイテンシなコードを書くのに、どんな影響を及ぼしますか?

Lawrey氏: ソリューションは状況によって異なります。私のお気に入りは、ゴミをほとんど出さなくして、問題にならないようにすることです。日に一度発生するかしないかまで、GCを減らせます。

私見ですが、ゴミをなくすための真の理由は、CPUキャッシュがゴミであふれないようにすることです。出るゴミを減らすことで、コードのパフォーマンスを2~5倍改善します。

Montgomery氏: これまで見たJavaで書かれた低レイテンシシステムのほとんどは、ゴミが出るのを最小限にする、さらにはなくすための労力を惜しんでいませんでした。たとえば、Stringをまったく使わないことも珍しくありません。Informatica Ultra Messaging(UM)は特別なJavaメソッドを提供することで、オブジェクトの再利用といくつかの利用パターンの回避に関して、多くのユーザのニーズを満たします。思うに、一番よく見られる影響は、オブジェクトの再利用の普及です。このパターンは、Hadoopのような多くの非低レイテンシライブラリにも影響を及ぼしています。今や、APIやフレームワークのユーザに対して、ゴミを減らしたりなくしたりするオプションやメソッドを提供することは、コミュニティ内でよく見られるテクニックです。

コーディングプラクティスへの影響に加えて、低レイテンシシステムにも運用上の効果があります。多くのシステムには、まあ言ってみれば、GC制御の工夫が必要です。特定の時間にしかGCが起こらないようにするのも珍しくありません。アプリケーション設計と運用要求における効果は、異常値をコントロールして決定性を高める上で大きな要因となります。

Thompson氏: オブジェクトプールを使ったり、先ほどの回答でもふれたように、多くのデータ構造をByteBuffersやoff heapで管理する必要があります。これはJavaでCスタイルのプログラミングをするということです。もし本当に並列ガーベジコレクタがあれば、こうしたことは避けられるかもしれません。

Piper氏: java.lang.Stringの長さですって? すいません、冗談です。実際のところ、GCの挙動に及ぼす最大の変化は、プログラマ個人によるコーディング判断よりも、JVMの改善によってもたらされます。たとえば、HotSpotはGCポーズが何分もかかる初期の時代から、長い道のりを経てきました。こうした変化の多くは、競争によってもたらされました。これまで、レイテンシの観点から、BEA JRockitはHotSpotよりもうまく動き、ジッターはずっと小さいものでした。しかし、そのギャップはわずかだったので、Oracleは最近、JRockitとHotSpotのコードベースをマージしました。同じような改善は、AzulのZingのような最近のJVMでも見られます。GCの挙動を「改善」する開発者の試みは、たいていの場合、実際のところメリットがなかったり、悪化させることすらあります。

でも、開発者にやれることがないわけではありません。たとえば、メモリの急激な増減を制限するために、プールしたりオフヒープストレージを使ったりすることで、オブジェクトの割り当てを減らせます。ただし、こうした問題はJVMの開発者も非常に注目しており、何もする必要がなかったり、単に商用のJVMを購入する方が簡単だったりすることを覚えておきましょう。ムダなのは、実際に問題かどうかわからないまま、アプリケーションを早期に最適化することです。これにより、非常に有用なJavaの機能(GC)をバイパスして、アプリケーションの複雑さを高め、メンテナンスを困難にしてしまうためです。
 

Q6: 低レイテンシアプリケーションを解析するとき、パフォーマンスの「スパイク」や異常の背後によく見られる原因やパターンはありますか?

Lawrey氏: 特定のタイプのIOを待つこと。CPUインストラクションやデータキャッシュ障害。コンテキストスイッチ。

Montgomery氏: JavaではGCポーズが理解されはじめており、ありがたいことに、すぐれたGCが利用できます。でも、システムの影響はすべての言語に共通です。OSのスケジュール遅延は、スパイクの背後にある多くの原因の1つです。それが直接の遅延になる場合もあれば、真の問題である遅延によって引き起こされた連鎖反応の場合もあります。高負荷下のスケジュールについては、OSの方がすぐれている場合もあります。スケジュールリングにおいてアプリケーションがやった下手な選択が及ぼす影響は驚くべきもので、多くの場合、デバッグを困難にします。ここで注意すべきことは、I/OやI/Oがシステムで起こす競合からもたらされる遅延です。どんなI/O呼び出しもブロックするおそれがあり、どこででもブロックすると仮定するとよいでしょう。そこにある関係をじっくり考えることが非常に重要です。覚えておきましょう。ネットワーク呼び出しもI/Oです。

低パフォーマンスには、ネットワーク固有の原因が多数あります。考慮すべき重要な項目について挙げてみましょう。

  • ネットワークは行き来するのに時間がかかります。WAN環境において、データが伝播するのにかかる時間は自明ではありません。
  • イーサネットには信頼性がありません。そこに信頼性をもたらしているのはプロトコルです。
  • ネットワークロスは、再送や回復、TCP行頭ブロッキングのような二次的影響による遅延を引き起こします。
  • UDPを利用するときには、リソーススタベーションによって、さまざまな方法で受信側にネットワークロスが起こります。
  • 輻輳によって、スイッチやルータ内でネットワークロスが起こります。ルーターとスイッチはもともと競合ポイントです。競合が起こるとロスが発生します。
  • InfiniBandのような信頼性のあるネットワーク媒体は、ネットワークレベルでの遅延ロスとのトレードオフです。しかし、ロスが遅延を引き起こすという最終結果は同じです。

ネットワークを利用する低レイテンシアプリケーションでは、多くの場合、ネットワークにおける多数の遅延要因とジッターのもとを調べる必要があります。ネットワーク遅延に加えて、ネットワークロスは多くの低レイテンシアプリケーションにおけるジッターの原因の第一候補です。

Thompson氏: これまでいくつものレイテンシスパイクの原因を見てきました。GCはよく知られていますが、ロック競合、TCP関連の問題、下手な設定によるLinuxカーネル関連の問題など、いろいろあります。多くのアプリケーションはアルゴリズム設計がまずく、バースト状態におけるIOやキャッシュミスといったコストの高いオペレーションをならさずに、キューイングの影響にこ苦しむことになります。私が見てきたアプリケーションでは、たいていの場合、アルゴリズム設計がパフォーマンス問題とレイテンシスパイクの最大の原因です。

レイテンシスパイクを扱うとき、セーフポイントまでの時間(Time To Safepoint, TTS)が大きな問題になります。多くのJVMオペレーションには セーフポイントで停止させられるユーザスレッドが必要になります。セーフポイントチェックは通常、メソッドの戻りで実行されます。セーフポイントの必要性は、偏ったロックの取消し、JNIインタラクション、最適化されていないコードから、多数のGCフェーズまで、さまざまです。すべてのスレッドをセーフポイントに持って来るのにかかる時間は、作業を完了するよりも大きなものになります。そして、そうしたスレッドをすべて再び動かすのには、かなりのコストがかかります。スレッドをセーフポイントへすばやく予想通りに持っていくことは、たとえば、オブジェクトのクローンや配列のコピーなど、多くのJVMで考慮されたり、最適化されている部分ではありません。

Piper氏: 異常の一番の原因はGCポーズですが、GCポーズの一番の解決策は、実際にコードを変更するよりもGCをチューニングすることです。たとえば、JDK6とJDK7でデフォルトで使われるパラレルコレクタから、並行マークスイープコレクタに変更するだけで、レイテンシスパイクを引き起こす「stop-the-world」(すべてを止める)GCポーズに大きな違いをもたらします。チューニング以外に覚えておくべきことは、利用される全体のヒープサイズです。通常、非常に大きなヒープはガベージコレクタに圧力をかけ、長いポーズを引き起こすおそれがあります。たいていの場合、メモリリークをなくしてメモリ使用を減らすだけで、低レイテンシアプリケーションの動作全体に大きな違いをもたらします。

GC以外にも、ロック競合はレイテンシスパイクのもう1つの大きな原因ですが、その非決定性という性質のために、それを特定して解決するのは困難です。アプリケーションが続行できないときにはいつも、レイテンシスパイクが生じることも覚えておきましょう。これはいろいろなことで発生します。JVMのコントロール外のもの、たとえば、カーネルやOSリソースへのアクセスなどによっても引き起こされます。こうした制約が特定できれば、そのリソースの使用を避けるよう、あるいは使用するタイミングを変えるよう、アプリケーションを変更することができます。

Q7: Java 7でInfiniBand fabric上でのSockets Direct Protocol (SDP)がサポートされました。これがプロダクションシステムで活用されるのを見たことはありますか? もし使われているのを見たことがなければ、ほかにどんなソリューションを見たことがありますか?

Lawrey氏: かなりのゴミが出るので、イーサネットで使ったことはありません。低レイテンシシステムでは、ネットワークホップ数を最小化する必要があります。通常、取り除けるのは外部接続だけです。これらはほとんどイーサネットです。

Montgomery氏: あまり見たことがありません。気にはしていましたが、真剣に検討したことはありません。Ultra MessagingはSDPとメッセージングを使う開発者とのインターフェイスとして使われています。SDPはプッシュベースの利用パターンよりも、(R)DMAアクセスに適しています。DMAパターンをプッシュパターンに変えることは可能ですが、残念ながらSDPはあまりふさわしくありません。

Thompson氏: 実際に使われているのは見たことがありません。たいていの人は、OpenOnloadのようなスタックやSolarflareやMellanoxなどにあるネットワークアダプタを使っています。一方、独自のロックフリーアルゴリズムを使ってJavaから共有メモリに直接アクセスする、RDMA over InfiniBandは見たことがあります。

Piper氏: OracleのExalogicとCoherenceという製品は、ある時期、JavaとSDPを使っていました。その意味で、製品システムでこの機能を使っているのを見たことがあると言えるでしょう。サードパーティ製品を通してではなく、実際にJava SDPサポートディレクトリを使っている開発者から見ると、それほどのものではありません。でも、もしそれがビジネスメリットになるなら、これが変わるのを期待します。私たち自身は、レイテンシに最適化したハードウェア(たとえば、Solarflareの10GbEアダプタ)を利用しています。そのメリットは、Javaのチューニングよりもカーネルドライバの導入からもたらされています。

Q8: Java固有の質問ではないかもしれませんが、なぜ競合を避けようとする必要があるのでしょうか?  競合が避けられない場合、どう管理するのがよいでしょうか?

Lawrey氏: 超低レイテンシにとっては問題ですが、私は数マイクロ秒のレイテンシを問題だとは見ていません。リソース競合が避けられない場合には、影響について自覚し、最小化するようにしましょう。

Montgomery氏: 競合は起こります。したがって、それを管理することが重要になります。競合を扱うのにベストな方法の1つは、アーキテクチャ的に扱うことです。それには「単一書き手の原則」が効果的な方法です。要するに、競合しないようにするだけです。単一の書き手を想定し、その原則に従って作るのです。単一の書き出しでやることを最小化しましょう。これでできることに、あなたは驚くでしょう。

非同期動作は競合を避けるのにすばらしい方法です。「いつも役立つ仕事をしている」という原則で、すべて解決してくれます。

これも通常は、単一書き込みの原則になります。私はたいてい、競合リソースにおける単一書き手の前のロックフリーキューが好きで、あらゆる書き込みをするスレッドを使います。このスレッドでは、キューから取り出して書き込み処理をするループ以外、何もしません。エンキュー側におけるウェイトフリーのアプローチは、ここに大きなメリットがあります。すなわち非同期動作は呼び出し元の観点から、私にとってメリットをもたらすのです。

Thompson氏: アルゴリズムに競合があると、スケーリングのボトルネックになります。競合のあるポイントにキューができ、Little's Lawが効いてきます。Amdahl's Lawを使って、競合ポイントにシーケンシャルな制約をモデル化することもできます。ほとんどのアルゴリズムは、複数のスレッドや実行コンテキストからの競合を回避するよう改変でき、多くの場合、パイプラインによって並列化してスピードアップを図ることができます。もし本当に、特定のリソースにおける競合を管理する必要があるなら、ロックよりもプロセッサが提供するアトミックなインストラクションの方が、多くの場合、すぐれたソリューションになるでしょう。ロックはユーザ空間での操作であって、カーネルには関与しないためです。次世代Intelプロセッサ(Haswell)は、インストラクションを拡張して、データの一部をアトミックに更新するためのハードウェアトランザクショナルメモリを提供しています。残念ながら、Javaがこうした機能をプログラマのために直接サポートするには、かなり時間がかかるでしょう。

Piper氏: ロック競合は低レイテンシアプリケーションにおける最大のパフォーマンス障害の1つです。ロックそのものはそれほど高くつくわけではなく、競合がなければJavaのsynchronizedブロックは非常にうまく機能します。でも競合があると、ロックのパフォーマンスは急落するおそれがあります。ロックを保持しているスレッドが同じロックを必要とする別のスレットの動作を妨げるのはもちろんのこと、複数のスレッドがロックにアクセスすることはJVMによるロックの管理コストを高くするためです。これを回避することは重要であり、それはすなわち、必要ないものを同期しないことです。何も保護していないロックを取り除く、ロックの範囲を限定する、ロックを保持する時間を限定する、ロックの責務を混ぜない、などです。もう1つよくあるテクニックは、マルチスレッドアクセスを取り除くことです。共有データ構造に対してマルチスレッドアクセスする代わりに、シングルスレッドが面倒をみるキューを使って、更新をコマンドとしてキューに入れるのです。こうすることで、ロック競合はキューにアイテムを追加するだけになり、ロックフリーで管理することができます。

Q9: Javaによる低レイテンシソフトウェア開発のアプローチは、この数年で変わりましたか?

Lawrey氏: やりたいことをするシンプルなシステムを作りましょう。できる限り端から端までプロファイルしましょう。きちんと測定して、ボトルネックになっている部分を最適化したり書き直しましょう。

Montgomery氏: すっかり変わりましたね。Ultra Messagingは2004年にスタートしました。当時、低レイテンシなものにJavaを使うという考えは、ほとんど見うけられませんでした。でも、わずかでしたが、検討している人はいました。以後、それはどんどん増えてきました。今では、状況はすっかり変わったと思います。Javaは可能性があるだけでなく、低レイテンシシステムにとって代表的な選択肢かもしれません。コミュニティにおける姿勢の変化をもたらしたことは、Martin Thompson氏と [Azul Systemsの] Gil Tene氏のすばらしい業績です。

Thompson氏: この数年にあった大きな変化は、ロックフリーとキャッシュフレンドリーなアルゴリズムの絶え間無き改善です。パフォーマンスにとって、言語よりもアルゴリズムの方が重要であることを証明し続ける、という言語競争に関わるのを楽しんでいます。たいていの場合、機械的共感(mechanical sympathy)を示したクリーンなコードは、言語によらず、すばらしいパフォーマンスをもたらします。

Piper氏: Java VMとハードウェアは常に変化しており、低レイテンシ開発は常に、ターゲットとなるインフラストラクチャのスイートスポットに留まるための激戦区になっています。下位のハードウェアサポートを頼りにしたJavaメモリモデルと並行データ構造の実装において、JVMも安定して信頼できるようになっています。その結果、ロックフリー/ウェイトフリーといったテクニックが主流になってきました。今やハードウェアも、実行コアの増加による並列性の増加という方向へと向かっており、こうした変化を利用して混乱を最小限にするテクニック(たとえば、ロック競合を避けることを重視することで)が、開発にとって重要になってきています。

Diffusionでは、手元のIntelマシン上で手元のJVMを使って、1桁マイクロ秒以下のレイテンシを実現しています。

Q10: ほかのパフォーマンス重視な作業にJavaはふさわしいのでしょうか? たとえば、HFTシステム(高頻度取引システム)でJavaは使えるでしょうか、それとも、まだC++を選ぶべきでしょうか?

Lawrey氏: 開発期間、メンテナンス性、能力混成チームのサポートに関して、Javaがベストだと思います。JavaとFPGAやGPUを使うところの間にあるCやC++の居場所は、時間とともに狭くなってきています。

Montgomery氏: Javaは間違いなく、最高のパフォーマンスを得るための選択肢の1つです。HFTについて、Javaはすでに必要なものをほとんど備えています。とはいえ、作業すべきところはまだあります。明らかなのは、さらなるintrinsicsです。ほかの領域でも、Javaはうまく機能すると思います。でも低レイテンシと同様、何とかして実現させようとする開発者がいることが必要だと思います。

Thompson氏: 十分な時間があれば、JavaよりもすぐれたC/C++/ASMプログラムが動かせますが、最近はそういうことはめったにありません。たいていの場合、Javaを使うのが近道です。もしJavaにすぐれた並行GC、メモリレイアウトの制御、unsigned型、SIMDや並行プリミティブにアクセスするためのintrinsicsがもう少しあれば、とてもうれしいのですが。

Piper氏: 私はC++を最適化の選択肢として見ています。Javaは開発期間、信頼性、高品質という観点から、かなり望ましい開発環境です。そのため、私はいつも、最初にJavaを選んで、Javaで解決できないボトルネックがあった場合にだけ、他のものに切り替えるようにしています。それが最適化の持論です。

パネリストについて

Peter Lawrey氏。低レイテンシと高スループットシステムに関心を持っているJavaコンサルタント。多数のヘッジファンド、商社、投資銀行のために仕事をしてきた。彼はStackOverflowのJavaで第三位であり、彼の技術ブログには月に120Kページビューものアクセスがある。OpenHFTプロジェクトのリードデベロッパー。OpenHFTプロジェクトには、毎秒1億メッセージをサポートできるChronicleが含まれている。Performance Java User's Groupで月2回、低レイテンシに関する話題のフリーセッションを提供している。

Todd L. Montgomery氏。現在はInformaticaの一部である29Westのメッセージングビジネス部門のアーキテクチャ担当副社長。Informaticaのメッセージングビジネス部門のチーフアーキテクトとして、Ultra Messaging製品ファミリーの設計と実装に責任を持つ。これまでファイナンシャルサービス分野で170以上の製品をデプロイしてきた。以前は、TIBCOとTalarianのアーキテクチャ、ウェストバージニア大学での研究と講義、IETFへの貢献、さまざまなソフトウェア分野におけるNASAのための研究をしてきた。メッセージングシステム、信頼性のあるマルチキャスト、ネットワークセキュリティ、輻輳制御、ソフトウェア保障について熟知しており、20年におよぶ実務経験から独特の視点を持つ。


Martin Thompson
氏。高パフォーマンスと低レイテンシに関するスペシャリスト。20年以上、自動車、ゲーム、ファイナンシャル、モバイル、コンテンツマネジメントといった領域で、大規模トランザクションシステムおよびビッグデータシステムを使って仕事をしてきた。彼は機械的共感(Mechanical Sympathy、ハードウェアの理解をソフトウェア作成に適用すること)がエレガントで高パフォーマンスなソリューションをもたらす基礎になると信じている。すばらしいパフォーマンスの実現を手助けするスペシャリストになるまでは、LMAXの共同創業者でCTOだった。Disruptor並行プログラミングフレームワークはまさに彼の機械的共感が生んだ一例だ。

Dr Andy Piper氏。最近、Push TechnologyチームにCTOとしてジョイン。以前はOracleで技術ディレクタを務め、技術界の最先端で18年以上仕事をしてきた。Oracleでは、Oracle Complex Event Processing(OCEP)の開発をリードし、グローバルな製品ストラテジおよびイノベーションを牽引した。Oracleの前は、ミドルウェアインフラ技術のプロバイダであるBEA Systemsで、WebLogic Server Coreのアーキテクトを務めた。

この記事に星をつける

おすすめ度
スタイル

BT