FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

MICRO 2021の論文を読む (Software Defined Vector (2))

MICRO 2021 の論文が Free Access になっているので、興味のあるものを読んでいくことにした。 Software-Defined Vectorの続き。メモリアクセスを頑張っているようだが、ここまで分散させているとちゃんとコンシステンシが取れるのか不思議に思えてくる。

https://dl.acm.org/doi/10.1145/3466752.3480099

www.microarch.org


2.3 ベクトルメモリアクセス

単一コアでの小さなメモリアクセスリクエストを集約して大きなベクトルメモリアクセスとすることで性能向上を目指す。実際のメモリアクセスはスカラコアに依存している。

  1. スカラコアが先に実行してデータをベクトルコアに供給する。
  2. スカラコア上で実行されるワイドロード命令をサポートする。
2.3.1 ベクトル・グループのためのDecoupled Access/Execute

Decoupled Access/Executeでは、スカラコアが先にメモリアクセスを実行してデータをロードし、ベクトルコアに配信する。

この方式では、ベクトルコアのローカルスクラッチパッドメモリをベクトルへの転送用のキューとして使用する。このキューでは、読み込まれたデータをフレームと呼ばれるメモリの塊に整理する。各フレームでは、マイクロスレッドが消費する必要のあるデータが含まれている。

図3は、フレームキューの論理構成を示したものです。スカラコアはロードを発行してフレームを生成し、ベクターコアはフレームを生成順に消費しますが、フレーム内にデータが到着する順番は関係ありません。ベクトルコアは、フレームサイズの領域の円形バッファをスクラッチパッドに保持します。図4は、この円形バッファの状態の一例を示しています。コアがキューの先頭から読み出している間に、メモリシステムは同時に未来のフレームをファイリングします。

図3は、フレームキューの論理構造を示したものである。

  1. スカラコアがロード命令を発行する。このときにフレームを生成する。
  2. ベクトルコアはフレームを生成順に消費する。ベクトルコアはフレームと同じサイズのリングバッファをスクラッチパッドに保持する。
  3. ベクトルコアがキューの先頭からデータを読みだしている間に、メモリシステムは同時に将来のフレームを取り込む。

ベクトルグループを形成する前に、フレームサイズとフレームの全体サイズをCSRに書き込むことですべてのコアはフレームを構成する。この作業は、論理フレームキューをスクラッチパッド上に構成することで実現する。

f:id:msyksphinz:20211026235834p:plain
図3. フレームとマイクロスレッドの論理ビュー。ベクトルコアはフレーム0(F0)を読み込み、マイクロスレッド0(M0)を実行する。同時に、スカラコアは次のフレーム(F1)のリクエストを発行し、次のマイクロスレッド(M1)の初期化を行う。
f:id:msyksphinz:20211026235859p:plain
図4. 4ワードを含む4つのフレームキューの構成例。古いフレームはすでに取り除かれている。最初のフレームはすでに解放されており、メモリを待っている。ベクトルコアは現在2番目のフレームにアクセスしている。3番目と4番目のフレームは一部埋められている(データは到着している途中である)、ベクトルコアはまだこのデータを読むことは出来ない。

マイクロスレッドコード内では、ベクトルコアがそれぞれのスクラッチパッドからフレームを消費する。frame_start命令によって、キューの先頭にあるフレームの準備が整うまで、現在のマイクロスレッドをストールさせる。フレームの消費が終わると、ベクトルコアは現在のフレームを解放するためにremem命令を使用する。

C言語レベルでは、ベクターコアでのフレームの使用は次のようになる。

int frame_ptr = FRAME_START ();
int a = spad[frame_ptr + 0];
int b = spad[frame_ptr + 1];
int c = a + b;
REMEM ();

このDecoupled Access/Execute機構は、ロードパスにのみ使用され、ストアでは使用されない。

2.3.2 ワイドベクトルロード

