LSIマルチコア時代のソフトウェア開発の落とし穴とその対策 ―― 電力や性能が改善しない,まさにデスマーチのこの状態にどう対処するか
パソコンやサーバと同じように,組み込み機器においてもマルチコア技術が使われ始めている.しかし実際のところ,マルチコアを導入して,期待どおりの成果は上がっているのだろうか? 実はマルチコア・システムの場合,ソフトウェアを正攻法で開発してもうまくいかないケースが珍しくない.ここでは,組み込み機器向けマルチコア対応ソフトウェア開発の課題とその改善策について解説する.(Tech Village編集部)
携帯電話などの情報系組み込み機器において,マルチコア構成のプロセッサ・アーキテクチャが一般的に用いられるようになりました.マルチコアを導入した組み込み機器にはさまざまなものがありますが,ここでは汎用OSを組み込み,多くのアプリケーションが動作する携帯端末機器(例えばスマートフォン)を考えてみてください.このような機器にマルチコアを導入する目的は,多くの場合,動作ソフトウェアの並列処理による性能向上と低消費電力化です.
ここでは,システム構築の観点から,マルチコア環境の課題を定量分析し,いくつかの改善のヒントを考察していきます.
●マルチコア・アーキテクチャは本当に効果的なのか?
本題に入る前に,開発エンジニアとしてではなく,まずは利用者として客観的にマルチコアの携帯端末機器製品を操作してみてください.できれば,同種のシングル・コア製品と比較しながら.それぞれのスペック表が傍らにあると,なお良いでしょう.
- その体感性能差は,CPUコアの延べクロック周波数の性能差ほどありますか?
- 電池の持ち時間や発熱はどうですか?
実際に操作してみて,確かに体感性能は向上したような気がすると思いますが,おそらくほとんどの場合,スペック表に記された数値ほどの差を感じることはないと思います.
では,さっそくこれを客観的に分析するために,前述した性能と電池の持ち時間を定量的に整理してみます.図1は,実際の市販製品をCPUの延べクロック周波数(MHz)で並べ,その消費エネルギー(J)をグラフ化したものです.当然,OSの版数,操作条件,その端末の搭載部品の違いにより誤差はありますが,おおむね図1のような傾向にあると考えてください.
さて,ここで着目すべきは,マルチコア時代に突入してから,ハードウェア・スペックをもとに換算した消費エネルギーの理論値(ideal)と,実際の端末の消費エネルギー(effective)とのギャップが,一気に2倍ほどに開いている点です.
これを見て「マルチコアを導入しても効果がない」と悲観するのではなく,まだまだ伸びしろがある,すなわち「このギャップを埋めることができれば,差異化できる」と考えることが,マルチコア時代を生き抜く開発エンジニアとしてのモチベーションであると考えています.
●何がボトルネックか考えてみよう
ここで,電力と性能の二つの側面に注目してマルチコアの課題を考えてみます.
1) 電力の改善しない
図2はLinux-Androidの基本的な電力制御の仕組みを,ソフトウェア,ハードウェア両面の観点から模式化したものです.アプリケーションやシステムのスレッド,およびプロセスを管理するOSのスケジューラなどを通じ,電力遷移の契機に応じて,PMU(Power Management Unit)経由でクロックや電源供給回路を制御します.電力制御の基本は,家庭内でいうと「こまめに電気を消しましょう」と同じ発想です.組み込みシステムでは,一般に動作不要なブロックの電源を遮断するPG(Power Gating)や,クロック制御まで加味したDVFS(Dynamic Voltage and Frequency Scaling)制御がよく知られています.
従来のシングル・コア構成の場合,すべてのスレッドやプロセスは一つのCPUで動作しているので,極端な話,ディスパッチや割り込みなどのイベント契機ごとにクロックや電源を制御できました.ところが,これがマルチコアになると,各コアにおいて同時に動作しているスレッドやプロセスの状態を監視し,電力遷移はor条件として成立したうえで電力制御を行います.
しかし,アプリケーションやシステムのスレッド,およびプロセスが大量にあり,負荷の大小が混在するような運用環境では,それぞれが指定する電力状態の遷移条件が輻輳(ふくそう)するため,所望の電力状態に遷移する条件が成立することがまれです.
図3はこの電力状態を市販製品で調べた事例です.左上は,端末を操作し,ブラウザ経由でストリーミング動画再生を行う一連の動作を電力測定した結果です.部分的に拡大してみると,なにか細かい制御が行われているようですが,大ざっぱにはフラットに電力を消費し続けています.
このアプリケーションの一連の動作中には負荷の緩急があるはずで,システム設計者はもっと電力的に凹凸があると期待します.そこで多くの開発現場では,アプリケーションそのものをより効果的に電力管理するため,かなり凝った実装を行うことで図3の右下のような結果を得ようと考えると思います(この測定結果は,電力遷移を意図的に起こさせるため,動画以外の処理を強制的に排他する仕込みを行ってみた).
専用機器や単機能システムであれば,このようなアプローチも効果的です.しかし,汎用のシステムは,例えば動画処理とは別に,バックグラウンドで通信,およびフレームワークのスレッドやプロセスが動作しているわけです.すると任意のタイミングで任意のスレッドやプロセスが動作することによって電力遷移条件が成立しなくなり,期待した効果が得られない状態になってしまいます.
先の例では,強制的に排他制御を行ってみましたが,システムとして成立させるには非現実的なインプリメンテーション(実装)になってしまっています.
結局,正攻法で工数をかけ電力制御機能を実装しても,実際の運用では「使っている」,「使ってない」程度の大ざっぱな実現がほとんどで,「不要な電気はこまめにカット」できていないのが問題の一つと考えられます.
2) 性能が改善しない
マルチコア環境で性能を考察するときには,並列拡張されたアムダールの法則が重要となってきます.一般的なアムダールの法則の考察では,対象となるソフトウェアの「並列性」に着目し,「並列化可能な部分が,与えられたコア数分に分配することで,性能向上となる」とされています.実際には,コアに分配するときに発生するオーバヘッドも存在しますが,この法則では,一つのソフトウェアの並列性しか考慮されていません.ここが性能に関する落とし穴になります.アムダールの法則は書籍によりいろいろな表現がありますが,以下のような式(1)で並列化したときの実行時間を評価します.ここでTは実行時間,Fは対象となるソフトウェアの並列化可能な比率です.
(1)
ここでポイントとなるのは,τとτ'です(簡易的に独立項として表現している).分岐や排他,あるいはOS処理といったソフトウェアに起因するオーバヘッドをτとすると,このオーバヘッドは,スレッドやプロセス構成,ソース・コード上の依存関係を調べることで回避策を講じることが可能です.これは,明示的なオーバヘッドと考えることができるでしょう.
一方,非明示的なオーバヘッドとしてτ'の考慮が必要になります.非明示的なオーバヘッドとは,設計やソース・コード上の依存関係がないのに発生するオーバヘッドで,今回,対象としているような,メモリやバスなどを共有するリソース共有型のアーキテクチャ構造に起因する現象です.
リアルタイム・システムや専用機器などでネイティブなコードを扱ってきた開発エンジニアの方は,こういったアーキテクチャ上の競合の考慮を行ってきたと思います.今回対象としている汎用機器のアプリケーションのレイヤ,ことJavaなどの抽象化された高級言語を用いて開発しているレイヤでは,そもそも制御が困難な状態にあります.
図4は,これらのオーバヘッドを考慮したアムダールの法則をグラフ化したものです.このグラフを見ると,並列化率が100%でもリニアに性能が伸びないだけでなく,並列化率が低い場合には,シングル・コアよりも性能が劣化するケースがあることを示しています.
非明示的なオーバーヘッドを実効性能として表現したものが図5になります.基本的にはキャッシュ・ミス特性が見えますが(グラフ青),ソース上の依存関係がないスレッドやプロセス同士でも,経路上で競合が発生すると,キャッシュ・ミスよりも悪い特性を示します(グラフ赤).これらの結果は,チップセット,および調停回路の構成やさまざまな環境により細かな結果は異なりますが,実効性能の劣化特性を調べると,ピーク時の10%も性能がでないケースもあります.
しかも,こういった劣化現象はマルチコア環境において,任意のコア,任意のタイミング,任意のスレッドやプロセスが引き起こす現象です.これが性能に関する落とし穴です.こういった法則特性を十分把握しないまま,多くの工数を割いてマルチコア対応の並列拡張の対策などを正攻法で行ってしまい,結果的に性能が伸びない,あるいは性能が劣化するといった苦い経験をした(あるいは現在経験中の)開発エンジニアの方も少なくないと思います.
マルチコア環境における電力や性能が改善しない問題は奥が深く,時間をかければ成果がでるものではありません.まさに落とし穴の中のデスマーチ状態です.さて,困りました.
ずばり,ソフトウェアやハードウェア,あるいはシステム開発などが分業化された現在の状態では,この問題は解決しません!