2 <動作の見える化>マルチコアの問題解決に役立つ可視化の技術

マルチコア実行には、アプリケーションの高速化や低消費電力化など、さまざまな利点がある。ただし、その長所を引き出すためには、複数のコアを有効に利用できるように、適切な方法でソフトウェアを作成及び配置する必要がある。

マルチコア実行では、複数のプログラムが同時並列に動作するため、デッドロックなど、さまざまな問題が起こる。さらに、OSなどのタスクのコアに対する動的割り当てを利用する場合は、問題が発生した状況の把握や問題の再現性困難となる現象が起こりがちである。

こうしたマルチコア特有の問題を解析したり、解決したりする際には、CPU負荷やプログラム内部の依存関係などを可視化する手法が有効である。マルチコア向けソフトウェア開発支援ツール製品の中には、さまざまな可視化機能を備えているものが少なくない。

本章では、マルチコア上での理想的な実行状態を阻害するいくつかの課題を取り上げ、それらの課題を解析、解決するための情報の可視化について説明する。

  • 本章の対象読者
    • 知識・経験:レベル1(入門者) シングルコアの知識・開発経験のみ
    • プロセス:実装、テスト
    • ドメイン:組込み全般
    • キーワード:可視化、性能解析、依存解析、デバッグ、プロファイリング
  • 本章を読んで得られるもの
    • マルチコアの理想的な実行状態(あるべき姿)が分かる。
    • 実行状態に問題が発生した場合の解決のポイントを把握できる。
    • 問題の解析や解決に役立つ可視化の技術を理解できる。

2.1 マルチコアソフトウェア開発になぜ可視化が必要か?

最初に、マルチコアソフトウェア開発における可視化の有用性について述べる。

シングルコア実行では、動作プロセッサが一つであるため、時間順に表示されたログのような情報でも、ある程度の解析ができる。

マルチコア実行の場合、複数プロセッサが同時に動作するため、その動作状況を表示する際には、コア方向と時間方向の少なくとも2軸が必要であり、視覚的表示が有効である。

マルチコア動作に問題がある場合、可視化により、短時間で問題点を発見できることが多い。

2.1.1 マルチコア実行の理想像(性能向上)

マルチコア実行の理想的状況と可視化について述べる。

図 1 のように、各プロセッサ(コア0~コア2)ごとの実行状況について、時間軸を合わせて可視化することにより、CPU利用状況や負荷分散状況が分かる。

「ガントチャート」や「タイミングチャート」などの呼び方がある。

図 1 の例は、すべてのコアにおいて、常に処理が実行され、同時に終了しているため、理想的な状況と言える。

図 1: マルチコア実行の可視化

2.1.2 可視化を行う理由

2.1.2.1 並列実行の可視化

並列実行に課題がある例を紹介し、可視化を行う理由について説明する。

図 2 は、並列実行があまりうまくいっていない例

  • 各プロセッサに空き時間がある。
  • 同時に終わっていない。
図 2: 並列実行の可視化

可視化により、ひと目で問題の有無や深刻度、状況が分かる。さらなる原因解析についても、可視化した結果(グラフや図)を参照しながら進めることができる。

2.1.2.2 OSオブジェクト状態の可視化

組込みシステムではOS(リアルタイムOS)を使うことが多い。リアルタイムOSにはOSオブジェクトの状態をログとして記録する機能があるが、ログだけから現象を把握することは容易ではないため,可視化が重要である。

ログでは、各コアのイベントが1次元で並んでいるため、コア間の関係が読み取りづらい。

図 3: デッドロックが発生している例

2.2 マルチコア実行の課題とさまざまな可視化

次に、マルチコア実行に課題がある場合の原因についていくつか述べ、可視化により原因解析する例について説明する。

2.2.1 負荷分散の可視化

負荷分散に問題があると、性能に影響する。ここでは負荷分散に関するマルチコア実行の課題と可視化について紹介する。

図 4 の例では、改善前、コア0は常に処理を実行しているにもかかわらず、コア1には時間の空きがある。可視化により、負荷分散に問題があることがひと目で分かる。

例えば、処理Dをコア1に移すことにより、性能を改善できる。

図 4: 負荷分散の可視化

2.2.1.1 負荷分散の可視化に関連する用語

