HILS高速化(2)マルチノード

前回は、マルチレートモデルを作る事によって並列処理を行うための方法をご紹介しました。

今回は、それをマルチノードという仕組みを使って行う方法をご紹介します。マルチノードシステムを組むのは、なんとかしてHILSを高速化するためです。なんとかして高速化するためには、マルチノードの原理を理解しておくことが重要です。原理を理解すれば、マルチノードのシステムを組む際に、何に注意すれば良いのかが分かるようになります。

さて、マルチノードHILSの基本的な考え方は、『複数のノードを使って処理を分散しよう』というものです。

blog090706_00

ノードが複数あれば、モデルの演算や、I/O処理などの負荷を分散させる事ができます。マルチノードシステムとは、これらを並列実行することにより、トータルでの実行速度を稼ごう、というアイデアです。

マルチノードとは?

細かい話にいくまえに、まずはおおよそのイメージをつかんでください。この部分さえ理解できれば、マルチノードについて8割がた理解したも同然です。

Simulinkモデルは、このような感じで信号線がつながっている物です。これを、まずは大まかな部分に分けます。その後で、この部分は『ノード1』で実行しよう、この部分は『ノード2』で実行しよう、と決めます。

blog090706_01

ノード1、ノード2、それぞれの部分が並行して走るために、負荷を分散する事ができます。

原理はたったこれだけです。モデルをざっくりと分けて、それぞれのノードで走らせるだけのものです。

ダイレクトフィードスルー

マルチノードに関する理解は、前章のレベルでも十分です。しかし、マルチノードシステムには実はちょっとした『つまづきの石』があります。そこで、まず『ダイレクトフィードスルー』という概念を理解しましょう。そうすれば、どうやったらうまくマルチノード対応のモデルを書けるのか?が分かるようになります。

『ダイレクトフィードスルー』とは、『今回のステップの出力を計算するには、今回のステップの入力が必要』という意味です。

たとえば、Gainブロックについて考えてみます。このブロックは、『入力信号をX倍したものを出力する』ためのものです。入力値が決まらないと出力値が決まりません。こういったものを、ダイレクトフィードスルーと呼びます。

blog090706_02

次に、Memoryブロックについて考えてみます。このブロックは、『1ステップ前に入力された値を、今回のステップで出力する』というものです。今回のステップでの出力値を決めるのに、今回のステップでの入力値は必要ありません。前回のステップでの入力値さえあれば良いのです。そのため、Memoryブロックはダイレクトフィードスルーではありません。それはつまり、Memoryブロックの前の部分を先に計算してももちろんいいし、Memoryブロックの後の部分を先に計算しておいてもいい、という意味になります。

blog090706_03

このような感じで、ダイレクトフィードスルーのブロックが使われていると、モデルのどの部分を先に実行しなくてはいけないのか?が自動的にきまります。

たとえば、以下のモデルでは、Gain、Sum(++)がダイレクトフィードスルーです。したがって、次のような実行順序にしなくてはいけません。

blog090706_04

しかし、良く見るとこのモデルには、ダイレクトフィードスルーではない部分もあります。1つは、Memoryブロックの前後です。もう1つは、Sum(++)ブロックの2つの入力です。この2つの入力は、どちらを先に実行してもかまいません。

すなわち、次のような順番で実行しても構わない事になります。

blog090706_05『Gainの出力よりも先にGainの入力を演算する』、『Sum(++)の出力よりも先に、Sum(++)の入力を演算する』この2つさえ守っていれば、あとはどのように実行しても構いません。

ダイレクトフィードスルーでつながっている部分だけが、実行順序の制約を受けるわけです。

マルチノードとダイレクトフィードスルー

前章では『ダイレクトフィードスルー』について理解しました。あとは、マルチノードにおいて『ダイレクトフィードスルー』がどんな形で影響してくるかを理解する必要があります。そうすれば、マルチノード対応のモデルをうまく書けるようになります。

まずは、単純な例を見てみましょう。次のように、一直線にダイレクトフィードスルーでつながっているモデルについて考えてみます。

blog090706_06

このモデルを、赤線の部分で、それぞれのノードに分割したとしましょう。

blog090706_07

