OpenCL版

今の状況を書いておくと、

https://github.com/tanakamura/waifu2x-converter-cpp/blob/gpu/src/modelHandler_OpenCL.cl

いちおう動いている、が、FMA版よりまだ遅い。i7 4700MQ vs GTX 765M でFLOPS値 200 : 80 ぐらい 765M は 1300GFLOPS なので効率6%ぐらい。

FMA 版と同じように、outputplane を最内ループにすると、係数全部ローカルメモリに乗せないといけないのだが、16KBでは全然入らない。(最大576KBになる)

案としては、

  • occupancy に頼らない。参考:http://www.cs.berkeley.edu/~volkov/volkov10-GTC.pdf
  • SMあたり65536,32768 レジスタあることを考えると、128x9 は余裕で全部レジスタにのる。ただ、これを分けるとxxxxxが必要になる。まあ、スレッドあたり63レジスタまで使えると考えると、4x9ぐらいは1スレッドに乗るので、それなら32thread reductionならまあ許容範囲?
  • for (x) { for (inputPl) { for (outputPl){} } }
  • 128x128x9 = 147456 = 3SM あればレジスタに乗る。outputPlane を 65535(強いNVIDIA)レジスタあるマシンでは3つか4つ、32768(弱いNVIDIA)マシンでは8ぐらい、16384(AMD)では16ぐらいに分ける
    • は?さすがに16だと割に合わんでしょ。没没。あとinputのlocality を維持するのがむずい
    • AMD はlocal memory が64KBある。
      • AMD : register 64KB lmem 64KB
      • NVIDIA(low) : register 128KB lmem 16KB
      • NVIDIA(high) : register 256KB lmem 16KB
      • weight : 128x128x9 = 576KB
  • for (x // lmemに入るだけブロッキング) { for (outputPlBlock) { for(inputPl) { for (outputPl) { } } } } // 没
// 没

for (x=0; x<width; x+=X_BLOCK_SIZE) {
  for (ip=0; ip<inputPlane; ip++) {
     coef0 = coef@global[0*32 + lid];
     coef.. = coef@global[.. + lid];
     coef8 = coef@global[8*32 + lid];

     x_block@local[] = input@global(ip,x,y)

     if (ip != 0) {
        sum = sum@global(op,
     }

     for (xb=0; xb<X_BLOCK_SIZE; xb++) {
         
     }
  }
}
  • for (x) { for (inputPlane) { for (output) { for (x_block // 64@NV, ) { for (outputblock // 4) {} } sum@global[64*output] = v } } } こうかな
    • 1. inputPlane は局所性いちばんきくので外側
    • 2. 没
  • outputPlane を最内にすると中間データが増える。入力の局所性が上がる
  • xi を最内にすると気合いだけで演算メモリ比は9:2あたりまで持っていける、が、そこに限界がある
  • inputPlane を最内にすると中間データは要らないが、入力の局所性が無くなる


つまりまとめると

  • outputPlane 方向にN回ブロッキングすると、入力メモリ量1/N になる。中間メモリ使用量がN倍になる。出力メモリ回数は変わらない
  • inputPlane 方向にM回ブロッキングすると、出力メモリ量が1/Mになる。inputを保持するローカルメモリがM倍になる。入力メモリ回数は変わらない
  • 使用レジスタ数は N x M に比例(weight を全部レジスタに乗せるなら)

というトレードオフか。


これを踏まえると、手順としては、

  1. xi を最内にして、演算メモリ比 9:2 版を作る (多分効率5%ぐらい)
  2. outputPlane 方向に4回ぐらいブロッキング。メモリネックなら2倍に近づくはず(効率10%、レジスタ36個)
  3. occupancy が下がらない程度のローカルメモリ量になるまで inputPlaneを増やしていく (余計な命令が増えて演算ネックになってくるはず。効率15%)

という手順かな。

いやそれでも15%か。30%ぐらいはいかないといけないのだけど。