Weitere ähnliche Inhalte Ähnlich wie PBL1-v1-010j.pptx (20) PBL1-v1-010j.pptx1. CPU GPU
Ultimate CGRA w/ high-speed compiler
CGRA for Energy-efficient Cryptography
Beyond-Neuromorphic Systems
Non-Deterministic Computing
1
ナレータ VOICEVOX:もち子(cv 明日葉よもぎ)
はらぺこエンジニアに贈るCGRAの世界2022
(10. 高速コンパイラ編)
4. 20220819
4
FMA C+B*A ⇒ D
C has location info (next unit)
D has location info (same as C)
Sequential updates
7. 20220202
7
命令テンプレート(自由記述は非効率、超CISC的表現が必要)
cex(OP_CEXE, &ex0-9, c3, c2, c1, c0, 16bit-pattern)
c3,c2,c2,c1は各々64bit値であり,bit32を連結した4bitと,bit0を連結した4bitにより,各々,16bit-patternのbit位置を
取り出した結果が,ex[0-9]のbit1およびbit0に格納される.ex0-9のbit1は条件付きストアの上位32bit,bit0は条件付きス
トアの下位32bitに対応する.
exe(OP_X, &var|&AR[0-63][0-3], s1, e1, s2, e2, s3, e3, OP_Y, s4, OP_Z, s5)
ex4(OP_X, &var|&AR[0-63], s1, e1, s2, e2, s3, e3, OP_Y, s4, OP_Z, s5)
varまたはAR[0-63][0-3]はALUの演算結果格納先であり,前者は位置情報無し,後者は行列位置情報に対応する.s1か
らs3の各64bit値は,各々,e1からe3による修飾後に演算器に入力される.修飾子は以下の通り.
EXP_H3210: 無加工
EXP_H1010: 下位32bitを上位+下位32bitにコピー
EXP_H3232: 上位32bitを上位+下位32bitにコピー
EXP_B5410: byte5,4,1,0を各々16bitに拡張し連結
EXP_B7632: byte7,6,3,2を各々16bitに拡張し連結
OP_Xには主に算術演算,OP_Yには主に論理演算,OP_Zには主にシフト演算を記述できる.各演算はSIMD型であり,上
位32bitと下位32bitを独立に扱う.exe()は倍幅、ex4()は8倍幅のSIMDである.
8. 20220202
8
命令テンプレート
exe(OP_X, &var, INIT0?var:var, e1, s2, e2, s3, e3, OP_Y, s4, OP_Z, s5)
INIT0?var:varは,C言語としては常にvarであるため冗長である.これは,CGRAにおいてホストの介入無しに多重ループ
に対応する工夫である.多重ループ起動前に,ホストは,内側ループに関する各初期値をCGRA内にセットする.通常,
内側ループ完了時にホストが介入して内部レジスタの再初期化を行う必要がある.しかし,IMAXでは,INIT0?var:varが
記述された変数は,CGRAが自ら再初期化を行い,ホスト介入のオーバヘッドを排除している.具体的には,varに初期値
がセットされていることを前提に,LOOP0の初回(INIT0=1)は,ホストが予めセットした当該初期値を演算器の第1入力とし,
次回以降は演算器出力を第1入力とするようデータパスを切替える.
exe(OP_X, &var, var, e1, INIT0?s2:0, e2, s3, e3, OP_Y, s4, OP_Z, s5)
これも,CGRAにおいて,ホストの介入無しに多重ループに対応する工夫である.INIT0?s2:0が記述されている場合,
LOOP0の初回(INIT0=1)は,s2を演算器の第2入力とし,次回以降は0を第2入力とするようデータパスを切替える.2次元
サブアレイの先頭アドレス計算に利用できる.
9. 20220202
9
命令テンプレート
mex(OP MEX2, &s2, INIT0?s20:s2, INIT0?0:expr, OP MEX1, &s1, INIT0?s10:s1,
INIT0?0:expr, limit, BR[0-63][0-3][1], BR[0-63][0-3][0])
ホストの介入無しに多重ループ疎行列計算またはマージソートを行うためのアドレス計算補助記述.INIT0?s20:s2 および
INIT0?s10:s1 はベースアドレス,INIT0?0:expr は加算するオフセットに対応する.LOOP0 の初回 (INIT0=1) は,s2 と s1に,
s20とs10(初期値)が格納され,次回以降は,前回ロー カルメモリから読み出した 2 つの64bitデータの上位32bitを比較し,
大小関係によって,s2またはs1,す なわちアドレス計算器出力に,0またはexprを加算した結果が格納される.また,limit
はマージソート用の比較対象間距離である.使用方法はプログラム例を参照のこと.OP_MEX は以下の通り.
OP_NOP: 常にbase
OP_ALWAYS: 常にbase+offset
OP_CMPA_LE: 上位32bit(BR[][][1])≦上位32bit(BR[][][0])ならbase+offset.それ以外はbase
OP_CMPA_GE: 上位32bit(BR[][][1])≧上位32bit(BR[][][0])ならbase+offset.それ以外はbase?_
10. 20220202
10
命令テンプレート
mop(OP_X, ex9-0, &src|&dst, base, offset, mask, top, len, block, force, ptop, plen)
mo4(OP_X, ex9-0, &src|&dst, base, offset, mask, top, len, block, force, ptop, plen)
ローカルメモリに対するロードまたはストアを記述する.各項目は以下の通り.
OP_X: データ幅に応じた倍幅SIMD型ロード命令またはストア命令.mo4は8倍幅SIMDである
ex0-9: 無条件ストアの場合は定数3,前述の条件付きストアの場合は変数
src|dst: ロードの場合は格納先レジスタ,ストアの場合はストアデータ
base: 参照するメモリアドレス base+mask(offset) のbase部分.ホストの主記憶アドレスをそのまま使える.ローカルメモリ内アドレスへはコンパイラと
ハードウェアの連携により自動変換するので,アドレス変換を意識する必要はない
offset: 同じくoffset部分.offsetの単位は1byteである点に注意
mask: 最終的なアドレスは,baseに,offsetレジスタをmaskにより修飾した値を加えたもの.MSK_B0:bit7-0、MSK_B1:bit15-8、MSK_B2:bit23-16、
MSK_B3:bit31-24、MSK_B4:bit39-32、MSK_B5:bit47-40、MSK_B6:bit55-48、MSK_B7:bit63-56、MSK_H0:bit15-0、MSK_H1:bit31-16、
MSK_H2:bit47-32、MSK_H3:bit63-48、MSK_W0:bit31-0、MSK_W1:bit63-32、MSK_D0:bit63-0
top: バースト演算が参照するホスト主記憶の先頭アドレス.先頭アドレスと長さを用いてホストDMAコントローラがホスト主記憶-LMM間転送を行う
len: 同じく長さ.単位は1word(4byte)である.なお,DMA転送速度は256bit/cycleである.lenに定数0を指定した場合,topにより指定された領域が
確保されるものの,DMAは抑止される.この指定は,同一LMMによるダブルバッファリングを行う際に使用する.
block: IMAXでは使用しない(EMAX5ではDMAギャザ機能のパラメタ)
force: ロードとストアとで動作が異なる
0: ロードの場合,前回DMAの先頭アドレスおよび長さが同一であればDMAを起動せずLMMを再利用する.ストアの場合,バースト演算後に
LMMからホスト主記憶にデータ転送する.ただし,次回バースト演算と同時の遅延ドレインが指定されている場合はDMAを抑止する.
1: ロードの場合,必ずホスト主記憶からLMMにデータ転送する.外部機器入力のように,アドレスが同じでも内容が異なる場合に使用する.
ストアの場合,パーシャルストア(一部アドレスのみストア)を想定し,バースト演算前にホスト主記憶から一旦LMMにデータ転送する.ただし,
前回ストアとアドレス範囲が同じ場合はDMAを抑止する.
ptop: バースト演算中に行うDMAの先頭アドレス.ロードの場合,バースト演算中に次回バースト演算に必要な入力がホスト主記憶からLMMに転送
される(プリフェッチ).ストアの場合,バースト演算中に前回バースト演算結果がLMMからホスト主記憶に転送される(遅延ドレイン).なお,
mapdist=0 の場合,同一 LMM を対象に,LD/ST とプリフェッチ/ドレインが同時動作する.
plen: 同じく長さ.単位は1word(4byte)である.
11. 20220202
11
コンパイル過程 src/conv-mark/conv-mark の入力
void tone_curve(r, d, t)
unsigned int *r, *d;
unsigned char *t;
{
#if !defined(EMAX5) && !defined(EMAX6)
int j;
for (j=0; j<WD; j++) {
*d = ((t)[*r>>24])<<24 | (t[256+((*r>>16)&255)])<<16 | (t[512+((*r>>8)&255)])<<8;
r++; d++;
}
#else
Ull t1 = t;
Ull t2 = t+256;
Ull t3 = t+512;
Ull BR[16][4][4]; /* output registers in each unit */
Ull r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15;
Ull r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, r26, r27, r28, r29, r30, r31;
int loop=WD;
//EMAX5A begin tone_curve mapdist=0
while (loop--) {
mop(OP_LDWR, 1, &BR[0][1][1], (Ull)(r++), 0LL, MSK_D0, (Ull)r, 320, 0, 0, (Ull)NULL, 320); /* stage#0 */
mop(OP_LDUBR, 1, &BR[1][1][1], (Ull)t1, BR[0][1][1], MSK_B3, (Ull)t1, 64, 0, 0, (Ull)NULL, 64); /* stage#1 */
mop(OP_LDUBR, 1, &BR[1][2][1], (Ull)t2, BR[0][1][1], MSK_B2, (Ull)t2, 64, 0, 0, (Ull)NULL, 64); /* stage#1 */
mop(OP_LDUBR, 1, &BR[1][3][1], (Ull)t3, BR[0][1][1], MSK_B1, (Ull)t3, 64, 0, 0, (Ull)NULL, 64); /* stage#1 */
exe(OP_MMRG, &r1, BR[1][1][1], EXP_H3210, BR[1][2][1], EXP_H3210, BR[1][3][1], EXP_H3210, OP_NOP, 0LL, OP_NOP, 0LL);
mop(OP_STWR, 3, &r1, (Ull)(d++), 0LL, MSK_D0, (Ull)d, 320, 0, 0, (Ull)NULL, 320); /* stage#2 */
}
//EMAX5A end
#endif
}
filter+rmm.c
12. 20220202
12
コンパイル過程 cpp –P の入力
filter+rmm.c-mark.c
void tone_curve(r, d, t)
unsigned int *r, *d;
unsigned char *t;
{
#if !defined(EMAX5) && !defined(EMAX6)
int j;
for (j=0; j<WD; j++) {
*d = ((t)[*r>>24])<<24 | (t[256+((*r>>16)&255)])<<16 | (t[512+((*r>>8)&255)])<<8;
r++; d++;
}
#else
Ull t1 = t;
Ull t2 = t+256;
Ull t3 = t+512;
Ull BR[16][4][4]; /* output registers in each unit */
Ull r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15;
Ull r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, r26, r27, r28, r29, r30, r31;
int loop=WD;
#define printf(format,...)
/-EMAX5AB-/ tone_curve 0
/-EMAX5AS-/ while (loop--) {
/-EMAX5AS-/ mop(OP_LDWR, 1, &BR[0][1][1], (Ull)(r++), 0LL, MSK_D0, (Ull)r, 320, 0, 0, (Ull)NULL, 320); /* stage#0 */
/-EMAX5AS-/ mop(OP_LDUBR, 1, &BR[1][1][1], (Ull)t1, BR[0][1][1], MSK_B3, (Ull)t1, 64, 0, 0, (Ull)NULL, 64); /* stage#1 */
/-EMAX5AS-/ mop(OP_LDUBR, 1, &BR[1][2][1], (Ull)t2, BR[0][1][1], MSK_B2, (Ull)t2, 64, 0, 0, (Ull)NULL, 64); /* stage#1 */
/-EMAX5AS-/ mop(OP_LDUBR, 1, &BR[1][3][1], (Ull)t3, BR[0][1][1], MSK_B1, (Ull)t3, 64, 0, 0, (Ull)NULL, 64); /* stage#1 */
/-EMAX5AS-/ exe(OP_MMRG, &r1, BR[1][1][1], EXP_H3210, BR[1][2][1], EXP_H3210, BR[1][3][1], EXP_H3210, OP_NOP, 0LL, OP_NOP, 0LL);
/-EMAX5AS-/ mop(OP_STWR, 3, &r1, (Ull)(d++), 0LL, MSK_D0, (Ull)d, 320, 0, 0, (Ull)NULL, 320); /* stage#2 */
/-EMAX5AS-/ }
/-EMAX5AE-/
#undef printf
#endif
}
13. 20220202
13
コンパイル過程 src/conv-c2c/conv-c2c の入力
filter+rmm.c-cppo.c
void tone_curve(r, d, t)
unsigned int *r, *d;
unsigned char *t;
{
Ull t1 = t;
Ull t2 = t+256;
Ull t3 = t+512;
Ull BR[16][4][4];
Ull r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15;
Ull r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, r26, r27, r28, r29, r30, r31;
int loop=WD;
/-EMAX5AB-/ tone_curve 0
/-EMAX5AS-/ while (loop--) {
/-EMAX5AS-/ mop(0x02, 1, &BR[0][1][1], (Ull)(r++), 0LL, 14, (Ull)r, 320, 0, 0, (Ull)((void *)0), 320);
/-EMAX5AS-/ mop(0x07, 1, &BR[1][1][1], (Ull)t1, BR[0][1][1], 3, (Ull)t1, 64, 0, 0, (Ull)((void *)0), 64);
/-EMAX5AS-/ mop(0x07, 1, &BR[1][2][1], (Ull)t2, BR[0][1][1], 2, (Ull)t2, 64, 0, 0, (Ull)((void *)0), 64);
/-EMAX5AS-/ mop(0x07, 1, &BR[1][3][1], (Ull)t3, BR[0][1][1], 1, (Ull)t3, 64, 0, 0, (Ull)((void *)0), 64);
/-EMAX5AS-/ exe(0x25, &r1, BR[1][1][1], 3, BR[1][2][1], 3, BR[1][3][1], 3, 0x00, 0LL, 0x00, 0LL);
/-EMAX5AS-/ mop(0x12, 3, &r1, (Ull)(d++), 0LL, 14, (Ull)d, 320, 0, 0, (Ull)((void *)0), 320);
/-EMAX5AS-/ }
/-EMAX5AE-/
}
14. 20220202
14
コンパイル過程 通常のARM-Cコンパイラの入力 1/3
filter+rmm-emax6.c ../../src/conv-c2c/emax6.h ../../src/conv-c2c/emax6lib.c
void tone_curve(r, d, t)
unsigned int *r, *d;
unsigned char *t;
{
Ull t1 = t;
Ull t2 = t+256;
Ull t3 = t+512;
Ull BR[16][4][4];
Ull r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, r26, r27, r28, r29, r30, r31;
int loop=WD;
volatile emax6_conf_tone_curve();
emax6.lmmio = emax6.lmmic;
emax6.lmmic = 1-emax6.lmmic;
emax6.mapdist = 0;
*(Uint*)&emax6.lmmi[0][0][1][emax6.lmmic] = 0x013f0001|(0<<2);
emax6.lmmi[0][0][1][emax6.lmmic].ofs = 0; emax6.lmmi[0][0][1][emax6.lmmic].top = r;
*(Uint*)&emax6.lmmi[0][1][1][emax6.lmmic] = 0x003f0001|(0<<2);
emax6.lmmi[0][1][1][emax6.lmmic].ofs = 0; emax6.lmmi[0][1][1][emax6.lmmic].top = t1;
*(Uint*)&emax6.lmmi[0][1][2][emax6.lmmic] = 0x003f0001|(0<<2);
emax6.lmmi[0][1][2][emax6.lmmic].ofs = 0; emax6.lmmi[0][1][2][emax6.lmmic].top = t2;
*(Uint*)&emax6.lmmi[0][1][3][emax6.lmmic] = 0x003f0001|(0<<2);
emax6.lmmi[0][1][3][emax6.lmmic].ofs = 0; emax6.lmmi[0][1][3][emax6.lmmic].top = t3;
*(Uint*)&emax6.lmmi[0][2][0][emax6.lmmic] = 0x013f0003|(0<<2);
emax6.lmmi[0][2][0][emax6.lmmic].ofs = 0; emax6.lmmi[0][2][0][emax6.lmmic].top = d;
emax6.lmmi_bitmap[0] = 0x0000000000000004LL;
emax6.lmmi_bitmap[1] = 0x0000000000000003LL;
emax6.lmmi_bitmap[2] = 0x0000000000000002LL;
emax6.lmmi_bitmap[3] = 0x0000000000000002LL;
emax6_pre_with_drain_cache();
get_nanosec(NANOS_ARM);
if (emax6.last_conf == emax6_conf_tone_curve) {
emax6.status = STATUS_DRAIN;
emax6_check_lmmi_and_dma(0, 1, 0, 0, 2, 0);/*drain*/
}
get_nanosec(NANOS_DRAIN);
LMMのアドレス範囲情報
必要に応じて前回演算結果の回収
15. 20220202
15
コンパイル過程 通常のARM-Cコンパイラの入力 2/3
if (emax6.last_conf != emax6_conf_tone_curve) {
Dll *dst, *src;
int i,j;
emax6.status = STATUS_CONF;
emax6.last_conf = emax6_conf_tone_curve;
emax6.lastdist = 0;
dst = (Dll*)(((struct reg_ctrl*)emax6.reg_ctrl)->i[0].conf);
src = (Dll*)emax6_conf_tone_curve;
for (i=0; i<sizeof(conf)/sizeof(Dll); i++)
*dst++ = *src++;
for (i=0; i<64; i++) {
for (j=0; j<4; j++)
emax6.lmmi[0][i][j][emax6.lmmio].v = 0;
}
while (((struct reg_ctrl*)emax6.reg_ctrl)->i[0].stat & 0xf0); //LMRING_BUSY
}
get_nanosec(NANOS_CONF);
emax6.status = STATUS_REGV;
((struct reg_ctrl*)emax6.reg_ctrl)->i[0].breg[63][0].br[0] = loop;
((struct reg_ctrl*)emax6.reg_ctrl)->i[0].breg[63][0].br[1] = -1LL;
((struct reg_ctrl*)emax6.reg_ctrl)->i[0].addr[0][1].ea1b = (Ull)r;
((struct reg_ctrl*)emax6.reg_ctrl)->i[0].addr[0][1].ea1o = (Ull)0LL;
((struct reg_ctrl*)emax6.reg_ctrl)->i[0].addr[1][1].ea1b = (Ull)t1;
((struct reg_ctrl*)emax6.reg_ctrl)->i[0].addr[1][2].ea1b = (Ull)t2;
((struct reg_ctrl*)emax6.reg_ctrl)->i[0].addr[1][3].ea1b = (Ull)t3;
((struct reg_ctrl*)emax6.reg_ctrl)->i[0].addr[2][0].ea0b = (Ull)d;
((struct reg_ctrl*)emax6.reg_ctrl)->i[0].addr[2][0].ea0o = (Ull)0LL;
get_nanosec(NANOS_REGV);
emax6.status = STATUS_RANGE;
{struct reg_ctrl *reg_ctrl = emax6.reg_ctrl;
Uint lmmic = emax6.lmmic;
*(Ull*)&(reg_ctrl->i[0].addr[0][1].top)=((Ull)(emax6.lmmi[0][0][1][lmmic].top+*((Ushort*)&emax6.lmmi[0][0][1][lmmic]+1)*sizeof(Uint)+(sizeof(Uint)-1))<<32)|(Ull)(Uint)emax6.lmmi[0][0][1][lm
*(Ull*)&(reg_ctrl->i[0].addr[1][1].top)=((Ull)(emax6.lmmi[0][1][1][lmmic].top+*((Ushort*)&emax6.lmmi[0][1][1][lmmic]+1)*sizeof(Uint)+(sizeof(Uint)-1))<<32)|(Ull)(Uint)emax6.lmmi[0][1][1][lm
*(Ull*)&(reg_ctrl->i[0].addr[1][2].top)=((Ull)(emax6.lmmi[0][1][2][lmmic].top+*((Ushort*)&emax6.lmmi[0][1][2][lmmic]+1)*sizeof(Uint)+(sizeof(Uint)-1))<<32)|(Ull)(Uint)emax6.lmmi[0][1][2][lm
*(Ull*)&(reg_ctrl->i[0].addr[1][3].top)=((Ull)(emax6.lmmi[0][1][3][lmmic].top+*((Ushort*)&emax6.lmmi[0][1][3][lmmic]+1)*sizeof(Uint)+(sizeof(Uint)-1))<<32)|(Ull)(Uint)emax6.lmmi[0][1][3][lm
*(Ull*)&(reg_ctrl->i[0].addr[2][0].top)=((Ull)(emax6.lmmi[0][2][0][lmmic].top+*((Ushort*)&emax6.lmmi[0][2][0][lmmic]+1)*sizeof(Uint)+(sizeof(Uint)-1))<<32)|(Ull)(Uint)emax6.lmmi[0][2][0][lm
}
get_nanosec(NANOS_RANGE);
命令写像が前回と異なる場合は
再写像
AXIIF/PIOによるレジスタ初期化
LMMにアドレス範囲情報書き込み
16. 20220202
16
コンパイル過程 通常のARM-Cコンパイラの入力 3/3
emax6.status = STATUS_LOAD;
emax6_check_lmmi_and_dma(0, 2, emax6.lastdist, 0, 0, 1);/*load*/
emax6_check_lmmi_and_dma(0, 2, emax6.lastdist, 0, 1, 1);/*load*/
emax6_check_lmmi_and_dma(0, 2, emax6.lastdist, 0, 1, 2);/*load*/
emax6_check_lmmi_and_dma(0, 2, emax6.lastdist, 0, 1, 3);/*load*/
get_nanosec(NANOS_LOAD);
((struct reg_ctrl*)emax6.reg_ctrl)->i[0].cmd = 3LL; // EXEC
{struct reg_ctrl *reg_ctrl = emax6.reg_ctrl;
Uint lmmic = emax6.lmmic;
}
emax6.lmmd[2][0] = 0xff>>7;
while (((struct reg_ctrl*)emax6.reg_ctrl)->i[0].stat); //LMRING_BUSY|EXRING_BUSY
get_nanosec(NANOS_EXEC);
asm volatile("b emax6_conf_end_tone_curven"
".align 5n"
".global emax6_conf_tone_curven"
"emax6_conf_tone_curve:n"
" .word 0x031e0003, 0x00000000n"
" .word 0xffff0000, 0x00000000n"
" .word 0x00000000, 0x00000000n"
:
" .word 0xffff0000, 0x00000000n"
" .word 0x00000000, 0x00000000n"
" .word 0x00000000, 0x00000000n"
".global emax6_conf_end_tone_curven"
"emax6_conf_end_tone_curve:n"
);
}
AXIIF/DMAによる
LMMデータ書き込み
AXIIF/PIOによるIMAX起動
演算と同時の次データ転送が
あればDMA起動
UNITのconfiguration情報
17. ld @(gr1, 0) -> gr2
add gr1, 4 -> gr1
sub gr3, 1 -> gr3, z
VLIW0
add sub
gr1 gr3z
Register File
gr1 4 gr3 1
eag
gr1 4
0 1 2 3 4 5 6
0 0 0 0 0 0 0
0 1 2 3 4 5 6
0 0 0 0 0 0 0
0 1 2 3 4 5 6
0 0 0 0 0 0 0
0 1 2 3 4 5 6
0 0 0 0 0 0 0
0 1 2 3 4 5 6
0 0 0 0 0 0 0
z
0
z
0
z
0
z
0
z
0
gr2
ld
Propagation skip table
gr/cr
Stage
0
1
2
3
4
20220202
17
コンパイルが速いのは、非探索的だから
18. ld @(gr4, 0) -> gr5
add gr4, 4 -> gr4
bz end
VLIW1
add sub
gr1 gr3z
add
gr4
Register File
gr1 4
gr4 4
gr3 1 gr4
bz
end
eag
eag
gr1 4
gr4 4
0 1 2 3 4 5 6
0 1 1 1 0 0 0
0 1 2 3 4 5 6
0 1 1 1 0 0 0
0 1 2 3 4 5 6
0 0 1 0 0 0 0
0 1 2 3 4 5 6
0 0 0 0 0 0 0
0 1 2 3 4 5 6
0 0 0 0 0 0 0
z
1
z
1
z
0
z
0
z
0
gr5
ld
gr2
ld
Propagation skip table
gr/cr
Stage
0
1
2
3
4
VLIW1
20220202
18
LAPPと同様の非探索的高速コンパイル手法
19. gr5
ld
gr2
ld
sll gr2, 16 -> gr2
VLIW2
add sub
gr1 gr3z
add
gr4
sll
Register File
gr1 4
gr4 4
16
gr3 1
0 1 2 3 4 5 6
0 1 1 1 1 1 0
0 1 2 3 4 5 6
0 1 1 1 1 1 0
0 1 2 3 4 5 6
0 0 1 0 1 1 0
0 1 2 3 4 5 6
0 0 0 0 0 1 0
0 1 2 3 4 5 6
0 0 0 0 0 0 0
z
1
z
1
z
0
z
0
z
0
gr4
bz
end
eag
eag
gr1 4
gr4 4
Propagation skip table
gr/cr
Stage
0
1
2
3
4
VLIW2
VLIW2
20220202
19
LAPPと同様の非探索的高速コンパイル手法
20. gr5
ld
gr2
ld
or gr2, gr5 -> gr5
VLIW3
add sub
gr1 gr3z
add
gr4
gr2
Register File
or
gr5
gr1 4
gr4 4
gr3 1
0 1 2 3 4 5 6
0 1 1 1 1 1 0
0 1 2 3 4 5 6
0 1 1 1 1 1 0
0 1 2 3 4 5 6
0 0 1 0 1 1 0
0 1 2 3 4 5 6
0 0 1 0 0 1 0
0 1 2 3 4 5 6
0 0 0 0 0 0 0
z
1
z
1
z
0
z
0
z
0
gr4
bz
end
eag
eag
gr1 4
gr4 4
Propagation skip table
gr/cr
Stage
0
1
2
3
4
sll
16
VLIW3
VLIW3
VLIW3
20220202
20
LAPPと同様の非探索的高速コンパイル手法
21. gr5
gr2
ld
st, gr5 -> @(gr6, 0)
add gr6, 4 -> gr6
bra loop
VLIW4
add sub
gr1 gr3z
add
gr4
Register File
gr5 gr5
add
gr6
gr1 4
gr4 4
16
gr6
gr6 4
gr6
gr6
gr3 1
0 1 2 3 4 5 6
0 1 1 1 1 1 0
0 1 2 3 4 5 6
0 1 1 1 1 1 0
0 1 2 3 4 5 6
0 0 1 0 1 1 0
0 1 2 3 4 5 6
0 0 1 0 0 1 0
0 1 2 3 4 5 6
0 0 0 0 0 1 0
z
1
z
1
z
0
z
0
z
0
gr4 gr6
bz
bra
end
loop
eag
eag
eag
gr1 4
gr4 4
gr6 0
st
Propagation skip table
gr/cr
Stage
0
1
2
3
4
gr5
ld
gr2
or
sll
VLIW4
VLIW4
VLIW4
VLIW4
20220202
21
LAPPと同様の非探索的高速コンパイル手法
22. ZYNQ ZCU102 …ホストはARMv8-1.2GHz PL経由でIMAX-150MHzを実装
i7-8650U cross …ホストはx86_64 クロス開発環境
Jetson TX2 …ホストはARMv8-1.2GHz GPU使用,CUDA8.0に移植
Gold6144+V100 …ホストはx86_64 GPU使用,CUDA10.0に移植
(C)はCGLA記述とライブラリを用いた通常コンパイル
(IMAX)はCGLA記述と提案コンパイル手法
書き込みはNFS先,SDcardが影響?
20220202
22
コンパイル時間の比較
23. 20220202
23
コンパイラの中身
/**********************************************************************************************************/
/* Step 1 ... decode[][]登録と伝搬レジスタ */
/* 1-1. 各insn[]のsrc毎に依存関係検査対象を抽出し,insn[].header.rdepと比較・rdepを下方へ更新 */
/* 1.2a 絶対位置指定★row,colがない場合(row=-1) */
/* 1.2b 絶対位置指定★row,colがある場合(row>=0) */
/* 1-3. 先行写像との競合検査 */
/* 1-4. insn[]->decode[][]コピーおよびALU機能割り当て可否検査 */
/* 1-5. busmap+伝搬レジスタ設定●emaxと異なり,更新変数は,同一行別カラムでの参照を禁止(逐次実行との互換) */
/* 1-6. 位置を確定.dst変数の位置情報をid[].row,colに記録 */
/**********************************************************************************************************/
/* Step 2 ... setup conf[][] */
/* 2-1. select EXE-in */
/* 2-2. select CEX-in and EAG-in */
/**********************************************************************************************************/
/* Step 3 ... setup conf[][] */
/* 3-1. select MW-in */
/* 3-2. select BR-in */
/* 3-3. set mapdist */
/**********************************************************************************************************/
/* Step 4 ... Insert LMM-buffering for neighbor LDDMQ. Multiple LDDMQ in the same row is not allowed */
/**********************************************************************************************************/
/* Step 5 ... Merge LMM imm_modeの初期値は,OP_IM_BUFRD/WRを含めて3 */
/* stage毎にcolumn間(3-2,1-0)を検査し,上記機能の干渉に応じてlmmを集約し,IM_PREF/DRAINと整合 */
/* 5-1. mode=0 | mode=0 1-0を検査し,#1/#0空 #0.mode=2/#1.mode=2(merge) */
/* 5-1. = 1-0を検査し,同一なら #1.mode=2�.mode=2(merge) */
/* 5-1. != 1-0を検査し,その他 #1.mode=3,#0.mode=3 */
/* 5-2. mode=0 | mode=0 3-2を検査し,#3/#2空 #2.mode=2/#3.mode=2(merge) */
/* 5-2. = 3-2を検査し,同一なら #3.mode=2.mode=2(merge) */
/* 5-2. != 3-2を検査し,その他 #3.mode=3,#2.mode=3 */
/* 5-3. mode=0,0 | mode=0,0 (3-2)と(1-0)を検査し,#2.mode=2/#0.mode=2&空有なら,mode=1(merge) */
/* 5-3. mode=2 = mode=2 (3-2)と(1-0)を検査し,#2.mode=2�.mode=2&同一なら,mode=1(merge) */
/* 5-3. != (3-2)と(1-0)を検査し,その他, そのまま */
/* 5-4. mapdist!=0とmop=IM_PREF/DRAINの検出により対応IMの調整が可能 */
/* 一通りmode拡張後,最後に,幅の狭いほうにあわせて再分割する */
/**********************************************************************************************************/
/* Step 6 ... Set additional copy-flag for Vertical Broadcast(slave) */
/* 上から探して後段のvcopyを1にすると,lmf+lmxの場合,lmxがbitmapから消され,結果がDRAINされない */
/* 下から探して前段のvcopyを1にするのが正しい */
/**********************************************************************************************************/
/* Step 7 ... emit EMAX6 SC (soft-CGRA for manycore) */
/**********************************************************************************************************/
/* Step 8 ... emit EMAX6 CGRA */
/**********************************************************************************************************/
Hinweis der Redaktion 番組の途中ですが、コンパイラ、どうなってんの? という問い合わせが多いので、高速コンパイラ編です。なぜ、CGRAのコンパイルに、1秒しかかからないのか、仕組みを説明しましょう。 図、aからfは,アイマックスが対応している計算パターンをまとめたものです。山形はALU,長方形はローカルメモリを示していて,プログラムの視点では,4列構成です。aは,0の要素が少ない密行列を、8並列のSIMD命令で処理するパターンです。青色メモリから,一度に配列Aの連続8要素を読み出します。同様に,きいろメモリから配列Bの連続8要素,オレンジから配列Cの連続8要素を読み出します。そして,4個の3入力ALUが,ABCの各2要素を使って,A+B掛けるCを計算し,連続8要素の計算結果を赤色メモリに書き込みます。つまり,CGRAの1行あたり,8組のA+B掛けるCを計算します。bは,各ぎょう各メモリの離れた2箇所から,連続2要素を読み出し,縦に送って,多くの積和演算を行うパターンです。1列目と2列目のメモリを共有すれば,各配列の離れた4箇所から読み出せます。さらに,横1列のメモリを全て共有すれば,配列の離れた8箇所から読み出せます。cも,縦に送って,多くの積和演算を行うパターンです。ただし,最後の結果をメモリに書き込む時に,以前のあたいに1回だけ累算し,つぎの場所に移動します。メモリから読み出して,ぐるっと回して同じ場所に1回だけ書き戻します。dは,メモリの内容に1回だけ累算するのではなく,パイプライン演算器自身の演算結果を毎回戻して累算します。マルチスレッディングは,特にこのパターンにおいて,演算器稼働率を25%から100%に引き上げる仕組みです。eは,確率的積和演算のパターンです。アイマックスのデータパスはよく考えられていて,確率的計算機能を自然に入れることができます。配列AとBから、連続8ワードを読み出すことで,各8ビットのあたいを32個一度に読み出します。そして,8個ずつ,4つの確率的積和演算器に投入し,全ての結果を加算したあたいをメモリに書き込みます。fは,そ行列計算のパターンです。あらかじめ,64ビットの上位に要素番号,下位に要素値を格納することで,0の要素が多いそ行列を密行列に圧縮しておきます。アイマックスは,CGRAならではの,とても巧妙な、アドレス同調機構を使って,圧縮した行列どうしの行列積を計算します。マージソートもできます。メモリの有効利用と高速化が同時にできます。 図の青い部分が,アイマックスの論理ユニット1つ分です。ALU、2個のアドレス生成器、および、デュアルポートローカルメモリがあります。横に4つ並べて,1行4列分です。Bのランダムアクセスのプログラムを普通に書くと、配列ABCDの名前だけを用いて,2要素毎に“DイコールC+B掛けるA”を計算するプログラムになります。この場合、コンパイラは、アイマックスの1行目に、ロードA、ロードB、および、ロードCをセットし、2行目に、ABCを入力とする積和演算FMA、および、ストアDをセットします。普通のCGRAと同じように、上から下への単純なデータフローが形成されます。 一方、この図は,配列ABCを用いて,2要素毎に“DイコールC+B書けるA”を計算し,結果Dを配列Cに書き戻すケースです。ロードの格納先C、および、FMAの結果を一時格納する変数Dに、次のユニットを使うよう、位置情報を足すと、配列Cへのロードとストアを同時に行います。コンパイラは、アイマックスの1行目にロードA、ロードBをセットし,2行目にロードCをセットします。積和演算FMAの結果を、一時格納する変数Dが,Cと同一ぎょうに指定された場合、演算器の入力ABCは、一度、おなじぎょうの下側のレジスタに送られた後、演算器入力へ戻されます。配列Cに対するDのストアも、同一ぎょうにセットされるため、同じローカルメモリの配列Cに対する、ロード、演算、ストアとなります。
次の図は,1行に全てを収容するケースです。デュアルポートローカルメモリは,物理的には,アイマックスの1行につき、1個だけ装備されていて、じぶんかつたじゅうにより、最大4列分の独立した論理メモリに見せています。このため、まえのケースでは、1つの物理ローカルメモリを配列AとBで共有したのに対し、このケースでは、配列Cも共有します。おのおのの配列が使用可能なメモリ空間は減少します。それでも、1行に収容するメリットがある場合は、ロードの格納先Cには、位置情報を付加せず、FMAの結果を一時格納する変数Dのみに、位置情報を付加します。そうすると、ロードA、ロードB、ロードC、積和演算FMA、配列Cに対するDのストアが、すべて、おなじぎょうに写像されます。
最後の図は、一時変数Dを使用せず、積和演算FMAを“CイコールC+B掛けるA”と記述したケースです。ストアCには位置情報を付加しません。ローカルメモリにストアしたデータを、再度ロードする、積和演算の繰り返しになります。しかし、ストア後に、同一アドレスからロードして、演算に回す計算は、とても時間がかかるので、CPUでも回避しなければならないハードウェア動作の1つです。一方で、CGRAの場合、この動作は、演算器内のアキュムレート機能により、時間をかけずに容易に吸収できます、前のケースとの違いは、FMAが、単純なロード、演算、ストアではなく、入力C0が、ループの初回のみ、配列Cのロード結果に接続され、次回以降は、演算結果に接続され、アキュムレート、ストアの繰り返しとなる点です。もちろん、パイプライン浮動小数点演算器の場合、演算器内アキュムレートは、単独では毎サイクル実行が不可能です。アイマックスは、4列マルチスレッディングにより、このケースでも、論理ユニットの動作にオーバヘッドが生じないため、性能低下を心配することなく、プログラムを書けます。
以上のように、似たような積和演算でも、4種類の書き方があります。どれにするかを別に指定する方法を使えば,コンパイラは楽に作れます。でも,アイマックスのプログラムは,CPUでも動くことを重視しています。C言語の制約の中で正しく動き,かつ,アイマックス上で狙い通りに高速動作させるために、変数の依存関係に、位置情報を組み合わせたプログラミング手法としています。。 ここからしばらくは,プログラミングに使う、テンプレートの説明です。アイマックス仕様書からの抜粋です。 つづきです。
つづきです。要するに、アイマックスのユニットは、スーパーシスクです。 つづきです。ロードストア機能を記述する際には、データの入れ替えに関する情報が、もっとも重要です。コンパイラは、DMAに関する情報を元に、LMMに関する空間融合や、DMA最適化を行います。 では、ソースプログラムが、どのように処理されていくかを見ていきましょう。これは,色を入れ換える簡単な画像処理プログラムです。まず,コンパイルの前に、まえ処理をします。コンブマークが変換します。 変換結果です。ぎょうの頭に,イーマックス5エービーという修飾が付いたり,中身が少し書き換えられています。次に,Cプリプロセッサ、cppが変換します。 Cppの変換結果です。いげた部分がなくなりました。次に,アイマックスコンパイラの本体、コンブシーツーシーが、各ぎょうを解釈して、アイマックスのコードを生成します。 コンパイル結果です。ワイルループの構造が消え,行数が増えて,一気に変わりました。各ローカルメモリの担当範囲の抽出,前回演算結果の回収,という具合に,アイマックスの制御コードが,CPUコードとして入りました。 さらに,4列分の論理ユニット機能を物理ユニットにセット,初期値をレジスタファイルへセット,担当範囲情報を各ローカルメモリへセットします。 最後に,ローカルメモリへのデータ送信,アイマックスの起動,同時に次データの送信を行い,アイマックスの動作完了を待ちます。残りは,各ユニットに送られる機能情報です。 この動画は、アイマックスの前身である、LAPPのハードウェアが,上から流れ込んでくるVLIWを、デコードし、レジスタ間依存関係に基づいて、CGRAの演算器間ネットワークをセットしていく様子です。探索が不要なアルゴリズムなので、ハードウェアが、動作周波数と同じ速度で高速にセットできます。アイマックスは、同じことをコンパイラがやっているだけです。ネットワーク設定ハードウェアがいらなくなるので、アイマックスのユニットは、LAPPのユニットよりも小さくなりました。 さて、導入編で、FPGAとCGRAは、再構成の粒度が違うと言いました。ソースプログラムを入力として,FPGAコンパイラはゲート間接続情報,CGRAコンパイラはユニット間接続情報を生成します。FPGAの場合,アイマックス程度の大きな回路だと,合成に24時間以上かかります。一方、アイマックスのコンパイル時間は,デバッグ用に、アイマックス機能C言語ライブラリを使う場合でも,アイマックスコンパイラを使う場合でも,数秒です。 コンブシーツーシーの中身は、こんな流れです。ソースコードをダウンロードできるので、詳細は、自分で確認しましょう。さっき説明した、演算器間ネットワークの構築だけでなく、ローカルメモリ空間を、たてよこにマージして、DMAを減らす機能が入っています。 今回は、アイマックスコンパイラの種明かしをしました。要するに、CGRAは、スーパーシスクにすることで、コンパイル時間を大幅に短縮できるということでした。では、今回はここまで。おつかれさま。