分割してしまうと、なんだかノード1、ノード2ともに関係ないモデルのように見えます。そうすると、すごく並列に実行できるんじゃないか?って思えてきます。

しかしそれはダメです。もともとのモデルはダイレクトフィードスルーでつながっていたのです。ですから、まずノード1の部分を実行し、その実行結果を受けてからノード2の部分を実行しないといけません。

これを言いかえると、ノード2は、ノード1の実行が終わるまで待たされる、とも言えます。

次に、ちょっと複雑な例を見てみましょう。

blog090706_08

このモデルを、赤線の部分でそれぞれのノードに割り当てたとします。

blog090706_09

そうすると、これもダイレクトフィードスルーでつながっているモデルですから、まずノード1の上半分から実行する必要があります。次に、ノード2。最後に、ノード1の下半分。かならず、この順序で実行する必要があります。

さて、この場合の処理がどうなっているのか、タイムチャートで確認してみましょう。

blog090706_10

こうやって書いて見ると一目瞭然です。なんと、ぜんぜん並列化が行われていないのです。せっかくマルチノードにしたのに、これでは全然高速化できません。高速化するどころか、ノード間通信のオーバーヘッドが発生する分、よけいに遅くなってしまいます。

マルチノードにする嬉しさはただ1つ、それは並列化です。ですから、なるべく並列化が行われるようなモデルにする必要があります。そのためには、なるべくダイレクトフィードスルーでつながっていない部分を割り当てる必要があります。

たとえば、次のようなモデルを考えてみます。

blog090706_11

これを、赤線で分割すると次のようになります。

blog090706_12

Gainブロックはダイレクトフィードスルーでしたが、Memoryブロックはダイレクトフィードスルーではありません。ということは、「処理2」→「処理3」という実行順序制約はありますが、それ以外は自由です。

すなわち、「処理1」の結果を待たずして「処理2」の実行を開始できます。この時のタイムチャートを書いて見ると、次のようになります。

blog090706_13

これで、かなり並列度が上がりました。このように、うまくモデルを分割してやることによって、並列度を上げる事が出来ます。並列度が上がると言う事はすなわち、HILSが高速化するということになります。

ダイレクトフィードスルーを考慮しないHILS

ここで1つだけ注意点を。

前章では、ダイレクトフィードスルーによる振る舞いについて考慮しました。しかし実は、こういう点についてきちんと考慮してくれるHILSは、かなり良い部類のHILSです。(私が良く知っているのは、Opal-RT社のRT-LABという製品です。)

ちょっといいかげんな作りのマルチノードHILSになると、ここまでの事を考えてくれません。ダイレクトフィードスルーでつながっているにも関わらず、実行順序を考慮してくれないのです。ノード間通信を共用メモリで実現しているHILSに、この手の物が多いように思います。もちろん、すべてがすべてそうではありませんが。

さて、たまたまそういうHILSを買ってしまったとしましょう。この手のHILSはダイレクトフィードスルーによる実行順序を考慮してくれません。そうすると、並列性が高くなります。ちゃんとしたHILSとくらべて実行スピードが上がってしまうのです。しかし、これに喜んではいけません。ダイレクトフィードスルーを無視しているということは、分割前のモデルとは全然違う何かを実行している、という事でもあります。ぜんぜん違う物を実行しているわけですから、当然演算結果が違って来ます。こういった誤差は、HILSとしては結構問題になってしまいますので注意が必要です。

だからといってあきらめる必要はありません。そういうHILSでマルチノードモデルを組む時は、そもそもダイレクトフィードスルーがノード間をまたがないようにすれば良いのです。どの部分をどの順番に実行してもいいよ、という形でモデルを作ってやれば、上記の演算誤差は無くなります。こういう事をするために多少工数がかかりますが、たぶんそれほど大変な作業でもないと思います。

まとめ

マルチノードは並列性が命です。ダイレクトフィードスルーに気を付けて、なるべく並列性が高くなるようなモデルを書きましょう。

次回

これまで、マルチレートと、マルチノードという2つの並列化によってスピードを稼ぐ方法をご紹介してきました。残念ながら、こういった方向でのHILS高速化はこれで限界です。

そこで次回は、FPGA化によって、さらなる高速化を実現するための方法についてご紹介します。