1つのマシンのマルチスレッドアプリケーションよりも遅いグリッドゲインアプリケーション
質問
私は最初のGridgainアプリケーションを実装しましたが、予想したパフォーマンスの改善を取得していません。悲しいことに、それは遅いです。実装を改善して、より速くなることができるように支援したいと思います。
私のアプリケーションの要点は、各関数評価にわずか数秒かかる数百万の可能なパラメーターを使用して、ブルートフォースの最適化を行っていることです。私はこれを実装し、数百万の反復をいくつかのグループに分割し、各グループが1つのジョブとして実行されます。
関連するコードの部分は以下にあります。関数maxAppliedrangeは、範囲xのすべての値の関数を呼び出し、最大値を返し、結果は各ジョブで見つかったすべての最大値になります。
scalar {
result = grid !*~
(for (x <- (1 to threads).map(i => ((i - 1) * iterations / threads, i * iterations / threads)))
yield () => maxAppliedRange(x, foo), (s: Seq[(Double, Long)]) => s.max)
}
私のコードは、1つのマシンでのマルチスレッド実行の間で選択するか、上記のコードを使用して複数のグリッドゲインノードを使用することができます。 Gridgainバージョンを実行すると、より速くなるように始まりますが、常にいくつかのことが起こります。
- ノードの1つ(別のマシン)はハートビートを逃し、メインコンピューターのノードがそのノードをあきらめ、2回目のジョブの実行を開始します。
- ハートビートを逃したノードは、同じ仕事を続けています。今、私は同じことをしている2つのノードを持っています。
- 最終的に、すべてのジョブは私のメインマシンで実行されていますが、一部のジョブは後で開始されたため、すべてが終了するまでにはずっと時間がかかります。
- ノードがタイムアウトし、タスク全体が失敗するため、Gridgainによって例外がスローされる場合があります。
- 私はイライラします。
私はそれをセットアップして多くの仕事をしようとしたので、失敗した場合、それはそれほど大したことではありませんが、これを行うと、各ノードで多くのジョブが実行されることになります。これにより、各マシンにはるかに大きな負担がかかるため、ノードがハートビートを逃す可能性が高くなり、すべてがより速く下り坂になります。 CPUごとに1つのジョブがある場合、1つのジョブが失敗した場合、別のノードが最初から最初からやり直す必要があります。いずれにせよ、私は勝てません。
私が最もうまくいくと思うのは、私が2つのことをすることができるかどうかです:
- ハートビートのタイムアウトを増やします
- 各ノードをスロットルして、一度に1つのジョブのみを行うようにします。
これができれば、自分の仕事を多くの仕事に分割することができます。各ノードは一度に1つのジョブを行い、ハートビートを逃すためにマシンが過負荷になることはありません。仕事が失敗した場合、作業はほとんど失われず、回復は迅速になります。
誰かがこれを行う方法を教えてもらえますか?ここで何をすべきですか?
解決 2
今、私はそれを正しく動作させています。私のアプリケーションの状況では、1つのマシンのマルチスレッドアプリケーションよりも約50%のスピードアップが得られていますが、それは私ができる最善ではありません。より多くの作業が行われることです。
Gridgainを使用するには、構成ファイルがすべてを機能させるために重要であるようです。これは、ノードの動作が設定されている場所であり、アプリケーションのニーズに合わせなければなりません。
XML構成ファイルで必要なことの1つは次のとおりです。
<property name="discoverySpi">
<bean class="org.gridgain.grid.spi.discovery.multicast.GridMulticastDiscoverySpi">
<property name="maxMissedHeartbeats" value="20"/>
<property name="leaveAttempts" value="10"/>
</bean>
</property>
これにより、ノードが欠落していると見なされる前に見逃す可能性のある最大心拍が設定されます。数秒後にノードを離れて戻ってくるという問題を抱えていたので、これを高い値に設定しました。あるいは、マルチキャストを使用する代わりに、構成ファイル内の他のプロパティを使用してノードを実行してマシンのIPSを修正できた可能性があります。私はこれをしませんでしたが、同じマシンを何度も使用している場合、おそらくより信頼性が高くなります。
私がしたもう一つのことは:
<property name="collisionSpi">
<bean class="org.gridgain.grid.spi.collision.jobstealing.GridJobStealingCollisionSpi">
<property name="activeJobsThreshold" value="2"/>
<property name="waitJobsThreshold" value="4"/>
<property name="maximumStealingAttempts" value="10"/>
<property name="stealingEnabled" value="true"/>
<property name="messageExpireTime" value="1000"/>
</bean>
</property>
<property name="failoverSpi">
<bean class="org.gridgain.grid.spi.failover.jobstealing.GridJobStealingFailoverSpi">
<property name="maximumFailoverAttempts" value="10"/>
</bean>
</property>
最初のものについては、ActiveJobSthreshold値は、同時に実行できるジョブの数をノードに伝えます。これは、執行者サービスのスレッドの数を変更するよりも、スロットリングを行うより良い方法です。また、一部のロードバランスとアイドルノードは、他のノードから作業を「盗む」ことができ、すべてをより速く成し遂げることができます。
これを行うためのより良い方法もあります。 Gridgainは、各ノードの測定されたパフォーマンスに基づいてジョブのサイズを実行できます。これにより、特にグリッドに高速で遅いコンピューターがある場合は、全体的なパフォーマンスが向上します。
将来のために、私は構成ファイルを研究し、それをJavadocsと比較して、すべての異なるオプションについてすべてを学び、これをさらに速く実行するようにします。
他のヒント
私はそれを考え出した。
まず、グリッドノードの動作方法の詳細を制御するXML構成ファイルがあります。デフォルトの構成ファイルは、gridgain_home/config/default-spring.xmlにあります。これを編集するか、コピーして、グリッドノードを起動するときに新しいファイルをggstart.shに渡すことができます。追加する必要がある2つのことは次のとおりです。
<property name="networkTimeout" value="25000"/>
ネットワークメッセージのタイムアウトを25秒に設定し、
<property name="executorService">
<bean class="org.gridgain.grid.thread.GridThreadPoolExecutor">
<constructor-arg type="int" value="1"/>
<constructor-arg type="int" value="1"/>
<constructor-arg type="long">
<util:constant static-field="java.lang.Long.MAX_VALUE"/>
</constructor-arg>
<constructor-arg type="java.util.concurrent.BlockingQueue">
<bean class="java.util.concurrent.LinkedBlockingQueue"/>
</constructor-arg>
</bean>
</property>
最初の2つのコンストラクターの引数は、1つのスレッドが起動し、最大スレッドサイズが1つです。エグゼキューターサービスは、グリッドゲインジョブを実行するスレッドプールを制御します。デフォルトは100です。そのため、アプリケーションが圧倒され、ハートビートがタイムアウトされていました。
私がコードにしなければならなかった他の変更は、次のとおりです。
scalar.apply("/path/to/gridgain home/config/custom-spring.xml") {
result = grid !*~
(for (x <- (1 to threads).map(i => ((i - 1) * iterations / threads, i * iterations / threads)))
yield () => maxAppliedRange(x, kalmanBruteForceObj.performKalmanIteration), (s: Seq[(Double, Long)]) => s.max)
}
.Applyステートメントがなければ、上記の編集を備えた構成ファイルではなく、すべてのデフォルトオプションを備えたグリッドノードを起動するためです。
今では、必要な通りに機能します。私はタスクを小さな部分に分割することができ、私の最も弱くて最も遅いコンピューターでさえ、この努力に貢献することができます。