後藤弘茂のWeekly海外ニュース

AMDが推進するHSA構想のカギとなる「hQ」

HSA構想でCPUコアとGPUコアを結ぶカギとなるhQ

 AMDが「hQ」(Heterogeneous Queuing)についての発表を行なった。hQは、シンプルに言えば、GPUコアをCPUコアと同じように扱えるようにするためのタスクキューの仕様だ。hQで定義されたメモリ上のキュー(待ち行列バッファ)に、GPUコアで実行するタスクを入れておくと、キューからGPUへと自動的に取り込まれて実行される。hQは、HSAランタイムを介せばCPUにも適用できるため、GPUコアとCPUコアの間で、タスクを連携させることにも使える。

 hQは、AMDが推進するヘテロジニアス(Heterogeneous:異種混合)コンピューティングを実現するためのソフトウェア/ハードウェアフレームワーク「HSA(Heterogeneous System Architecture)」の技術要素の1つとなる。HSA自体はAMD独自の規格ではなく、現在はHSA Foundationで他社と共同で策定を行なっている。hQは目立たないが、HSAの中で、ソフトウェア側とハードウェア側を結ぶカギとなる。

 hQは、すでにAMDが発表しているHSAでのバーチャルメモリの共有の仕様「hUMA(heterogeneous Uniform Memory Access:ヒューマ)」と連携する。hQとhUMAによって、CPUコアとGPUコア間の連携は劇的に容易になる。実際には、hQとhUMAに加えて、CPUコアとGPUコアの間のコミュニケーションの仕様である「HSA Signaling」も加わるが、それらの仕様によってGPUコアをCPUコアの1つのように扱うことができるようになる。下がその概念図で、現在はGPUへタスクを渡すことが非常に複雑だが、hUMAとHSA SignalingとhQによって劇的に簡素化される。

現在のアプリケーションからGPUへのタスクフロー
HSAの全ての要素が揃うとシンプルになる

 上の図で、GPUのネイティブ命令への変換はどうなっているという疑問を持ったかもしれない。実際には、カーネルのコードをGPUに受け渡しする際には、コードをGPUのネイティブ命令に変換するJITコンパイラが最終段に挟み込まれる。今回のhQの表で示されているのは、タスクのパケットのフローだ。

 hQ自体はタスクキューのソフトウェア上の仕様で、キューイングするパケットのフォーマットとキューイング言語として定義されている。アプリケーションまたはHSAランタイムがHSA仕様のパケットを生成して、キューに配置する。

 hQのキューは、GPUコアに対しては演算コマンドプロセッサのキューとなる。このキュー自体はメモリ上に置かれるが、少なくともAMD GPUの場合はGPUコア側がドライバソフトを介さずにハードウェアで管理する。そのため、アプリケーション側からは、キューにタスクを入れるだけで自動的にGPUで実行されるハードウェアキューとして見える。

 hQの完全なサポートのためには、AMDがすでに自社GPUコアに実装しているようなhQに対応した演算コマンドプロセッサの実装が必要となると見られる。hQは、従来のドライバベースのGPUアクセスではなく、HSAのソフトウェア層でのGPUアクセスを前提としているため、HSAのソフトウェアスタックの完成と連動する。つまり、hQは、HSAのハードウェアとソフトウェアと深く連動している。

 また、最終的にHSAが完全になる時には、hQの仕組みが、OSのタスクディスパッチャ(例えば、AppleのGrand Central Dispatch:GCD)と連携するか融合されるとAMDは構想している(少なくともhQを最初に発表した2010年頃は構想していた)。本来的には、OSやライブラリのタスクディスパッチャに組み込もうという発想から出発した技術だと見られる。

ソフトウェア層のオーバーヘッドを取り除くhQ

 AMDがhQを定義した最大の理由は、従来のGPUのソフトウェア層が分厚く複雑で、タスクの実行までのオーバーヘッドとレイテンシが大き過ぎることにある。下は、現在のドライバモデルでの演算タスクのディスパッチまでのフローを示したスライドだ。2011年のAMD Fusion Developer Summit(AFDS)で示されたスライド「TODAY’S COMMAND AND DISPATCH FLOW」の方がグラフィックスタスクの流れが分かりやすい。

