Ce diaporama a bien été signalé.
Le téléchargement de votre SlideShare est en cours. ×

PBL1-v1-003j.pptx

Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Prochain SlideShare
PBL1-v1-006j.pptx
PBL1-v1-006j.pptx
Chargement dans…3
×

Consultez-les par la suite

1 sur 12 Publicité

PBL1-v1-003j.pptx

Télécharger pour lire hors ligne

IMAX3: Amazing Dataflow-Centric CGRA and its Applications
I present this slide to all hungry engineers who are tired of CPU, GPU, FPGA, tensor core, AI core, who want some challenging one with no black box inside, and who want to improve by themselves.

IMAX3: Amazing Dataflow-Centric CGRA and its Applications
I present this slide to all hungry engineers who are tired of CPU, GPU, FPGA, tensor core, AI core, who want some challenging one with no black box inside, and who want to improve by themselves.

Publicité
Publicité

Plus De Contenu Connexe

Similaire à PBL1-v1-003j.pptx (20)

Publicité

PBL1-v1-003j.pptx

  1. 1. 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だけじゃない超効率計算手法
  2. 2. Input pixels Load Sort Select center Output 4bytes 4bytes 20220202 2 メディアンフィルタを超高速実行する
  3. 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入力演算を考える
  4. 4. 20220202 4 画素値のロード・演算・ストアを並べる
  5. 5. 20220202 5 演算だけ並べても意味がないと言ったことの意味 LM LM LM LM Main Memory PREFETCH DRAIN LM LM
  6. 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. 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. 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 }
  9. 9. 20220202 9 コンパイル結果 ① ⑮ ④ ② ③ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑯ LM LM LM LM PREFETCH LM LM DRAIN
  10. 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. 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
  12. 12. 20220202 12 今回のおさらい

Notes de l'éditeur

  • 様々なアプリケーションを取りあげて、アイマックスのポテンシャルを説明するシリーズです。第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機能について説明しました。では、今回はここまでです。おつかれさま。

×