解説は…まあ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];
                }
            }
        }
    }
}

こんな感じ。

https://github.com/tanakamura/waifu2x-converter-cpp/blob/4f0000a6a9a1bfb1bdf1f5132e18b006679b46b5/src/modelHandler_simd.hpp#L103