https://github.com/tanakamura/waifu2x-converter-cpp/tree/simd_improve

やる気が出てきたのでチューニングしなおした。(バイナリは http://int.main.jp/files/waifu2x-converter_x64_1101.zip )

cpu before[GFLOPS] after[GFLOPS] improvements[%] efficiency[%] theoretical peak[GFLOPS] 備考
i7-6700(Skylake TB無し3.4G固定 4C8T) 259.3 359.8 38.8 82.7 435.2
Tegra4(Cortex-A15 1.6GHz x 4) 11.3 21.2 87.6 41.2 51.392 クロックは変動大きいのであやしい
Snapdragon 410(Cortex-A53 1.21GHz x4) 6.37 9.46 48.5 24.4 38.72
Celeron 2955U(Haswell 1.4GHz 2C SSE) 16.7 17.5 4.7 78.1 22.4
i7-4700MQ(Haswell TB無し2.4GHz固定 4C8T) 145.0 245.0 63.3 79.8 307.2
BCM2836(Cortex-A7x2 900MHz) 2.41 3.78 56.8 52.5 7.2
Core2 Duo 1.4GHz 11.2 12.3 9.8 54.9 22.4
MT8173(Cortex-A72x2 1.989GHz 32bit) 13.3 18.4 38.3 57.8 31.824 周波数はCPU-Z目視
MT8173(Cortex-A72x2 1.989GHz 64bit) 15.8 15.2 -3.8 47.8 31.824 周波数はCPU-Z目視


性能は、ファイル入出力を含まない(runbenchの値)

SSEは良くなると思ったが、Atomでは悪くなっている。これはSSEではbroadcastが弱くてロード+shuffleになってしまうから。Atomは2issueなので命令が増えた分そのまま遅くなってしまう。
まあAtomOpenCLのほうが確実にはやいから忘れよう。

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