井戸を掘るという作業は並列化が困難です。穴の中で土を掘るという作業は基本的に1人で行わなければなりません。他の作業者は、掘り返した土をシャベルで取り除く程度の作業しかできません。ですから、穴の中の人数を増やしても作業が早く終わるわけではありません。それどころか、他の人はかえって邪魔になり、作業を遅らせる結果となってしまいます。このように並列化が向かないタスクはあります。
一方、並列化が容易なタスクもあります。例えば、水路の掘削などは並列化に適した作業です。この場合は複数の作業者が並んで作業を進めることができます。
上の絵は、MIMD(Multiple Instruction Multiple Data:複数命令 複数データ)と呼ばれる並列化の形態を示しています。それぞれの作業者は独立したユニットで、異なる作業を行うことができます。この場合は、4人の作業者がいれば、1人の場合の約1/4の時間で作業が完了すると考えられます。
SIMD(Single Instruction Multiple Data:単一命令複数データ)は、1人の作業者が下のようなシャベルを使う場合に例えることができます。
SIMDユニットは一度に1種類の計算しか行えませんが、複数のデータを並列で処理することができます。このタイプの命令は、多くのプロセッサのベクタ処理ユニットで広く使われています。これは、例えば画像処理のように使用するデータの規則性が高く、大量のデータセットに対して同じ処理を繰り返し行う必要がある場合に有効です。しかし、より一般的なコンピューティングタスクにおいてはこのモデルは柔軟性に欠け、性能向上は望めません。
このような理由から、複数のフルCPUサブシステムを1つのチップ上に置く、つまりマルチコアプロセッサを作るという選択肢を選ぶことになりました。1つのチップ上に複数のコアを置けば性能を向上させることができます。それぞれのコアは完全なCPUであり、個別に動作させることも他のコアと連携させることもできます。
プロセッサチップ上には異なるタイプのコアをさまざまな組み合わせで配置できる他、それらのコアにさまざまな形で処理を割り当てることができます。
ホモジニアスマルチコアプロセッサは、同じプロセッサコアを2個以上備えています。それぞれのコアは自律的に動作し、共有メモリシステムやメールボックスシステムなど各種メカニズムを通じて他のコアと通信したり同期したりすることができます。各プロセッサは固有のレジスタと機能ユニットを備えており、さらにローカルメモリやキャッシュを備えているものもあります。このコアがホモジニアス(同種)と呼ばれるのは、以上に述べたコアがすべて同じタイプのものだからです。
もう1つのタイプのマルチコアチップはヘテロジニアス(異種)マルチコアと呼ばれるもので、2個以上の異なるタイプのCPUコアを搭載しています。通常、搭載されたそれぞれのコアはまったく異なる特性を備えており、さまざまなシステム処理ニーズに使用できます。その一例がBluetooth通信チップで、1個のコアがBluetoothプロトコルスタックの管理専用に使われ、もう1個のコアが外部通信、アプリケーション処理、ヒューマンインタフェースなどの管理を行います。この種のマルチコアチップは、リアルタイムに特化した性能を備えたコアと、システム管理機能を持つコアの両方を必要とするアプリケーションに使用できます。
それでは、これらのコアがどのように使われるのかを見てみましょう。複数のコアが同じプロジェクトコードベースを実行する場合は、対称型マルチプロセッシング(SMP)と呼ばれます。異なるコアがコードの異なる部分を同時に実行できますが、そのコードは単一プロジェクトとして作成され、リアルタイムオペレーティングシステム(RTOS)のような何らかの制御プログラムによってそれぞれのコアに別々にディスパッチされます。必然的に、このように動作するコアは同じタイプのものでなければなりません。すべてのコアが、同一タイプのプロセッサ用にコンパイルされた同一のプロジェクトコードを使用するからです。
複数のコアまたはプロセッサがそれぞれ別のプロジェクトアプリケーションを実行する場合は、非対称型マルチプロセッシング(AMP)と呼ばれます。各コアは必要に応じて同期や通信が可能ですが、それぞれが異なるコードベースを実行します。複数のコアはそれぞれのプロジェクトを実行するので、異なるタイプのコア(ヘテロジニアスコア)を使うことができます。しかし、これは必須条件ではありません。2個以上の同じタイプのコアが異なるプロジェクトコードを実行する場合、それはAMPを実行するホモジニアスコアになります。
SMP動作の場合には、すべてのコアが同一プロジェクトコードベースのコードを実行するので、複数のホモジニアスコアが必要です。しかし、異なるコアが異なるコードベースを実行するような複数のプロジェクトの場合には、ヘテロジニアスシステムと同様に異なるコアを使うことができます。ただし、同じコアを使っても実行は可能です。
過去数年間の状況からすると、1960年代半ばに唱えられたムーアの法則も、限界を迎えつつあります。少なくともその勢いは鈍っています。もはやプロセッサのクロックレートが2~3年ごとに倍増することはなくなっており、実際に最も高速のCPUも、そのレートは長年にわたり数GHz(1桁台前半)の範囲で頭打ちの状態になっています。
引き続き性能向上の限界に挑む方法の1つは、効率的に使用できるならば、という条件付きですが、さらに多くのCPUコアを連携させることです。
スピードは横這い状態ですが、トランジスタのサイズは縮小し続けています。これまでより速度は低下しますが、小型のトランジスタを使えば、1つのチップ上により多くのロジックを組み込むことが可能になります。結果として、これらのトランジスタを使って複数のCPUコアを1つのチップに組み込めば、複数のCPUとメモリサブシステムの間で、従来よりもはるかに高速で広いバス接続を使用できます。
ホモジニアスコアによる非対称型マルチプロセッシングは、アプリケーションが、特性や条件の大きく異なる2つ以上のワークロードを抱えているような場合に非常に有効です。例として、一方がリアルタイムで割込み遅延の影響が大きく、もう一方が応答時間よりもスループットに大きく影響されるといった場合が考えられます。このモデルは非常に良好に機能します。例えば、あるデバイスにおいて、1個のコアをBluetoothやZigbeeのような通信プロトコルスタックの管理専用にあて、もう1個をヒューマンインタラクションとシステム全体の管理を行うアプリケーションプロセッサとして動作させるような場合があります。通信プロセッサを分離することで、プロトコルスタックに必要な優れたリアルタイム応答を提供することができます。加えて、通信ソフトウェア規格への適合を検証できるので、機能的変更をシステムのこの部分から分離することによって、製品全体の認証が容易になります。
1つのチップに複数のコアを組み込んだ場合、どのような問題があるのでしょうか? 詳しく見てみましょう。
モノリシックのアプリケーションやソフトウェアは、利用可能なコンピューティングリソースを効率的に使用できなくなる可能性があります。複数コアのリソースを使用するには、同時に実行可能な並列タスク用にアプリケーションを構成する必要があります。そのため、ソフトウェアエンジニアは、馴染みのない方法で組込み設計を考える必要が出てくるかもしれません。既存のシングルループコードを移植することは、恐らくそれほど容易ではありません。スレッドが少な過ぎたり、逆に多過ぎたりすることが性能上の障害となることもあります。
複数のスレッドやプロセス間でデータ構造やI/Oデバイスを共有するアプリケーションでは、シリアルボトルネックが生じる可能性があります。データ完全性を維持するには、多くの場合、読出しロック、読出し-書込みロック、書込みロック、スピンロック、ミューテックスなどのロッキングメカニズムを使用することによって、これらの共有リソースへのアクセスをシリアル化する必要があります。ロックの設計が非効率的な場合、共有リソースを使用するためにロックを獲得しようとする複数のスレッドやプロセス間で競争率が高くなり、ボトルネックが生じる可能性があります。これは、アプリケーションやソフトウェアの性能を低下させる恐れがあります。あるコアがストールして別のコアが共通ロックを待っている場合などは、2個のコアが動作しているにも拘わらず、その性能は1個のコアの場合より劣るという事態を招き、コアやプロセッサの数が増えるにしたがってアプリケーションの性能がさらに低下することもあります。
負荷分担が不均等な場合にも、コンピューティングリソースを効率的に使用できなくなります。大きなタスクは、並列で実行できる小さいタスクに分割する必要があります。また、性能とスケーラビリティを向上させるために、シリアルアルゴリズムをパラレルアルゴリズムに変更しなければならないこともあります。しかし、このような対策を講じても、あるタスクの実行が非常に高速である一方、他のタスクの実行にかなりの時間を要するのであれば、高速タスクは、低速タスクが完了するまで長時間待機することになります。これは貴重なコンピューティングリソースをアイドル状態にして、性能のスケーリング能力を低下させる結果となります。
RTOSは問題解決の助けとなるかもしれませんが、すべての問題を解決できるわけではありません。SMPシステムの場合、多数の同じコアのタスクスケジューリングを行うには、実質的にRTOSが必須条件となります。行う作業は、データごと、あるいは機能ごとに分割できます。作業をデータチャンクごとに分割した場合、各スレッドは、処理を構成する1つのパイプライン内のすべてのステップを実行します。これに対し、ある機能の1つのステップを1つのスレッドに実行させ、別のスレッドには別のステップを実行させるという方法も可能です。どちらが効率的かは、実行する作業の特性によって異なります。
マルチコアシステムをデバッグする際に最も役立つのは、すべてのコアを可視化することです。複数のコアを同時に、あるいは個別に開始したり停止したりできるのが理想です。つまり、他のコアの実行中または停止中に、1つのコアをシングルステップで実行できるということです。マルチコアブレークポイントは、あるコアの状態に基づき他のコアの動作を制御する際に、極めて有効な手段となり得ます。
マルチコアトレースを行うのは容易ではありません。複数のコアから得られる高帯域幅のトレース情報を管理したり、種類の異なるコアから得られるタイプの異なる可能性のあるトレースデータを扱うことは、非常に困難な作業です。
ヘテロジニアスマルチコアとホモジニアスマルチコアの両方が実装されたプロセッサの例を上に示します。ここには2つのホモジニアスコアのグループがあり、1つは2個のArm Cortec-A57がベースで、もう1つは4個のCoretex-A53がベースです。それぞれのグループはホモジニアスですが、2つのグループは互いにヘテロジニアスの関係にあります。
CoreSightデバッグアーキテクチャは、すべてのコア上のデバッグリソースと通信するためのプロトコルとメカニズムを備えています。そして、これらすべての情報の管理と、異なるコアからのメッセージの解析がデバッガの役割になります。クロストリガインタフェース(CTI)とクロストリガマトリックス(CTM)により、両方のコアを同時に一時停止したりトレースをトリガしたり、さまざまな操作が可能です。このトレースインフラストラクチャには、トレースフローをスムーズにするために使用するシリアルトレースポート(SWD)とパラレルトレースポート(TPIU)、および各ソースからのトレースを1つのフローにまとめるトレースファネルが含まれています。上図のチップは、デュアルコアデバイスと比較すると、はるかに複雑で制御が難しいことが分かります。
IAR Embedded WorkbenchのC-SPYデバッガは、対称型マルチコアデバッグと非対称型マルチコアデバッグの両方をサポートしています。これは、マルチコアタブのデバッガオプションを使ってイネーブルします。対称型マルチコアデバッグをイネーブルするのに必要な操作は、コア数をデバッガに入力して、通信を行うプロセッサの数を知らせることだけです。他のIDEでも同様のオプションを使用できるでしょう。
上図の右側にあるのがデバッガのビューです。図では4コアで構成されるCortex-A9 SMPクラスタのコアステータスが表示されており、それによるとNo.2は停止していますが、他の3個のコアは実行中です。
Coretex-M7コアとCoretex-M4コアを1個ずつ使用するSTのSTM32H745/755のように、非対称型マルチコアシステムにはヘテロジニアスマルチコアデバイスが使われます。この場合、デバッガの実行時にはIDEの2つのインスタンスが使われます(マスタとパートナ)。2個のコアが異なるプロジェクトコードを実行するので、1コアにつき1インスタンスが使われます。
IDEの各インスタンスには、現在制御中のコアと他のウィンドウで制御されるコアに関するステータス情報があります。デバッガの動作はオプションを選択することによって制御できるので、開発者は必要に応じて、すべてのコアを同時に、あるいは個々のコアを個別に開始あるいは停止することができます。
このような細やかな制御を可能にしているのが、クロストリガインタフェース(CTI)とクロストリガマトリックス(CTM)で構成されるArmの組込みクロストリガ機能です。CTIコンポーネントは3つあり、1つはシステムレベル用、1つはCortex-M7専用、残りの1つはCortex-M4専用です。下図に示すように、これら3つのCTIはCTMを介して相互に接続されています。システムレベルのCTIとCortex-M4用のCTIは、システムアクセスポートとポートに対応するAPB-Dを介してデバッガにアクセスできます。Cortex-M7用のCTIはCortex-M7コアに物理的に組み込まれており、Cortex-M7のアクセスポートを介してデバッガにアクセスできます。
CTIを使用すると、さまざまなソースからのイベントを使ってデバッグやトレースをトリガできます。例えば、複数のプロセッサコアのうちの1つで検出されたブレークポイントによって別のプロセッサを停止したり、外部トリガ入力で検出された遷移をコードトレース開始用として設定することができます。
この例は、1つのチップ上にCortex-M7コアとCoretex-M4コアを1個ずつ搭載したヘテロジニアスマルチコアプロセッサですが、ここでは2つの異なるプログラムが使われ、1つはCortex-M4上で、もう1つはCortex-M7上で実行されます。各プロジェクトは、プロセッサ上で実行されるこれらのソフトウェアをFreeRTOSによって管理し、2個のコアは共有メモリインタフェースを通じて通信を行います。しかし、これらのアプリケーションは、ともにFreeRTOSのメッセージパッシングメカニズムを使ってもう一方のプロセッサと通信を行うので、内在するメカニズムの複雑さが表に出てくることはありません。したがって、1つのCPUから見た場合、他のタスクとの間でメッセージの送受信を行っているに過ぎません。他のタスクが別のCPUコア上で実行されることになっても、それは透過的に行われます。
下の図は、IDEのワークスペースエクスプローラウィンドウです。ここには2つのプロジェクトの概要が示されているので、Cortex-M7プロジェクトとCortex-M4プロジェクト両方の内容を確認できます。
ウィンドウ下方にあるタブの内、現在選択されていないタブを選択すれば、M4プロジェクトまたはM7プロジェクトのどちらかにフォーカスを切り替えることができます。
Cortex-M7プロジェクトには、Cortex-M4で実行中のタスクにメッセージを送るタスクがあります。Cortex-M4には、実行中の受信タスクのインスタンスが2つあります。また、Cortex-M7には、種々の処理が正しく実行されているかどうかを確認するために周期的に実行される「チェック」タスクがあります。
最終的に、デバッガは両方のプロジェクトをロードします。これは、もう1つのデバッガ用にEmbedded Workbenchの新たなインスタンスが開始されることを意味します。
非対称型マルチプロセッシングを行えるようにデバッガをセットアップするには、一方を「マスタ」プロジェクトとして、もう一方を「パートナ」プロジェクトとして指定する必要があります。実際にはこの選択は任意で、起動時にどちらのプロジェクトが他方のプロジェクトを開始できるようにするかを決めるだけです。
「パートナ」プロジェクトに特別な設定はなく、他方のプロジェクトの「パートナ」として動作しているかどうかも認識していません。
このようにして「マスタ」プロジェクトがそのデバッガを起動すると、IDEの別のインスタンスが自動的に開始され、もう1つのデバッガセッションを提供し、その中で2つめのプロジェクトが実行されます。
マルチコアは、ムーアの法則が限界に達した場合でも性能の向上を可能にします。ただし、マルチコアにはデバッグ上の課題もあり、アプリケーションがマルチコアアーキテクチャの利点を最大限に生かせるようにするには、マルチコア特有の開発方法が必要になります。
一旦デバッグのセットアップが完了すれば、マルチコアデバッグは、これまでにないほど簡単なものとなります。過去にシングルコア用のツールを使ったことがあれば、このツールには必要なすべてのものが含まれていることが分かるでしょう。また、マルチコアデバッグがいかに難しいかということを話している人たちのことを、理解し難いと感じるようになるでしょう。
最新のハードウェアツールとソフトウェアツールが、マルチコアデバッグの問題解決に役立つことは確かです。