Ryuz's tech blog

FPGAなどの技術ブログ

リアルタイム処理を再考してみる

はじめに

最近、github連携が便利でZennを使い始めましたが、あくまでこちらはQiitaの乗り換え色が濃いので、引き続き、結論のない試行錯誤中の感想とか、ポエム系はこちらに書こうかなと思います。

今回はまたあらためてプロセッサのリソース観点からリアルタイム処理を考えてみたいと思います(何度目だろう?)。

扱うのはリアルタイムシステムですので、デッドラインまでの時間制約を満たすという条件を守りつつ、より少ないリソースや電力での実現とか、よりリアルタイム制約のきついところに対応できるようにするには、とかの効率についてあれこれ駄文を書いている次第です。

TSSと優先度順スケジュールの違い

今回は話をシンプルにするために、CPUがまだシングルコアが当たり前だった時代のプロセッサ、つまり近年のCPUのようなSMT(Simultaneous Multi-Threading)など持たない、一度に一つの処理しかできない原始的なプロセッサを単位として話を進めようと思います(それでも組み込みの世界では十分な話にはなるかとは思います)。

普段使っているWindowsLinuxなどのユーザーフレンドリーなOSは TSS(Time Sharing System)と呼ばれるスケジューリング方式が多く、これらはタイマーを用い、例えば10ミリ秒ぐらいの人間からすると感知できないような短い時間でタスクを切り替えることで、まるで2つ以上のタスク(ここではスレッド/プロセス/ジョブなどを含んだ広い意味での実行単位のことをタスクと呼んでおきますね)が同時に動いているように見せることができます。

これはプロセッサが1つしかなかった時代に、複数人で使ったり、一人であっても複数のタスクを実行させるときに大変便利な仕組みでした。プロセッサが時分割で複数のタスクの処理を一度に進めてくれるので、まるでプロセッサ複数あるかのような振る舞いをしてくれ、ユーザーは複数のアプリを動かすことが可能でした(UNIX文化では当初からこの機能は導入されておりますが、一般に有名なところだとWindows95の登場からユーザーがTSSの恩恵を受けることが増えたのではないかと思います。当時はコンシューマ用途ではシングルプロセッサが普通でした)。

一方で、タスクを沢山実行すると、どんどんタスクの処理が終わるまでの時間が遅くなってしまいます。そうすると例えば通信の接続がタイムアウトしてしまったりと、制限時間のある処理に支障が出始めます。特に組み込み機器などですと「危険を感知してからブレーキを掛けるのが間に合わない」などと言う致命的なことも起こりかねません。

そこで活躍するのが優先度順のスケジューリングであり、特にその機能に特化して設計されたのが RTOS(Realtime-OS)と呼ばれるものです。

少し、極端な例ですが比較のための例を以下に示します。

TSSなOSとRTOSの違い

1秒かかるタスクが2つ起動された時、TSSでは2秒かけて2つのタスクをこなすのに対して、RTOSではまず先に優先度の高いタスクAを全力でこなし、その後にタスクBを処理します。

このときタスクBの結果が得られる時間は2秒後と変わらず、タスクAの結果が得られる時刻が1秒早まるという応答性観点だけならメリットしかない結果となります。

TSSの場合、さらにタスクCやタスクDなどが同時に起動されたりすると結果が得られるまで3秒、4秒と時間が伸びていきますが、優先度順のスケジューリングだと、さらにタスクCやタスクDなどが同時に起動されたとしても、タスクAに最高優先度が設定されている限り、タスクAは常に「要求されたら1秒後に結果を返す」ということが保証されますので、リアルタイム処理に向いているわけです。

(もちろん最近のOSはRTOSでなくても、優先度実行の機能は持っていたりしますが、プロセッサを完全に握ったったままになるバグがあったりするとシステムがロックする恐れもあり、ユーザー権限では利用できないケースが多いかと思います。また一般的な計算機環境ではキャッシュメモリや分岐予測といった他の実行時間を乱す要因も多いので、ハードウェア的にもこれらの要因がケアされた組み込みなどの特別な環境でRTOSが活躍することが多いです。)

なお別の視点でもう一度TSSを考えてみると、TSSは1個のプロセッサを1/Nの性能のN個のプロセッサがあるように見せる技術とも言えますので、ポラックの法則が機能する範囲においては、折角N2 倍のリソースを投入してN倍にしたプロセッサをN分割してしまう事になります。少し乱暴な考察になりますが、TSSでN個のタスク数を動かすとリソース効率は 1/N に低下するということになります。

シングルプロセッサ性能とマルチプロセッサ性能

ここでポラックの法則と呼ばれる「プロセッサの性能はその複雑性の平方根に比例する」という経験則を持ち出します。この法則自体破れ始めている部分はあるかと思いますが、いったん成り立つ前提に立つという前提で話を進めます。

当初はそもそもユーザーフレンドリーな領域のアプリケーションが十分に並列化されてなったといういう事情もあり、シングルプロセッサの性能がユーザー価値を決めていたためトランジスタはプロセッサ単体の性能向上に使われがちであったなどの事情もありましたが、純粋にリアルタイム処理という観点で見ると下記のようになります。

