■
解説は…まあGPU版も動いたら考えよう。
簡単に説明するとブロッキングを頑張った。
前が
for (yi=0; yi<height; yi++) { for (xi=0; xi<width; xi++) { float sum[nOutputPlanes] = {0}; for (ip=0; ip<nInputPlane; ip++) { for (op=0; op<nOutputPlane; op++) { sum[op] += kernel(planes[ip][yi][xi], weight[ip][op]); } } for (op=0; op<nOutputPlane; op++) { o_planes[op][yi][xi] = ReLU(sum[op]); } } }
改善後が、
// L1 : 32KB // L2 : 256KB // reg : 256bit x 16 weight_reg[2]; // 2reg output_reg[5][2]; // 10reg input_reg[1] // 1reg // 13reg@FMA, 14reg@AVX,SSE // working set size // output input weight | total (where width=512) for (dposy=0; dposy<3; dposy++) { // 128*4*width 128*3*4*width 128*128*9*4 | 1.6MB for (ii0=0; ii0<nInputPlane; ii0+=32) { // 128*4*width 128* 4*width 128*128*3*4 | 704KB for (oi0=0; oi0<nOutput; oi0+=16) { // 128*4*width 32* 4*width 32*128*3*4 | 368KB for (x0=0; x0<width; x0+=5) { // 16*4*width 32* 4*width 32* 16*3*4 | 102KB (L2) output_reg[0..4][0..1] = 0; for (dposx=0; dposx<3; dposx++) { // 16*4*5(10reg) 32* 4*5 32* 16*3*4 | 7KB (L1) for (ii1=ii0; ii1<ii0+32; ii1++) {// 16*4*5 32* 4*5 32* 16* 4 | .. (L1) for (oi1=0; oi1<16; oi1++) { // 16*4*5 4*5 16* 4 | .. (L1) weight_reg[0] = weigh[oi0+0][ii1][dpos][oi1]; weight_reg[1] = weigh[oi0+1][ii1][dpos][oi1]; output_reg[0..4][0][oi1] += weight_reg[0] * input[ii1][x0+(0..4)+dpos]; output_reg[0..4][1][oi1] += weight_reg[1] * input[ii1][x0+(0..4)+dpos]; } } } if (ii0 == last) { output[xi0..xi0+4][oi0..oi0+1]=ReLU(output[][] + output_reg[0..4][0..1]); } else { output[xi0..xi0+4][oi0..oi0+1]+=output_reg[0..4][0..1]; } } } } }
こんな感じ。