シングルコア用プログラムをマルチコア用プログラムへ移植する際に、移植元のプログラムでは問題が発生しなかったにもかかわらず、移植先のプログラムで不具合が発生するケースがよくある。
これは、並列に処理するプログラムには、逐次的に処理するプログラムでは考えなくてよかった、特有の欠陥が潜んでいる可能性があるためである。そこでソフトウェアテストの分野では、並列処理のプログラムやシステムに対するテストの難しさの原因、およびテスト手法について、長らく議論されてきた。
本章では、マルチコア用プログラムに潜む欠陥として、並列処理プログラムに特有の「安全性の破壊」、「生存性の破壊」、「公平性の破壊」の三つについて説明する。
こうしたマルチコア用プログラムの欠陥を効率的に検出できる「バックツーバックテスト(back-to-back testing)」を紹介する。
マルチコアプロセッサを使ったシステムの開発では、マルチコア用プログラムを対象としたテストが課題となっている。各プロセッサメーカがCPUの動作周波数を、これ以上飛躍的に向上させることは困難。一つのプロセッサチップに複数のCPUコアを内蔵するマルチコアへ向かっている。
マルチコアやマルチスレッドを用いることにより、ソフトウェア処理の並列性を高め、システム全体の処理性能を引き上げようという考え方。しかし、ソフトウェアが複数のCPUコアを効率的に利用できなければ、意味がなくなる可能性がある。並列コンピューティングに対応したプログラミングスキルが必要になる。
マルチコア用プログラム(並列処理プログラム)では、シングルコア用プログラム(逐次処理プログラム)では発生しない、並列処理特有の欠陥が発生してしまう。複数のプロセスが利用できる共有資源に対して、複数のプロセスからの同時アクセスにより、競合が発生する場合がある。
複数のプロセスの処理が共有資源にアクセスする時は、ロックなどを使った排他制御(相互排除)が必要である。排他制御とは、あるプロセスに資源を独占的に利用させている間、他のプロセスがその資源を利用できないようにすることによって、データの一貫性(整合性)を保つ処理のことである。
従来の逐次処理の欠陥の知識に加えて、並列処理特有の欠陥の知識を理解することが不可欠である。ただし、並列プログラミングで発生するバグを発見することは容易ではない。こうしたバグの多くが、プログラミング作業の終わりの段階で見つかることになる。
マルチコア用プログラムの動作確認は難しい。以下に、コンパクトデジカメの事例を紹介する。このシステムは二つのCPUコアを搭載している。
これら二つのデータ処理を同時並行に実行する。
ユーザが次の被写体を素早く狙えるように、キャプチャ処理と現像処理を別々のCPUコアで実行する。画像データは共有メモリに格納されており、両方のCPUコアからアクセス可能である。CPUコア間の通信には、AMP(asynchronous multi-processing)対応のリアルタイムOSの機能を利用する。
しかし,最初の機種の開発では、多くのトラブルを経験した。
シングルコア用プログラム(逐次処理プログラム)では発生しない並列処理特有の欠陥が、マルチコア用プログラム(並列処理プログラム)で発生した。
並列処理プログラムを対象としたソフトウェアテストの技術は、長年、研究されてきた。並列処理のプログラムやシステムに対するテストの難しさの原因、およびテスト手法については、1980年ころから議論。当時、「コンピュータシステムの処理性能向上のため、並列分散処理の時代が来る」と言われていた。
実際は、ハードウェアの発展、特にCPUの動作周波数の向上が著しかったため、並列処理が強く望まれる応用領域は限られていた。並列処理が持つ本質的な難しさやその原因については、このころから指摘されており、現在もそれほど変わっていない。
マルチコア用プログラム特有の欠陥は、以下の3種類に分類できる。
安全性の破壊とは、プロセス間で利用される共有資源の排他制御の失敗により、データの一貫性(整合性)が失われる欠陥である。
例えば、二つのプロセスで同じ変数の値(仮にx=0とする)を読み出し、一方のプロセスが1加え(x=x+1)、もう一方のプロセスが2加えて(x=x+2)から値を書き込んだとする。
この場合、値を同時に読み込んでしまうと、後に書き込んだ値だけが有効になるので、結果が一意に定まらない状況が生じる(x=1 or 2 or 3)。
並列処理が行われているプログラムのテストでは、同期が適切に制御されているかどうかを確認するため、さまざまなタイミングでデータの読み書きを行うテストが要求される。
ホテルの部屋、列車や飛行機の座席のオンライン予約などでも、この問題は起きる。
生存性の破壊とは、プロセス間の同期の失敗によって、プロセスが永久に待ち状態となってしまうデッドロックと呼ばれる状態が発生する欠陥。
デッドロックの例としては、Dijkstraが提案した「哲学者の食事問題」が有名である。
全員が1本ずつフォークを取ってしまうと、誰も永久に食事ができなくなってしまう。(デッドロックの状態)
公平性の破壊とは、ある処理(プロセス)だけが不当に実行されないライブロック(スタベーション、飢餓)と呼ばれる状態が発生する欠陥。
ライブロックの例として、先ほどの「哲学者の食事問題」にルールを追加して考える。
システムが異なった状態に変化していくので、デッドロックは回避できた。 しかし、もし5人の哲学者がまったく同時に食卓に着いたとしたら、いっせいに左のフォークを取って5分間右のフォークを待ち、左のフォークをいっせいに置いて5分間待つ、という状況が発生する。 この状態をライブロックと称し、このようにデッドロックを回避してもこの状態は回避できない場合がある。
マルチコア用プログラムを対象としたテスト設計を行う際には、以下のことに気をつける。
「二つ以上の異なるコンポーネントまたはシステムに対して同じ入力で実行し、出力を比較する。その結果、相違がある場合はそれを分析する」。( 参考文献5 )
適用例として車載機器に対する機能安全規格(ISO 26262)では、モデルベース開発のユニットテストや統合テストでの検証方法として規定されている。
並列処理特有の欠陥の検出には、バックツーバックテスト(B2Bテスト)が有効。
あらかじめ実行したシングルコア用プログラムの出力と、マルチコア用プログラムの出力を比較することで、マルチコア化によりプログラムの機能性が損なわれていないことの検証に適用できる。
データの精度や時間応答性に対して、完全一致が求められるのか、一定の閾値範囲内での許容誤差を設けるのか、B2Bテスト前に事前検討が必要となる。
CPUクロックに依存した応答性など、ペリフェラル側の挙動にも影響するかどうか検討した上で、B2Bテストの環境を構築する必要がある。
マルチコア用プログラムのテストでは、タイミング(適切な実行順序)の考慮が重要。 複数考えられる実行列のうち、たった一つだけをテストして終わってしまってはいけない。
さまざまなタイミングでプログラムを実行させることにより、再現しにくい並列処理特有の、タイミング依存の欠陥を検出する。
例えば、Javaマルチスレッドのテストフレームワークとして提案されているConTestである。
各プロセッサメーカは、一つのプロセッサチップに複数のCPUコアを内蔵するマルチコアに向かっており、その性能を引き出すため、マルチコア用プログラムの開発が必要になっている。
高品質なマルチコア用プログラムを開発するためには、マルチコアの特性を考慮したテストが重要になる。十分なテストを実施するためには、マルチコア特有の欠陥を理解していないといけない。
マルチコア用プログラムを対象としたテスト設計を実施しよう!
従来のシングルコア用プログラムで起こる欠陥も忘れずに…。