負荷分散の可視化に関連する用語を説明する。

  • 処理単位
    • 前ページでは単に「処理」と記載したが、処理の単位としてはさまざまな可能性がある。典型例は「スレッド」、「タスク」、「プロセス」など。
  • CPU利用率(アイドル時間)
    • 各プロセッサコアで有効な処理を実行している割合を「CPU利用率」と呼ぶ。アイドル状態になっている時間が「アイドル時間」である。
    • マルチコアの場合、他プロセッサが利用している資源を待ってビジーウェイトしている時間など、実行しているが有効とは言えない処理時間もあるため、注意が必要である。
  • アプリケーションの逐次実行部分
    • ある処理を実行している時、同時に実行できる処理が他に存在しない時間帯を「逐次実行部分」と呼ぶ。アムダールの法則をもとに理論並列性能向上率を考える上で、逐次実行部分の割合は重要である。

2.2.2 依存関係の可視化

依存関係は並列実行に制約を与えるため、性能に影響する。ここでは依存関係によるマルチコア実行の課題と可視化について紹介する。

図 5 は処理の実行順序制約を示している。

処理C、Dはともに、処理Aと処理Bの両方が終了していないと実行できない。

図 5: タスクの依存関係の例

実際の実行が 図 6 のように可視化されたとすると、コア0に長い待ちがあり、効率のよいマルチコア実行ができていないことがひと目で分かる。

図 6: 依存関係の可視化の例

このような処理の関係を「依存関係」と呼ぶ。

2.2.2.1 制御依存,データ依存

依存関係には「制御依存」と「データ依存」がある。

制御依存:プログラム制御上の順序関係

例:

if 処理A then 処理B else 処理C

データ依存:データ(変数など)の読み書き順序によって規定される順序関係

例:

i = f(x);   // 処理A
j = g(i);   // 処理B

としたとき、処理Bは処理Aの結果データを使っているのでデータ依存。

図 7: 制御依存とデータ依存

2.2.3 メモリ読み書き順序の可視化

同一メモリアドレスに対する読み書き順序の問題により、不具合が起こる場合がある。ここではメモリ読み書き順序にかかわるマルチコア実行の課題と可視化について紹介する。

  • 並列化により、処理が同時並行に実行される。同一メモリアドレスに対する読み書き順序が変わると、実行結果が変わることがある。
  • 並列化の際には、データ依存の関係( 1.2.2 参照)を見つけ、読み書き順序が変わらないように、処理順序に従って並列化を行う。
  • 同一メモリアドレスに対して、異なるプロセッサがほぼ同時に値を更新しようとすると、誤動作を引き起こすことがある。
  • 並列化の際には、排他制御( 1.2.5.1参照)により更新処理を保護する必要がある。

いずれの場合も、その対応により並列性が下がるので、対応箇所の範囲は最小限に抑える必要がある。ここで、ポインタの利用などを見逃すと、不具合が生じる。不具合箇所発見のため、可視化が重要になる。

2.2.3.1 メモリ読み書き順序の可視化の例

逐次実行および並列実行に対し、トレースなどを用いた動的解析によりメモリアクセス履歴を取り、対応する命令アドレス(プログラム上の場所)、メモリアドレス(プログラム内の共有変数名)、アクセス属性(Read/Write、Lockなど)、値、時刻などを表示する。

同一メモリアドレスに対し、逐次実行におけるメモリアクセス順と異なる箇所、あるいは同時実行可能性がある箇所の色分け表示などを行って可視化する。

図 8: Intel社Inspectorにおける可視化の例

図 8 は右図はIntel社 Inspectorにおける可視化の例。

保護されていないデータ競合(同時アクセスの可能性)箇所、および該当共有変数をソースコード上で色分け表示している。

2.2.3.2 RAW/WAW/WAR

メモリ読み書き順序が問題となる場合には、RAW(Read After Write)、WAW(Write After Write)、WAR(Write After Read)の3種類がある。Read After Readは順序が変わっても不具合が起こらないため、考慮しない。

図 9 、 図 10 、 図 11 は、逐次実行ではスレッド1→スレッド2の順だったものが、同時実行により、メモリアクセス順が入れ替わる例である。すべてのRead、Writeは同じメモリアドレスへのアクセスとする。

図 9: RAW(Read After Write)
図 10: WAW(Write After Write)
図 11: WAR(Write After Read)

2.2.3.3 動的解析を使う理由

メモリ読み書き順序を解析するためには、プログラム上のメモリアクセス箇所と、その時のメモリアドレスを特定する必要がある。解析には「静的解析」と「動的解析」があるが、以下の理由で可視化には動的解析が使われることが多い。

大域変数宣言された共有変数などは静的解析で特定可能であり、コンパイラなどで自動的に対応できる。