現在のドライバモデルでのタスクディスパッチの流れ

 グラフィックスタスクの場合、まずDirect3Dのユーザーモードドライバから、ソフトウェア管理のコマンドバッファへと予約され、そこからカーネルモードドライバを経てDMAバッファへと送られる。オーバーヘッドがこれだけ大きくても問題にならないのは、グラフィックスタスクが粒度が大きくストリーム型のタスクだからだ。

 しかし、演算タスクでは、このモデルで実行すると不都合が生じる、粒度が小さくレイテンシが重要なタスクが出てくる。そのため、AMDはHSAでは、GPUで実行するためのタスクフロー自体を改良しようとしている。

 従来モデルでオーバーヘッドが大きい理由は複数ある。(1)つは、このフローの場合、CPU上のカーネルドライバがキューを管理するため、CPUのユーザーモードからカーネルモードへの遷移が必ず入ること。(2)つ目は、キューをドライバソフトウェアが管理するため、ソフトウェア処理のオーバーヘッドが入ること。(3)つ目に、GPUベンダー毎に異なるキューフォーマットへと変換する必要があること。(4)つ目に、GPUコアからGPUコア自身やCPUコアへのタスクディスパッチができず、そうしたタスクはCPU側がハンドルする必要があるという問題がある。

 HSA Foundationではこれらの問題を、(1)ユーザーモードキューイングの導入、(2)デバイス側のキューハンドリング(デバイスエンキュー)、(3)ベンダー間で共通化したパケットフォーマットとキューイング言語の定義、(4)GPUからのタスクディスパッチ、などを含むhQによって解決する。

ユーザーモードキューイングでハードウェアキューに渡す

 (1)の特権レベルの遷移は頻繁だと重くなるが、これはHSAでは特に大きな問題となる。それは、HSAがモバイルSoC(System on a Chip)もカバーするからだ。CPUアーキテクチャとパフォーマンスによっては、カーネルモードへのレベルの遷移がAMD CPUよりさらに重い場合がある。HSA FoundationにはARMコアやMIPSコアといったx86以外のCPUアーキテクチャのベンダーも含まれる。

 そのため、HSAではユーザーモードだけでキューイングを処理するユーザーモードキューイングを採用した。アプリケーションまたはユーザーモードランタイムが、共有メモリ上のキューにパケットを書き込む。GPU側はリードインデックスを見て、スタックの古い方からパケットを読み込む。

 ちなみに、カーネルドライバを使わないことは、従来のカーネルドライバタスクの全てをユーザーモードに移すという意味ではない。HSAでも、依然としてカーネルモードドライバは存在する。キューイングだけを、カーネルドライバから切り離し、ユーザーモードで動くアプリケーションやHSAランタイムで扱えるようにしたと見られる。

 (2)の、キューがCPU側のソフトウェアで管理されていることも、フローのオーバーヘッドを大きくしている原因だ。ソフトウェアが、キューからGPUへのディスパッチを管理する。AMDはこの複雑なソフトウェアキューイングを、シンプルなハードウェア(GPU側)のキューイングへと置き換えようとしている。アプリケーション側がタスクキューにGPUワークアイテムを置けば、GPU側が自動的にアクセスして処理してくれるというスタイルだ。HSAランタイムがキューの管理を行なう場合も、従来のようなキューイング全てを管理するわけではなく、オーバーヘッドは小さい。

 GPU側からのタスクのフェッチはコマンドプロセッサによって管理される。コマンドプロセッサの正体は、実はGPU内蔵のマイクロコントローラ(MCU)でプログラマブルにさまざまな処理ができる。つまり、GPU側のフロントエンドにあるMCUが、CPUからのタスクの取り込みと展開を行ない、GPUコアで実行させる。ドライバの処理の一部をGPU側に取り込んだと言い換えることもできそうだ。