シングルプロセッサとマルチプロセッサの違い

ここで、全体性能で考えると、どちらもプロセッサは常に100%稼働しており、マルチプロセッサの場合1秒後にすべての処理が完了するにもかかわらず、シングルプロセッサだと1.4秒後の完了になるので明らかにマルチプロセッサの方が性能が良いことになります。

一方で、タスクAが結果を返すまでの応答時間みをリアルタイム性能として取り扱うと、シングルプロセッサの方が0.3秒ほど速くなっており性能が向上していることになります。これはもし 0.7秒以内にブレーキを掛けないとクラッシュする装置の制御に用いる場合は、たとえ全体性能が悪くともシングルプロセッサへのリソース投入を採用せざるを得ないということが起こります。

ここがリアルタイム処理の面白いところかと思います。

FPGA を考える

ここで FPGA を考えてみます。FPGA はある程度設計の頑張り具合で、高い周波数で動くようにしたり、並列度の高い回路構成にしたり、有限なリソースの中でその配置バランスを変えた設計が可能です。

シングルプロセッサとマルチプロセッサで起こるようなことはプログラミングの設計時に織り込んで考えていく必要がある点で、FPGAでリアルタイム処理を考えるのは結構奥深かったりします。

さらに、昨今は ZynqMP のような、RTOS向けに 600MHzで動くRPU(Cortex-R5) などを内蔵した SoC などが存在しており、並列化が難しく周波数が重要な処理をPL(Programable Logic)からプロセッサにオフロードするというような使い方も可能になっており、リアルタイム性能を突き詰めたときの設計選択肢が広がっています。

少しまとめ

TSSは

以下、ポラックの法則が成り立ち、かつそれ以上タスクは並列分解できないという条件下で多少乱暴な考察をすると

  • TSSは1個のプロセッサを1/Nの性能のN個のプロセッサがあるように見せる技術であり、リアルタイム性、リソース効率の両者を悪化させる
  • リアルタイム性保証を行うタスクが含まれる範囲においては、速度を1.4倍にするためにリソースを倍掛ける意義があるケースがある
  • リソース効率だけを考える場合、タスクの数までプロセッサを並列化すると全体性能は上がる

一方で1個のプロセッサのシングルスレッド性能は際限なく上昇するわけではなくポラックの法則はどこかで破れていきますし、 下限についても同様で、トランジスタ1個でプロセッサは構成できるわけではないので、ある程度現実的な範囲での議論となります。

とはいえ、この「現実的な範囲」というのはFPGAプログラミングを考える上では案外妥当な範囲に収まってくるので悪くない考え方です。

FPGAのようなプログラミングを考える場合にリソース効率だけを考えるなら、各演算回路が100%近く稼働する前提のもと

  • 並列化可能な処理は事前に並列化し尽くしておく
  • 並列化できた分だけフルに並列エンジンを考える
  • 並列化したタスクの中でそれぞれのリアルタイム応答性に必要な最小限の性能の計算機を最適なリソースで設計する

というのが設計指針になってくるかと思います。

FPGA設計していると、リアルタイム保証や帯域保証の為に「時間当たりの計算量の保証」が必要なケースが多々あるかと思います。そしてしばし普通にやると間に合わないのであらゆる技法を用いて演算効率を高めて保証内で演算が終わるように考えなくてはなりません。この時「並列化」というのは非常によい選択肢であり、特にリアルタイム保証は不要で、帯域保証のみが必要な場合は、設計をN倍に並列化するごとにリソース効率が√N倍に上がっていきますのでより小さなロジック消費で目的が達成できる見積もりになります(まあ実際は理屈通りにはいきませんが)。

ここにリアルタイム保証が加わるともう少し複雑で、分割した各処理のうちデッドラインを持っているものについてはそれを保証できる性能の計算リソースを割り当てていく必要が出てきます。この時、計算リソースの稼働率が100%にならないケースが出てくるので「空いたところに別の処理を入れ込めないか?」という議論が起こってきて、再びRTOSの優先度スケジュールのような話に原点回帰していくことになります。

おわりに

リアルタイム処理を考える場合、実行されるタスクの数だけプロセッサがあると、スケジューリングを考える余地がなくなってしまうため、複数プロセッサを内蔵したマルチコア時代のCPUで RTOS の面白さが低下してきている部分は多少なりあるのではないかと思っております。

一方で、並列度と計算時間の両方を考えないといけないFPGA上でのリアルタイム処理は、まだまだプログラマが考える部分が多数残っている点で、まだまだ面白い分野ではないかと思う次第です。

ちなみに、当サイトは基本的にリアルタイム処理の観点での話が多いのですが、昔に比べるとエッジでもRTOSが使われる比率が下がっておりあまりなじみのない方も増えているように思います。 少し調べてみるととても分かりやすそうなサイトを見つけたので最後に勝手ながらリンクを張らせていただきますね。

hiroyukichishiro.com