C言語のポインタ変数などは、多くの場合、実際に実行させなければ同一アドレスになるかどうかは分からず、動的解析が必要。すべてのポインタ変数への読み書きを保護する、あるいは順序制約をつけると性能が大幅に落ちるため、必要な箇所のみの保護にしたい。

例えば、次の関数

void func1(int \*a, int \*b) { ... }

において変数aとbが同一アドレスになるかどうかは、呼び出し側に依存している。開発プロジェクト内の関数利用規則で制約をかけたとしても、人的ミスにより誤った使い方をすると不具合が生じる。このような不具合の発見には、動的解析による可視化が有効である。

2.2.4 OSオブジェクトの状態の可視化

2.2.4.1 タスクとは

可視化では、リアルタイムOSの考慮が必要になる。

  • タスク
    • リアルタイムOS上の処理単位であり、状態を持つ。
  • タスクの基本的な状態( 図 12 参照)
    • 実行状態
      • プロセッサで実行されている状態
    • 実行可能状態
      • 実行すべき処理があるが、他に優先すべき処理があり、実行が保留されている状態
    • 待ち状態
      • 事象の通知を待っている状態
    • 休止状態
      • 起動していない状態
図 12: タスクの状態遷移

2.2.4.2 シングルコア実行のタスクスケジューリング

タスク状態を可視化することにより,処理が滞りなく進んでいるかどうかを判断できる。

  • シングルコア実行におけるスケジューリング
    • プリエンプティブな優先度ベーススケジューリング。
    • 優先度の高いタスクが存在すれば、低いタスクは実行されない。
  • 可視化(下図)において着目する点
    • どのタスクによりプリエンプトされ、処理が止まっているか
    • 何が原因で待ち状態となり、処理が止まっているか
図 13: シングルコア実行のタスクスケジューリング

2.2.4.3 マルチコア実行のタスクスケジューリング

マルチコア実行におけるタスクスケジューリングの基本は、以下の二つ。

  • AMPスケジューリング( 図 14 )
    • タスクは設計時、(静的に)設計者によって特定のコアに割り付けられる。
    • コア内はプリエンプティブな優先度ベーススケジューリング。
図 14: AMPスケジューリング
  • SMPスケジューリング( 図 15 )
    • タスクは実行時、(動的に)OSにより実行するコアが決定される。
図 15: SMPスケジューリング

2.2.5 マルチコアにおけるタスク状態の可視化

マルチコア実行では、コアごとのタスクの状態を可視化する。

  • AMPスケジューリングの場合可視化( 図 16 )において着目する点
    • それぞれのコアで独立してスケジューリングされるため,並列実行されるタスク間の実行タイミングが非決定的となり,予想していない実行タイミングが実行されデットロック等の発生条件の見落としが発生する。
    • 実行タイミングが変化する要因の例
      • 割込み発生タイミング・キャッシュ状態
図 16: AMPスケジューリングの可視化
  • SMPスケジューリングの場合可視化( 図 17 )において着目する点
    • タスクがいつどのコアに割り付けられて実行されたか
    • あるタスクの実行タイミングが変化すると、他のタスクの実行されるコアと実行タイミングが変化する可能性がある
図 17: SMPスケジューリングの可視化

2.2.5.1 同期通信オブジェクト

リアルタイムOSには、数種類の同期通信オブジェクトがある。マルチコア実行で処理が理想的に動作している場合、これらのオブジェクトの状態を可視化することにより原因を解析が容易となる。

  • FIFO
    • 通信のためのオブジェクト。
    • 内部にバッファを持ち、送受信が可能。バッファがフルの場合に送信すると、待ち状態になる。
    • バッファがエンプティの場合に受信すると、データが来るまで待ち状態となる。
  • セマフォ
    • コア間のタスクの排他制御のためのオブジェクト。
    • 共有資源を操作する前に取得し、操作後に返却する。
    • 資源数をカウントするカウンタを持つ。
    • 取得時に資源数が0なら待ち状態となり、他のタスクが資源を返却すると待ち状態が解放される。
  • スピンロック
    • コア間の割込みハンドラ間、および割込みハンドラ-タスク間の排他制御を実現するためのオブジェクト。
    • セマフォと異なり,資源数が0の場合は実行状態のまま資源の解放を待つ。
    • 取得した際には割込みも禁止するのが一般的。
    • 取得を試みている間も、割込みを禁止する場合がある(複数のスピンロックを取得する場合)。

2.2.5.2 FIFO