ベクトルグループのロードを集中管理することにより、データの連続したチャンクをまとめて、ベクトル命令によってまとめロードするためである。このアーキテクチャでは、スカラコアがベクトルグループに変わってロード命令を実行する。ベクトルロードは、LLCにキャッシュラインを要求し、キャッシュデータをベクトルコアに1チャンクずつ分配する。

ノンブロッキングベクトルアクセスの多面新しい命令を追加する。

vload sp, add, off, width, var

オペランドの定義:

  1. 受信側スクラッチパッドのオフセット
  2. ソースメモリアドレス
  3. 最初の応答を受けるベクトルグループのコアのオフセット
  4. ベクトルコアごとのアクセス幅
  5. バリアント

vloadはデフォルトではAlignedキャッシュラインのみをサポートしているが、命令を組み合わせることでUnalignedロードもサポートしている。

ベクトルロードは、アクセスされたキャッシュラインの各部分をどこに送るかをLLCに指示するための3つのバリアントをサポートしている。

  • Single: 1つのベクターコアにラインの一部を送信する
  • Group: ベクトルグループ内の各コアにラインの連続した部分を送信する
  • Self: すべてのデータをリクエストを送信したコアに送り返す

図5は、各バリアントのリクエストとレスポンスの経路を示したものです。

f:id:msyksphinz:20211026235949p:plain
図5. スカラコアはリクエストをキャッシュラインに送信し、ベクトルコアに対して複数のレスポンスを送信する。(左側) Single : 幅2のデータをロードする、(中央) Group : 幅1のデータをフェッチする、(右側) Group : 幅2のデータをロードする。

サポートされているバリアントで、多くのパタンを効率的にマッピングできることが分かった。以下のようなプログラムを考えてみる。

for (int i = tid; i < N; i += VLEN)
    for (int j = 0; j < M; j += FLEN)
        c[i] += a[i*M+j];

各ベクトルコアはj次元の和を個別に実行する。スカラ―コアは、通常ならば以下のようにメモリアクセスを発行する必要がある。

for (int core = 0; core < VLEN; core ++)
    vload sp , a[(i+core)*N+j], core , FLEN , SINGLE;

しかし、作業分担の方法を変えれば、より効率的な方法を採用することができる。

for (int i = 0; i < N; i++)
    for (int j = tid*FLEN; j < M; j += FLEN*VLEN)
        c_partial [i] += a[i*M+j];

スカラーコアは、j の連続したチャンクを各コアにフェッチして、部分的な和を生成する。この場合、グループロードを使用して、FLEN*VLEN相当のデータをフェッチすることができる。

追加コストとして、部分和のリダクションが必要になりますが、これはある程度安価に行うことができます。

単一レベルのループネストなどでは、どのような方法でもよいのですが、その選択は妥当性よりも性能によります。グループロードは、スカラ命令やメモリ要求が少なくて済むので、より効率的です。

2.4 ベクトルOddとEnd

ベクトルグループは、古典的なベクトルマシンをエミュレートして、predicate, gather/scatter/shuffleなどのベクトル操作をサポートする。

Predicateにより、ベクトルマシンは制御フローを分岐させることなく条件付き実行を行うことができる。マイクロスレッドは各ベクトルコア上の1ビットフラグを使用してPredicateをサポートしている。

pred_eq rs1, rs2
pred_neq rs1, rs2

flagが0の場合、ベクトルコアは後続のpredicate命令によってflagが1になるまで、すべての命令をnopとして実行する。

if (cond == 1) { c = a + b; }

上記のC言語コードは、このように実装することができる。

PRED_EQ(cond , 1);
c = a + b;
PRED_EQ (0, 0);

Scatter/Gather命令は、ベクトルグループ内で、各コアがワードサイズのメモリ操作を行うことで実現でき切る。各アクセスはノンブロッキングで、各コンポーネントのメモリアクセスを待つためにベクトルグループ全体がストールすることを防ぐ。

Shuffle命令は、他のコアのスクラッチパッドを使用して、リモートストアを行う(メニーコアアーキテクチャでは一般的な機能である)。