Weitere ähnliche Inhalte Ähnlich wie PBL1-v1-003j.pptx (20) PBL1-v1-003j.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
(3. 画像フィルタ中級編)
スパコンからIoTまで 省エネ社会に
AI+BCだけじゃない超効率計算手法
3. v-sort v-sort v-sort v-sort
h-sort
h-sort
h-sort
Select max/mid/min value from three 8bits
Select max/min value from two 8bits
20220202
3
新しい3入力演算を考える
6. 20220202
6
プログラムに表現する
for (row=0; row<HT; row++) {
//EMAX5A begin median_filter mapdist=1
for (CHIP=0; CHIP<NCHIP; CHIP++) { /* output channels are parallelized by multi-chip (OC/#chip) */
for (INIT0=1,LOOP0=WD,col=0-4LL; LOOP0--; INIT0=0) {
exe(OP_ADD, &col, col, EXP_H3210, 4LL, EXP_H3210, 0, EXP_H3210, OP_AND, 0x00000000ffffffffLL, OP_NOP, 0);
exe(OP_ADD, &in_center, row_center, EXP_H3210, col, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 1*/ mop(OP_LDWR, &r7, in_center, -1276, MSK_D0, row_prev, WD, NULL, 0);
/* 1*/ mop(OP_LDWR, &r1, in_center, -1280, MSK_D0, row_prev, WD, NULL, 0);
/* 1*/ mop(OP_LDWR, &r5, in_center, -1284, MSK_D0, row_prev, WD, NULL, 0);
/* 4*/ exe(OP_MMIN3, &r17, r7, EXP_H3210, r1, EXP_H3210, r5, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 4*/ exe(OP_MMID3, &r11, r7, EXP_H3210, r1, EXP_H3210, r5, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 4*/ exe(OP_MMAX3, &r15, r7, EXP_H3210, r1, EXP_H3210, r5, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 2*/ mop(OP_LDWR, &r4, in_center, 4, MSK_D0, row_center, WD, NULL, 0);
/* 2*/ mop(OP_LDWR, &r0, in_center, 0, MSK_D0, row_center, WD, NULL, 0);
/* 2*/ mop(OP_LDWR, &r3, in_center, -4, MSK_D0, row_center, WD, NULL, 0);
/* 5*/ exe(OP_MMIN3, &r14, r4, EXP_H3210, r0, EXP_H3210, r3, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 5*/ exe(OP_MMID3, &r10, r4, EXP_H3210, r0, EXP_H3210, r3, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 5*/ exe(OP_MMAX3, &r13, r4, EXP_H3210, r0, EXP_H3210, r3, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 3*/ mop(OP_LDWR, &r8, in_center, 1284, MSK_D0, row_next, WD, row_next_next, WD);
/* 3*/ mop(OP_LDWR, &r2, in_center, 1280, MSK_D0, row_next, WD, row_next_next, WD);
/* 3*/ mop(OP_LDWR, &r6, in_center, 1276, MSK_D0, row_next, WD, row_next_next, WD);
/* 6*/ exe(OP_MMIN3, &r18, r8, EXP_H3210, r2, EXP_H3210, r6, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 6*/ exe(OP_MMID3, &r12, r8, EXP_H3210, r2, EXP_H3210, r6, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 6*/ exe(OP_MMAX3, &r16, r8, EXP_H3210, r2, EXP_H3210, r6, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
:
/*14*/ exe(OP_MMAX, &r8, r14, EXP_H3210, r18, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/*14*/ exe(OP_MMIN, &r5, r15, EXP_H3210, r13, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/*15*/ exe(OP_MMID3, &r31, r5, EXP_H3210, r10, EXP_H3210, r8, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/*16*/ mop(OP_STWR, &r31, out_center, col, MSK_D0, out_center, WD, out_prev, WD);
}
}
//EMAX5A end
}
7. 20220202
7
プログラムに表現する
for (row=0; row<HT; row++) {
//EMAX5A begin median_filter mapdist=1
for (CHIP=0; CHIP<NCHIP; CHIP++) { /* output channels are parallelized by multi-chip (OC/#chip) */
for (INIT0=1,LOOP0=WD,col=0-4LL; LOOP0--; INIT0=0) {
exe(OP_ADD, &col, col, EXP_H3210, 4LL, EXP_H3210, 0, EXP_H3210, OP_AND, 0x00000000ffffffffLL, OP_NOP, 0);
exe(OP_ADD, &in_center, row_center, EXP_H3210, col, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 1*/ mop(OP_LDWR, &r7, in_center, -1276, MSK_D0, row_prev, WD, NULL, 0);
/* 1*/ mop(OP_LDWR, &r1, in_center, -1280, MSK_D0, row_prev, WD, NULL, 0);
/* 1*/ mop(OP_LDWR, &r5, in_center, -1284, MSK_D0, row_prev, WD, NULL, 0);
/* 4*/ exe(OP_MMIN3, &r17, r7, EXP_H3210, r1, EXP_H3210, r5, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 4*/ exe(OP_MMID3, &r11, r7, EXP_H3210, r1, EXP_H3210, r5, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 4*/ exe(OP_MMAX3, &r15, r7, EXP_H3210, r1, EXP_H3210, r5, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 2*/ mop(OP_LDWR, &r4, in_center, 4, MSK_D0, row_center, WD, NULL, 0);
/* 2*/ mop(OP_LDWR, &r0, in_center, 0, MSK_D0, row_center, WD, NULL, 0);
/* 2*/ mop(OP_LDWR, &r3, in_center, -4, MSK_D0, row_center, WD, NULL, 0);
/* 5*/ exe(OP_MMIN3, &r14, r4, EXP_H3210, r0, EXP_H3210, r3, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 5*/ exe(OP_MMID3, &r10, r4, EXP_H3210, r0, EXP_H3210, r3, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 5*/ exe(OP_MMAX3, &r13, r4, EXP_H3210, r0, EXP_H3210, r3, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 3*/ mop(OP_LDWR, &r8, in_center, 1284, MSK_D0, row_next, WD, row_next_next, WD);
/* 3*/ mop(OP_LDWR, &r2, in_center, 1280, MSK_D0, row_next, WD, row_next_next, WD);
/* 3*/ mop(OP_LDWR, &r6, in_center, 1276, MSK_D0, row_next, WD, row_next_next, WD);
/* 6*/ exe(OP_MMIN3, &r18, r8, EXP_H3210, r2, EXP_H3210, r6, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 6*/ exe(OP_MMID3, &r12, r8, EXP_H3210, r2, EXP_H3210, r6, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 6*/ exe(OP_MMAX3, &r16, r8, EXP_H3210, r2, EXP_H3210, r6, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
:
/*14*/ exe(OP_MMAX, &r8, r14, EXP_H3210, r18, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/*14*/ exe(OP_MMIN, &r5, r15, EXP_H3210, r13, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/*15*/ exe(OP_MMID3, &r31, r5, EXP_H3210, r10, EXP_H3210, r8, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/*16*/ mop(OP_STWR, &r31, out_center, col, MSK_D0, out_center, WD, out_prev, WD);
}
}
//EMAX5A end
}
8. 20220202
8
プログラムに表現する
for (row=0; row<HT; row++) {
//EMAX5A begin median_filter mapdist=1
for (CHIP=0; CHIP<NCHIP; CHIP++) { /* output channels are parallelized by multi-chip (OC/#chip) */
for (INIT0=1,LOOP0=WD,col=0-4LL; LOOP0--; INIT0=0) {
exe(OP_ADD, &col, col, EXP_H3210, 4LL, EXP_H3210, 0, EXP_H3210, OP_AND, 0x00000000ffffffffLL, OP_NOP, 0);
exe(OP_ADD, &in_center, row_center, EXP_H3210, col, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 1*/ mop(OP_LDWR, &r7, in_center, -1276, MSK_D0, row_prev, WD, NULL, 0);
/* 1*/ mop(OP_LDWR, &r1, in_center, -1280, MSK_D0, row_prev, WD, NULL, 0);
/* 1*/ mop(OP_LDWR, &r5, in_center, -1284, MSK_D0, row_prev, WD, NULL, 0);
/* 4*/ exe(OP_MMIN3, &r17, r7, EXP_H3210, r1, EXP_H3210, r5, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 4*/ exe(OP_MMID3, &r11, r7, EXP_H3210, r1, EXP_H3210, r5, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 4*/ exe(OP_MMAX3, &r15, r7, EXP_H3210, r1, EXP_H3210, r5, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 2*/ mop(OP_LDWR, &r4, in_center, 4, MSK_D0, row_center, WD, NULL, 0);
/* 2*/ mop(OP_LDWR, &r0, in_center, 0, MSK_D0, row_center, WD, NULL, 0);
/* 2*/ mop(OP_LDWR, &r3, in_center, -4, MSK_D0, row_center, WD, NULL, 0);
/* 5*/ exe(OP_MMIN3, &r14, r4, EXP_H3210, r0, EXP_H3210, r3, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 5*/ exe(OP_MMID3, &r10, r4, EXP_H3210, r0, EXP_H3210, r3, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 5*/ exe(OP_MMAX3, &r13, r4, EXP_H3210, r0, EXP_H3210, r3, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 3*/ mop(OP_LDWR, &r8, in_center, 1284, MSK_D0, row_next, WD, row_next_next, WD);
/* 3*/ mop(OP_LDWR, &r2, in_center, 1280, MSK_D0, row_next, WD, row_next_next, WD);
/* 3*/ mop(OP_LDWR, &r6, in_center, 1276, MSK_D0, row_next, WD, row_next_next, WD);
/* 6*/ exe(OP_MMIN3, &r18, r8, EXP_H3210, r2, EXP_H3210, r6, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 6*/ exe(OP_MMID3, &r12, r8, EXP_H3210, r2, EXP_H3210, r6, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/* 6*/ exe(OP_MMAX3, &r16, r8, EXP_H3210, r2, EXP_H3210, r6, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
:
/*14*/ exe(OP_MMAX, &r8, r14, EXP_H3210, r18, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/*14*/ exe(OP_MMIN, &r5, r15, EXP_H3210, r13, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/*15*/ exe(OP_MMID3, &r31, r5, EXP_H3210, r10, EXP_H3210, r8, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
/*16*/ mop(OP_STWR, &r31, out_center, col, MSK_D0, out_center, WD, out_prev, WD);
}
}
//EMAX5A end
}
10. 20220202
10
アンシャープマスクはもっと簡単
#define r(p) ((p)>>24)
#define g(p) ((p)>>16 & 255)
#define b(p) ((p)>> 8 & 255)
for (row=0; row<HT; row++) {
for (col=0; col<WD; col++) {
pix0 = in[row][col];
pix1 = in[row-1][col];
pix2 = in[row+1][col];
pix3 = in[row ][col-1];
pix4 = in[row ][col+1];
pix5 = in[row-1][col-1];
pix6 = in[row-1][col+1];
pix7 = in[row+1][col-1];
pix8 = in[row+1][col+1];
// p0: 1.87 = +239/128
// p1234: 0.12 * 4 = -15.25/128
// p5678: 0.10 * 4 = -13/128
r0 = r(pix0); r1 = r(pix1)+r(pix2)+r(pix3)+r(pix4); r2 = r(pix5)+r(pix6)+r(pix7)+r(pix8);
g0 = g(pix0); g1 = g(pix1)+g(pix2)+g(pix3)+g(pix4); g2 = g(pix5)+g(pix6)+g(pix7)+g(pix8);
b0 = b(pix0); b1 = b(pix1)+b(pix2)+b(pix3)+b(pix4); b2 = b(pix5)+b(pix6)+b(pix7)+b(pix8);
rout = (r0 * 239 - r1 * 13 - r2 * 15 - r2/4) >> 7);
gout = (g0 * 239 - g1 * 13 - g2 * 15 - g2/4) >> 7);
bout = (b0 * 239 - b1 * 13 - b2 * 15 - b2/4) >> 7);
out[row][col] = rout<<24 | gout<<16 | bout<<8;
}
}
-0.1 -0.12 -0.1
-0.12 +1.88 -0.12
-0.1 -0.12 -0.1
11. 20220202
11
メディアンフィルタよりも簡単なアンシャープマスク
for (row=0; row<HT; row++) {
//EMAX5A begin unsharp mapdist=1
for (CHIP=0; CHIP<NCHIP; CHIP++) { /* output channels are parallelized by multi-chip (OC/#chip) */
for (INIT0=1,LOOP0=WD,col=0-4LL; LOOP0--; INIT0=0) {
exe(OP_ADD, &col, col, EXP_H3210, 4LL, EXP_H3210, 0, EXP_H3210, OP_AND, 0x00000ffffffffLL, OP_NOP,0);
exe(OP_ADD, &in_center, row_center, EXP_H3210, col, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
mop(OP_LDWR, &r1, in_center, -1276, MSK_D0, row_prev, WD, NULL, 0);
mop(OP_LDWR, &r2, in_center, -1284, MSK_D0, row_prev, WD, NULL, 0);
mop(OP_LDWR, &r5, in_center, -1280, MSK_D0, row_prev, WD, NULL, 0);
exe(OP_MAUH, &r11, r1, EXP_B5410, r2, EXP_B5410, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
exe(OP_MAUH, &r12, r1, EXP_B7632, r2, EXP_B7632, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
mop(OP_LDWR, &r6, in_center, 4, MSK_D0, row_center, WD, NULL, 0);
mop(OP_LDWR, &r7, in_center, -4, MSK_D0, row_center, WD, NULL, 0);
mop(OP_LDWR, &r0, in_center, 0, MSK_D0, row_center, WD, NULL, 0);
exe(OP_MLUH, &r20, r0, EXP_B5410, 239, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
exe(OP_MLUH, &r21, r0, EXP_B7632, 239, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
mop(OP_LDWR, &r3, in_center, 1284, MSK_D0, row_next, WD, row_next_next, WD);
mop(OP_LDWR, &r4, in_center, 1276, MSK_D0, row_next, WD, row_next_next, WD);
mop(OP_LDWR, &r8, in_center, 1280 , MSK_D0, row_next, WD, row_next_next, WD);
exe(OP_MAUH, &r15, r5, EXP_B5410, r6, EXP_B5410, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
exe(OP_MAUH, &r16, r5, EXP_B7632, r6, EXP_B7632, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
exe(OP_MAUH3, &r11, r3, EXP_B5410, r4, EXP_B5410, r11, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
exe(OP_MAUH3, &r12, r3, EXP_B7632, r4, EXP_B7632, r12, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
exe(OP_MLUH, &r13, r11, EXP_H3210, 13, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
exe(OP_MLUH, &r14, r12, EXP_H3210, 13, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
exe(OP_MAUH3, &r15, r7, EXP_B5410, r8, EXP_B5410, r15, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
exe(OP_MAUH3, &r16, r7, EXP_B7632, r8, EXP_B7632, r16, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
exe(OP_NOP, &r7, r15, EXP_H3210, 0LL, EXP_H3210, 0, EXP_H3210, OP_OR, 0, OP_SRLM, 2);
exe(OP_MLUH, &r17, r15, EXP_H3210, 15, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
exe(OP_NOP, &r8, r16, EXP_H3210, 0LL, EXP_H3210, 0, EXP_H3210, OP_OR, 0, OP_SRLM, 2);
exe(OP_MLUH, &r18, r16, EXP_H3210, 15, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
exe(OP_MSUH3, &r10, r20, EXP_H3210, r7, EXP_H3210, r17, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
exe(OP_MSUH3, &r11, r21, EXP_H3210, r8, EXP_H3210, r18, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
exe(OP_MSUH, &r20, r10, EXP_H3210, r13, EXP_H3210, 0, EXP_H3210, OP_OR, 0, OP_SRLM, 7);
exe(OP_MSUH, &r21, r11, EXP_H3210, r14, EXP_H3210, 0, EXP_H3210, OP_OR, 0, OP_SRLM, 7);
exe(OP_MH2BW, &r31, r21, EXP_H3210, r20, EXP_H3210, 0, EXP_H3210, OP_NOP, 0, OP_NOP, 0);
mop(OP_STWR, &r31, out_center, col, MSK_D0, out_center, WD, row_prev, WD);
}
}
//EMAX5A end
}
r1 r5 r2
r7 r0 r6
r4 r8 r3
Hinweis der Redaktion 様々なアプリケーションを取りあげて、アイマックスのポテンシャルを説明するシリーズです。第3回は、メディアンフィルタからはじめる、画像ステンシル計算です。 まず、メディアンフィルタと呼ぶ画像処理をとりあげます。写真のうえが元画像、下が処理結果です。メディアンフィルタは、3かける3領域の色成分、RGBを明るさ順に並べて、中央のあたいを計算結果とすることで、輪郭をぼかすことなく、色だけをぼかすことができる便利な画像フィルタです。例えば、輪郭抽出の前処理に使います。ただし、明るさ順に並べ替える作業が必要なので、かなりの計算量が必要です。したのプログラムはC言語で書いてあります。3かける3領域から9個の画素値を取り出します。そして、各々8ビットのRGB成分ごとに比較をして、並べ替えていきます。ここではバブルソートを使っています。隣どうしを比べて、逆順なら入れ換えることを繰り返すだけです。クイックソートのように、より高速なアルゴリズムもありますが、要素数が9しかないし、まん中のあたいが求まった時点で、残りを打ち切るので、これで十分です。6行目と7行目に2重ループがありますね。ループの中の比較と入れ換え処理が、8、9、10行目です。この3行が、合計8+7+6+5+4の合計30回繰り返されます。これでようやく1つの出力画素が求まります。でも、これをアイマックスに実装すると、なんと、毎サイクル、複数の出力画素が求まります。単なるアンシャープマスクなら、90パーセントのエンジニアが1秒で思いつくでしょうけどね。ソートが必要なメディアンフィルタを同速で実現できるのは、0.01パーセントのエンジニアだけです。 前の例では、2重ループを使って並べ替えました。しかし、CGRAを使うには、ループを使わないソート方法を考えて、単純なパイプライン処理に変形しなければなりません。そこで、8ビット単位に大小比較して、指定した順番のあたいを出力する、5つの基本操作を新たに作ります。マックス3ABCは,3つの画素値、各々4バイトを入力とし、3つの赤成分、各8ビットから最大値、3つの緑成分から同じく最大値、3つの青成分から同じく最大値を出力します。同様に、ミッド3ABCはちゅうかんち、ミン3ABCは最小値を出力します。マックス2ABCと、ミン2ABCは、2つの画素値を入力とし、Cは無視します。これらを使うと、2重ループを使わずにすみます。このプログラムでは、3行目で、9個の画素値をr0からr8に取り出しています。4、5、6行目が図、aに対応しています。まず、縦に3画素ずつまとめます。r5とr1とr7をマックス3、ミッド3、ミン3に与えると、最大値、ちゅうかんち、最小値が計算できます。これを改めてr5とr1とr7に代入すると、3つの画素の並べ替えが完了です。同様に、r3とr0とr4も並べ替えます。さらに、r6とr2とr8を並べ替えます。これで、9個の画素値を図、aの関係に並べ替えることができました。次は、横に3画素ずつまとめます。r5とr3とr6をマックス3、ミッド3、ミン3に与えて並べ替えます。同様に、r1とr0とr2、r7とr4とr8を並べ替えます。結果、図、bの関係に並べ替えができました。さて、この時点で何がわかるでしょうか。aの時点で、上の3つの画素値は、各グループで最大です。そして、下の3つの画素値は、各グループで最小です。bでは、3つの最大値の中の最大値、つまり全体の中での最大値が右上すみに移動します。同様に、3つの最小値の中の最小値、つまり全体の中での最小値が左下すみに移動します。最後に欲しいのは、全体の中の中間値なので、右上すみと左下すみのあたいは捨てることができます。以上を繰り返すと、最大値と最小値が順に捨てられて、最後のgに到達します。画素値が3つだけ残っているので、18行目でミッド3を実行して、ちゅうかんちr0を取り出します。この方法を考えたのが2008年、教科書にしたのが2012年です。 この図は、さっきのプログラムを機械語命令に書き換えて並べたものです。マックス、ミッド、ミンは、3入力演算器を使って1命令で実行できます。aからgは、前の図の各状態に対応しています。ループ構造がなくなって、データがうえから下に流れるように、機械語命令が並んでいます。この機械語命令列は、横1列を1つのVLIWにまとめることができます。ということは、1つの出力画素が、わずか16命令で求まることになります。そして、CGRAの場合、全ての機械語命令を、2次元構造に配置された多数のALUに一度にセットできます。うえから水のようにデータを流し込むと、下から、途切れなく出力画素が生成されます。つまり、VLIWの16倍速になりました。さらに、複数組をCGRAにセットすれば、毎サイクル、複数の出力画素が求まります。ほら、アイマックスを使えば、簡単にできそうです。 簡単と言いましたが、ここまでは、誰でも思いつくレベルです。アイマックスの特長は、さらに、演算と、外部メモリからのデータ供給と、結果の追い出しをパイプライン処理できる点にあります。1から3行目のロード命令には、画像の各行に対応する入力データをそれぞれ格納する、ローカルメモリを連結します。そして、次の4行目に、次データ供給のためのDMA機能を連結します。16行目のストア命令には、演算結果を格納するローカルメモリを連結し、前の15行目に、データ書き戻しのためのDMA機能を連結します。これで、主記憶を含めたパイプラインの完成です。 方針が決まったら、プログラムを書いていきます。主記憶を含めたパイプライン動作を、簡単にプログラムに表現できる点が、アイマックスのすごいところです。ほとんどのエンジニアには、ただの呪文にしかみえないでしょうけどね。実際には、シスクよりも高機能な機械語命令が並んでいるだけで、普通のレベルです。慣れた言語で適当に書いて、1時間のコンパイル時間を何度も無駄にするか、頭を使って、ターンアラウンドタイムを10秒にするか、まあ、個人のレベルに応じて選ぶだけの話です。無理な人は、頑張る必要はありません。0.01パーセントが対象というのは、そういうことです。
さて、exe関数ひとつが、論理ユニットの、5入力、3段パイプライン演算器に対応します。第1ステージには、メディア演算を含む3入力算術演算、第2ステージには1入力を加えた論理演算、第3ステージには1入力を加えたシフト演算があり、合計5入力の演算ができます。また、第1ステージの3入力は、64ビットレジスタのどの部分を使うかを指定できます。H3210は、4つの16ビットをそのまま使うことを意味します。64ビット全部使うという、普通の指定です。青文字の部分は、論理演算やシフト演算の場所ですが、今は使わないので、ノップが並んでいます。 では、本題です。ここで注目すべきは、演算ではなく、赤文字の部分です。まず、2行目を見てください。今まで、おまじないのように、マップディスト0と書いてありましたが、このプログラムでは、マップディスト1と書いてあります。これは、アイマックスの一連の実行が終わったら、ローカルメモリは触らずに、機能の写像位置を1段進めることを意味します。どういうことでしょうか。以前に、効率を上げるには、ローカルメモリの内容を極力再利用して、外部メモリを使わないのが良いと言いました。3かける3といった固定枠をずらしながら計算を進める、ステンシル計算と呼ぶ計算方法では、1行分の処理が終わったら、注目位置を下に1ぎょうずらせて、同じことを繰り返します。つまり、さっき使った、2行目と3行目のデータは、次の実行では、新たな1行目と2行目のデータとして再利用することができ、まだない3行目を、外部メモリから、次のローカルメモリに継ぎ足せばよいことになります。アイマックスには、機能の写像位置のみをずらす機能があって、これを使う場合に、マップディストに0以外のあたいを指定します。
前に、Mop関数が、高機能ロードストアに該当すると説明しました。先頭から順に、オペコード、読み書きレジスタ、ベースアドレス、オフセット、オフセットマスク、そして、使う主記憶領域の先頭アドレスとワード数でした。ロード関数を見ると、先頭アドレスに、ロウプレブ、ロウセンター、ロウネクストと書いてあります。それぞれ、注目している3かける3領域の、うえ、中央、したのぎょうの先頭です。そして、行頭に番号3がついた3つのロード関数では、さらに、ロウネクストネクストというアドレスが追加されています。
現在の実行に必要な1行目のデータは、ロウプレブ、2行目のデータは、ロウセンター、3行目のデータは、ロウネクストです。そして、未来の3行目に該当する4行目のデータが、ロウネクストネクストです。もう、わかりましたね。3行分のデータがローカルメモリに揃っている状態で、計算を開始し、同時に、4行目のデータを外部メモリから供給します。計算が終わって、機能の写像位置をしたにずらすと、2行目から4行目には、次の計算に必要なデータが、すでに揃っている状態になります。必要なデータ領域を指定する。そして、コンパイラが考えて、DMAをスケジューリングする。ここが、アイマックスコンパイラの肝です。 ストアについても同様です。行頭に番号16がついたストア関数が、演算結果をローカルメモリに格納します。そして、次の計算のために機能の写像位置が下にずれると、演算結果は、1つうえのローカルメモリにずれて見えます。これを、次の計算と同時に主記憶に追い出すことで、全体のパイプライン処理を行います。ストア関数では、アウトプレブという、1つ前のストア先領域を指定しておくことで、コンパイラが適切なDMAを生成します。
ところで、もし、プログラマが、ロウネクストネクストや、アウトプレブのアドレスを間違えていたら、何が起こるでしょう。アイマックスは、何が指定されていても、現在の各ローカルメモリの担当範囲を覚えていて、どのみち、計算開始前に、DMAが必要かどうかをチェックします。間違えていても、DMAが余計に起動されるだけで、結果がおかしくなることはありません。また、実行後、ロード、演算、ストアの実行時間内訳を表示できるので、思ったほど速くならない場合は、どこが遅いのかを確認できます。キャッシュメモリがないので、全体の実行時間は、簡単に予測でき、チューニングがうまくできていれば、予測通りの性能が出ます。データフローの設計が正しければ、チューニングに終わりが見えないといったことは、原理的にありえません。 これが、コンパイル結果です。ひとくみの計算に、14ユニットを使うので、64ユニット構成のアイマックスには、4組を写像できます。つまり、毎サイクル4つの出力が得られます。数字は、VLIW命令の番号と同じです。まる1とまる2とまる3に、画像の各ぎょうが格納されていて、計算結果が、まる16のローカルメモリに入ります。演算しながら、プリフェッチとドレインが動作します。演算器も、外部メモリバスも、フル稼働している状態です。 次は、アンシャープマスクです。アンシャープマスクは、色の境界をくっきりさせる効果があります。メディアンフィルタの高速化方法を理解したら、アンシャープマスクは、おもちゃに見えることでしょう。同じように、C言語から始めます。二重ループがあって、中に、3かける3画素に対する計算がかいてあります。中心画素には、1.88を掛けて、上下左右には、マイナス0.12を掛けて、斜め四隅には、マイナス0.1を掛けて、全部を合計するだけです。単純ですが、RGBの各成分ごとに計算するので、計算量はそれなりにあります。もちろん、まじめに浮動小数点演算をする必要はなくて、整数演算に置き換えることができます。 ロード、演算、ストアの流れや、プリフェッチ、ドレインの指定は、メディアンフィルタと全く同じです。MAUHは、64ビットレジスタに対して16ビット加算を4つ、MLUHは、64ビットレジスタに対して11ビット乗算を4つという具合に、メディア演算を使っている点が異なるだけです。様々な整数演算の種類については、アイマックスの仕様書を見てください。また、エッジ検出も同じように書くことができます。似たようなプログラムなので、そろそろ飽きてきたことと思います。具体的に知りたい人は、アイマックス仕様書のサンプルプログラムを見てください。 今回は、マップディストを使って、機能の写像位置をずらすことで、ローカルメモリを再利用する考え方、そして、外部メモリも含めたパイプライン処理、また、アイマックスコンパイラのDMA機能について説明しました。では、今回はここまでです。おつかれさま。