hQのパケットフォーマットや言語を定義

 (3)のパケットフォーマットとキューイング言語も重要だ。グラフィックスの実行モデルは、異なるベンダーの多世代のGPUコアを抽象化するように作られた。各ベンダーは、GPUコアのためのキューに独自のフォーマットを使っており、ベンダー間の互換性はない。そのため、タスクは、ベンダー毎に異なるタスクパケットへとソフトウェア変換する必要があった。

 HSAでは、パケットフォーマットとプロトコル、キューイング言語を定義することで、このオーバーヘッドもなくそうとしている。HSA Foundationには、AMDだけでなく、ARMやImagination Technologies、Qualcommなど独自のGPUアーキテクチャを持つベンダーが多く参加しているため、パケットなどの仕様の標準化は重要だ。

 キューイング言語の定義は、キューの持つ機能とGPU側でのキューの管理自体を定義することでもある。パケットには、GPUで実行するカーネルプログラム上のポインタやワークアイテムのサイズなどの情報が含まれる。

 パケットフォーマットにはバリアフラグが設定されており、それがセットされていると、前のパケットの処理が完了するまで次のパケットを開始できない。タスク間の依存関係がパケットに明示されている。これが重要なのは、旧来のアーキテクチャでは、GPU側ではキューの中のタスクの依存関係を知ることができなかったからだ。HSAキューイングパケットにはバリアパケットもあり、依存性を特定することもできる。

 現在のPC向けGPUやGPUコアは複数の演算タスクを並列に行なうことができる。その場合の問題は、タスク間の依存関係をGPU側が知ることができない点にあった。hQではその問題を新しいパケットフォーマットで解決する。

CPUコアとGPUコアの両方へタスクをディスパッチする

 (4)つ目の問題点は、従来はGPUコアからGPUコア自身やCPUコアへのタスクのディスパッチができなかった点で、これはCPUがマスタでGPUがスレーブという実行モデルに立っていたからだ。hQでは、この問題も解決する。まず、GPUから自分自身にタスクをキューに投げることができるようにする。さらに、CPUコア側にもHSAタスクキューイングランタイムでhQフォーマットのパケットを実行できるようにして、GPUコアからCPUコアにタスクを渡せるようにする。

 こうした形にすることで、最終的にはCPUコアとGPUコアのどちらにも同じフレームワークからタスクをディスパッチし、また、コアの間で自由にタスクを渡すことができる仕組みができあがる。これがHSAの目指すゴールだ。

 AMDの元々のhQのビジョンは、タスクキューイングランタイムの実装と密接に絡んでいた。現在のドライバベースのGPUコアのソフトウェアスタックを、タスクキューイングランタイムで置き換えようという発想だ。面白いのは、こうしたタスクキューイング&ディスパッチのラインタイムシステムは、すでにCPUの世界ではSMP(Symmetric Multi-Processing)システム向けに使われていることだ。HSAのキューイングは、それをGPUコアにも拡大した。

 では、HSAのhQは、既存のタスクキューイングランタイムとどうすり合わせて行こうとしているのか。当初のAMDの考えでは、下のスライドのようにタスクキューイングライブラリの例として、Appleの「Grand Central Dispatch(GCD)」、Microsoftの「Concurrency Runtime (ConcRT)」、さらにはIntelの「Threading Building Blocks(TBB)」などの名前を挙げていた。つまり、既存のタスクキューイングランタイムにhQをはめ込むことを構想していた。

旧来のドライバベースのソフトウェアスタック
2010年11月にAMDが発表したHSAが完成した状態のソフトウェアスタックの構想

 しかし、当然ながらこうしたOSやライブラリのキューイングシステムへのhQの統合は、ソフトウェアベンダーという相手がある話で、ハードウェアベンダーだけではできない。今回の発表も、hQ自体のスペックを策定したというもので、その先の展開には触れていない。とはいえ、HSA Foundationは下のスライドにあるような最終的なHSAソフトウェアスタックに向けて、また一歩前進したことは確かだ。

 HSAの目的は、GPUコアのプログラミングを容易にすることで、ヘテロジニアスコンピューティングを花開かせることにある。これは、GPUコアに利点を持つAMDにとっては、コンピューティング市場で勝ち抜くための必須の要素だ。また、モバイル系のCPUコアやGPUコアのIPベンダーを取り込んだことで、ヘテロジニアスコンピューティングのスタンダードを狙う道も見えて来ている。

 HSAの抱える現在の問題の1つはスピードだ。迅速に各技術要素を定義して実装して行かなければ、時流に乗り遅れる。牽引役のAMD自身のHSAのハードウェア実装がかなりゆっくりとしたペースなのも問題だ。ハードウェア側が準備完了にならないと、HSAソフトウェアスタックを完成させることができない。HSA Foundationの他のメンバーはともかく、AMDは急がなければならない。

(後藤 弘茂 (Hiroshige Goto)E-mail