FIFOによるデータ通信で知りたい情報( 図 18 の可視化例を参照)

  • タスクがどの程度待っているのか、どの程度の頻度で送受信しているのか
    • タスクの状態を可視化
  • バッファサイズは十分なのか
    • バッファの状態を可視化
図 18: FIFOの可視化

2.2.5.3 セマフォ

セマフォによる排他制御で知りたい情報( 図 19 の可視化例を参照)

  • タスクがどれだけ待っているのか、誰に待たされているのか
    • タスクの状態を可視化
  • 優先度逆転やデッドロックは起きていないか
    • タスクとセマフォの状態の可視化
図 19: セマフォの可視化

2.2.5.4 スピンロック

スピンロックによる排他制御で知りたい情報( 図 20 の可視化例を参照)

  • タスクがどれだけ待っているのか、誰に待たされているのか
    • タスクの状態を可視化
  • 優先度逆転やデッドロックは起きていないか
    • タスクとスピンロックの状態の可視化
  • スタベーション(飢餓)が発生していないか
    • 各コアのタスクやISR(interrupt service routine)の状態を可視化
図 20: スピンロックの可視化

2.2.6 割り込み応答時間の可視化

2.2.6.1 割り込み応答時間の可視化

割込み応答時間の悪化の原因を特定するために、可視化が有効。

  • 割込み応答時間の最悪値に着目
    • 物理的な割込みが入ってから割込みハンドラが起動されるまでの時間
  • 割込み応答時間が悪化する要因は割込み禁止にある
    • シングルコアの場合
      • 最悪値はコード中の最長の割込み禁止区間の長さと一致する。
図 21: 割り込み応答時間の可視化

2.2.6.2 スピンロックの解析

マルチコア実行で発生する状況を解析するために、可視化が有効。

  • スピンロックの取得と共に割込みを禁止するため,スピンロックを取得している区間は割込み処理が遅延する。
  • 2段以上のスピンロックの取得の場合、割込みを禁止して取得を試みるため,他のコアのタスクの振る舞いにより、割込み禁止区間が変化する。
図 22: スピンロックの解析

2.2.7 プロセッサ情報の可視化

近年のプロセッサは、高速化・高性能化のためにさまざまな手法を取り入れている。これらの手法がプログラムの性能に影響を及ぼすため、性能解析のためにプロセッサの情報を可視化したい。

  • キャッシュ、分岐予測、TLB(translation lookaside buffer)
    • これらの機構はミスが発生する場合がある。ミス時はプログラムの実行性能が低下する。
  • プログラムのどの箇所で発生したかを可視化したい( 図 23 参照)
    • プロセッサ情報はシミュレータでも取得可能。ただし、キャッシュやTLBまでシミュレーションすると、シミュレータの実行速度が遅くなる。
    • 実機では、単位時間当たりのミス回数の統計を取得して可視化する。
図 23: プロセッサ情報の可視化

2.2.8 バスネットワークの利用情報の可視化

ここまで説明した要因について問題がないにもかかわらず並列実行性能が向上しない場合、バスネットワーク競合が考えられる。この問題の解析に対しても、可視化が有効である。

  • プログラム中において共有メモリや共有資源の利用を避けることが難しい場合も少なくない。その場合、プロセッサ(クラスタ)ごとのローカルメモリやキャッシュなどを利用し、共有メモリ(共有資源)への同時アクセスを避ける工夫が必要になる。
  • 例えば、データ並列(各プロセッサは同一プログラムで異なるデータを処理)利用時にデータを共有メモリに置くと、同一プログラムであるがゆえに、同じタイミングでデータアクセスが起こることがあり、バスやネットワークの競合が起こりがちである。そのような状況を見るために、可視化が有効(右図参照)。
図 24: バスネットワークの利用情報の可視化

2.3 まとめ

マルチコア実行において、すんなりと最高並列性能が達成できることは少ない。場合によっては途中でデッドロックするなど、並列要因により実行が止まることすらある。

そのような場合のデバッグや性能解析においては、時間軸のほかに、複数コアや複数タスクの状況を解析しなければならず、少なくとも2次元の解析が必要となる。

そのため、可視化を行い、問題発生時刻付近における複数コアや複数タスクの状況を見ることが、問題解析を容易にする。

並列実行を妨げる要因にはさまざまなものがあり、本資料ではそのいくつかの要因について説明し、可視化の例、およびその見方を紹介した。

コア数が多くなると、画面上の情報量が増え、解析が困難になるため、必要最小限、かつ、設計者に分かりやすい表示方法が重要となる。