J

2004 | 08 | 09 | 10 | 11 | 12 |
2005 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2006 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 12 |
2008 | 01 | 02 | 04 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 03 | 04 | 05 | 06 | 07 | 08 | 12 |
2013 | 01 | 02 | 03 | 05 | 06 | 07 | 08 | 09 | 10 | 11 |
2014 | 01 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2015 | 02 | 03 | 04 | 05 | 06 | 07 | 10 | 11 | 12 |
2016 | 01 | 02 | 04 | 05 | 06 | 07 | 09 | 11 | 12 |
2017 | 01 | 02 | 03 | 05 | 06 | 07 | 08 | 09 |

ホーム

日記内の"morihyphen.hp.infoseek.co.jp"へのリンクは切れてます。必要な場合はお手数ですが int.main.jp へ書き換えをお願いします。

TODO: ファイル名確認を忘れないこと > 自分

twitter

 | 

2014-10-29

ひとり ARM tech なんとか 23:03

http://www.event-info.com/arm-seminar2014/

明日だった。あとintel なんとか も 明日ですね。

まあ別にこういうイベントって真面目系だから興味無いのだけど、せっかくなので Cortex A15も詰め詰めしよう。


A15 は今 37.2% で、これはA9より低い。なんで?


まあ4コア使うと1.605GHzに落ちているっぽいのだけどそれを考慮しても、41% もっと出てもいいはず。


今の最内ループは、

    __asm__ __volatile__ (".p2align 4\n\t"
                          "1:\n\t"
                          "vld1.32 {d0,d1}, [%[inL00_0]:64]!\n\t"
                          "vld1.32 {d2,d3}, [%[inL00_1]:64]!\n\t"
                          "pld [%[inRp1], %[pld_offset]]\n\t"
                          "vldmia %[inRp1], {q8-q11}\n\t"

                          "add %[inRp1], %[inRp1], %[pitch_f32]\n\t"

                          "vmla.f32 %q[vout0_0], q8, d0[0]\n\t"
                          "vmla.f32 %q[vout1_0], q8, d2[0]\n\t"
                          "vmla.f32 %q[vout0_1], q9, d0[0]\n\t"
                          "vmla.f32 %q[vout1_1], q9, d2[0]\n\t"
                          "pld [%[inL00_0], #64]\n\t"
                          "vmla.f32 %q[vout0_2], q10, d0[0]\n\t"
                          "vmla.f32 %q[vout1_2], q10, d2[0]\n\t"
                          "vmla.f32 %q[vout0_3], q11, d0[0]\n\t"
                          "vmla.f32 %q[vout1_3], q11, d2[0]\n\t"

                          "pld [%[inRp1], %[pld_offset]]\n\t"
                          "vldmia %[inRp1], {q8-q11}\n\t"
                          "add %[inRp1], %[inRp1], %[pitch_f32]\n\t"

                          "vmla.f32 %q[vout0_0], q8, d0[1]\n\t"
                          "vmla.f32 %q[vout1_0], q8, d2[1]\n\t"
                          "vmla.f32 %q[vout0_1], q9, d0[1]\n\t"
                          "vmla.f32 %q[vout1_1], q9, d2[1]\n\t"
                          "pld [%[inL00_1], #64]\n\t"
                          "vmla.f32 %q[vout0_2], q10, d0[1]\n\t"
                          "vmla.f32 %q[vout1_2], q10, d2[1]\n\t"
                          "vmla.f32 %q[vout0_3], q11, d0[1]\n\t"
                          "vmla.f32 %q[vout1_3], q11, d2[1]\n\t"

                          "pld [%[inRp1], %[pld_offset]]\n\t"
                          "vldmia %[inRp1], {q8-q11}\n\t"
                          "add %[inRp1], %[inRp1], %[pitch_f32]\n\t"

                          "vmla.f32 %q[vout0_0], q8, d1[0]\n\t"
                          "vmla.f32 %q[vout1_0], q8, d3[0]\n\t"
                          "vmla.f32 %q[vout0_1], q9, d1[0]\n\t"
                          "vmla.f32 %q[vout1_1], q9, d3[0]\n\t"
                          "vmla.f32 %q[vout0_2], q10, d1[0]\n\t"
                          "vmla.f32 %q[vout1_2], q10, d3[0]\n\t"
                          "vmla.f32 %q[vout0_3], q11, d1[0]\n\t"
                          "vmla.f32 %q[vout1_3], q11, d3[0]\n\t"

                          "pld [%[inRp1], %[pld_offset]]\n\t"
                          "vldmia %[inRp1], {q8-q11}\n\t"
                          "add %[inRp1], %[inRp1], %[pitch_f32]\n\t"

                          "vmla.f32 %q[vout0_0], q8, d1[1]\n\t"
                          "vmla.f32 %q[vout1_0], q8, d3[1]\n\t"
                          "vmla.f32 %q[vout0_1], q9, d1[1]\n\t"
                          "cmp %[inRp1], %[inRp_end]\n\t"
                          "vmla.f32 %q[vout1_1], q9, d3[1]\n\t"
                          "vmla.f32 %q[vout0_2], q10, d1[1]\n\t"
                          "vmla.f32 %q[vout1_2], q10, d3[1]\n\t"
                          "vmla.f32 %q[vout0_3], q11, d1[1]\n\t"
                          "vmla.f32 %q[vout1_3], q11, d3[1]\n\t"

                          "bne 1b\n\t"
                          :[inL00_0]"+r"(inL00_0

こうなっている。vmla 以外にもいくつか命令があるが、Cortex A15は、OoO 3issue なので、多少他の命令が混ざっていても、80-90%ぐらい出てもよさげだが。


Tegra4 は、4+1 とかいうのを実装していて、負荷によってonlineになるCPUの数が変わるらしい。

http://elinux.org/Jetson/Performance

無効にして、全コア有効にするのが良い。


が、まずは何故かクロックが下がってしまう4コアのことは忘れて、1コアだけonlineにしておこう。cpufreq で、クロックは1.8105 GHz に固定しておく。また、Android上だと複数スレッド時に値が安定しないようなので、AndroidLinuxをインストールしてその上で動かしている。



最初の条件

とりあえずグラフ描くと

f:id:w_o:20141029230600p:image

こう(オレンジが理論値)。まあ、メモリでいくらか引っかかってる気もするが、それ以上に別の理由で止まってるように見える。

とりあえず、



#if 0
    __asm__ __volatile__ (".p2align 4\n\t"
                          "1:\n\t"
                          "vld1.32 {d0,d1}, [%[inL00_0]:64]!\n\t"
                          <..snip..>
                         );

#else
    asm __volatile__ (""
                      :[inL00_0]"+r"(inL00_0), [inL00_1]"+r"(inL00_1),
                       [vout0_0]"+w"(vout0_0),
                       [vout0_1]"+w"(vout0_1),
                       [vout0_2]"+w"(vout0_2),
                       [vout0_3]"+w"(vout0_3),
                       [vout1_0]"+w"(vout1_0),
                       [vout1_1]"+w"(vout1_1),
                       [vout1_2]"+w"(vout1_2),
                       [inRp1]"+r"(inRp1)
                      :[pitch_f32]"r"(pitch_f32*4), [inRp_end]"r"(inRp1 + 128*pitch_f32), [pld_offset]"r"(pitch_f32*4*4));
#endif

こんな感じにする。こうすると、最内ループ以外で消費している時間がわかる。(inline asm の引数は、コンパイラの最適化をほどよく制御するのに便利ですね。覚えておきましょう。)

asm 中身あり
( 1024- 0):neon                : sec= 0.39874,   5.01581[GFLOPS],  0.02939[GB/s]
( 1024- 1):neon                : sec= 0.39206,   5.10120[GFLOPS],  0.02989[GB/s]
( 1024- 2):neon                : sec= 0.39550,   5.05686[GFLOPS],  0.02963[GB/s]

asm 中身無し
( 1024- 0):neon                : sec= 0.01932, 103.53541[GFLOPS],  0.60665[GB/s]
( 1024- 1):neon                : sec= 0.01427, 140.15840[GFLOPS],  0.82124[GB/s]
( 1024- 2):neon                : sec= 0.01227, 162.98274[GFLOPS],  0.95498[GB/s]

まあ5%ぐらいか。何も処理していないことを考えると、ちょっと大きい気もするが、今見るべきでは無いという気もする。


まず演算かメモリを切り分ける。

    __asm__ __volatile__ (".p2align 4\n\t"
                          "1:\n\t"
                          "//vld1.32 {d0,d1}, [%[inL00_0]:64]!\n\t"
                          "//vld1.32 {d2,d3}, [%[inL00_1]:64]!\n\t"
                          "//pld [%[inRp1], %[pld_offset]]\n\t"
                          "//vldmia %[inRp1], {q8-q11}\n\t"

                          "add %[inRp1], %[inRp1], %[pitch_f32]\n\t"

                          "vmla.f32 %q[vout0_0], q8, d0[0]\n\t"
                          "vmla.f32 %q[vout1_0], q8, d2[0]\n\t"
                          "vmla.f32 %q[vout0_1], q9, d0[0]\n\t"

こんな感じにする。わかりづらいが、メモリ関連命令を消している。

( 1024- 0):neon                : sec= 0.22819,   8.76453[GFLOPS],  0.05135[GB/s]
( 1024- 1):neon                : sec= 0.21176,   9.44475[GFLOPS],  0.05534[GB/s]
( 1024- 2):neon                : sec= 0.19515,  10.24869[GFLOPS],  0.06005[GB/s]
( 1152- 0):neon                : sec= 0.26081,  10.91833[GFLOPS],  0.05687[GB/s]
( 1152- 1):neon                : sec= 0.23850,  11.93990[GFLOPS],  0.06219[GB/s]
( 1152- 2):neon                : sec= 0.23796,  11.96672[GFLOPS],  0.06233[GB/s]

こんな感じ…んーなんか普通にメモリネックな気が…


次にロード命令かキャッシュかを切り分ける。


    __asm__ __volatile__ (".p2align 4\n\t"
                          "1:\n\t"
                          "vld1.32 {d0,d1}, [%[inL00_0]:64]\n\t"  // !
                          "vld1.32 {d2,d3}, [%[inL00_1]:64]\n\t"  // !
                          "pld [%[inRp1], %[pld_offset]]\n\t"
                          "vldmia %[inRp1], {q8-q11}\n\t"

                          "add %[inRp1_cnt], %[inRp1_cnt], %[pitch_f32]\n\t" // !

                          "vmla.f32 %q[vout0_0], q8, d0[0]\n\t"
                          "vmla.f32 %q[vout1_0], q8, d2[0]\n\t"
                          "vmla.f32 %q[vout0_1], q9, d0[0]\n\t"
                          "vmla.f32 %q[vout1_1], q9, d2[0]\n\t"
                          "pld [%[inL00_0], #64]\n\t"
                          "vmla.f32 %q[vout0_2], q10, d0[0]\n\t"
                          "vmla.f32 %q[vout1_2], q10, d2[0]\n\t"
                          "vmla.f32 %q[vout0_3], q11, d0[0]\n\t"
                          "vmla.f32 %q[vout1_3], q11, d2[0]\n\t"

                          "pld [%[inRp1], %[pld_offset]]\n\t"
                          "vldmia %[inRp1], {q8-q11}\n\t"
                          "add %[inRp1_cnt], %[inRp1_cnt], %[pitch_f32]\n\t"  // !

こんな感じにする。わかりづらいが、ポインタの加算をやめている。今は、ポインタとループカウンタを一緒にしているので、レジスタ使用が一個増えてしまっているが…まあ無視しよう。(ポインタとループカウンタを共有する手法については、VisualStudioが出すコードを参考にすると良い。あれはいつ見ても良い)

( 1280- 0):neon                : sec= 0.48997,   7.97241[GFLOPS],  0.03737[GB/s]
( 1280- 1):neon                : sec= 0.45807,   8.52755[GFLOPS],  0.03997[GB/s]
( 1280- 2):neon                : sec= 0.49251,   7.93127[GFLOPS],  0.03718[GB/s]
( 1408- 0):neon                : sec= 0.77800,   6.68278[GFLOPS],  0.02848[GB/s]
( 1408- 1):neon                : sec= 0.73396,   7.08381[GFLOPS],  0.03019[GB/s]
( 1408- 2):neon                : sec= 0.73258,   7.09712[GFLOPS],  0.03024[GB/s]

あまり変化が無い。最初のグラフで見た、7GFLOPSは、キャッシュに入るとそのぐらい出そうだというように見える。


あ、pldはキャッシュに乗っててもスループットが悪いとか。消した。まあ変わらん。


ということを考えると、普通にロード命令がネックでは?という気がする。


    __asm__ __volatile__ (".p2align 4\n\t"
                          "1:\n\t"
                          "vld1.32 {d0,d1}, [%[inL00_0]:64]!\n\t"
                          "vld1.32 {d2,d3}, [%[inL00_1]:64]!\n\t"
                          "pld [%[inRp1], %[pld_offset]]\n\t"
                          "vldmia %[inRp1], {q8-q11}\n\t"

                          "add %[inRp1], %[inRp1], %[pitch_f32]\n\t"

                          "//vmla.f32 %q[vout0_0], q8, d0[0]\n\t"
                          "//vmla.f32 %q[vout1_0], q8, d2[0]\n\t"
                          "//vmla.f32 %q[vout0_1], q9, d0[0]\n\t"
                          "//vmla.f32 %q[vout1_1], q9, d2[0]\n\t"
                          "pld [%[inL00_0], #64]\n\t"
                          "//vmla.f32 %q[vout0_2], q10, d0[0]\n\t"
                          "//vmla.f32 %q[vout1_2], q10, d2[0]\n\t"
                          "//vmla.f32 %q[vout0_3], q11, d0[0]\n\t"
                          "//vmla.f32 %q[vout1_3], q11, d2[0]\n\t"

                          "pld [%[inRp1], %[pld_offset]]\n\t"
                          "vldmia %[inRp1], {q8-q11}\n\t"
                          "add %[inRp1], %[inRp1], %[pitch_f32]\n\t"

                          "//vmla.f32 %q[vout0_0], q8, d0[1]\n\t"
                          "//vmla.f32 %q[vout1_0], q8, d2[1]\n\t"
                          "//vmla.f32 %q[vout0_1], q9, d0[1]\n\t"
                          "//vmla.f32 %q[vout1_1], q9, d2[1]\n\t"

演算を消す。まあ、ちゃんとロードしたレジスタに触れてあげないと公平ではないんだが、まあいいや。

( 1024- 0):neon                : sec= 0.33853,   5.90783[GFLOPS],  0.03462[GB/s]
( 1024- 1):neon                : sec= 0.33344,   5.99811[GFLOPS],  0.03515[GB/s]
( 1024- 2):neon                : sec= 0.33037,   6.05381[GFLOPS],  0.03547[GB/s]

は?もしかしてロード命令ってスループット128bit/cycle出ないんじゃね?


ここでやっとマニュアルを見る。


マヌアルの読みかたがわからないのだった。(終わり)

23:19

__attribute__ ((aligned(16))) float data[1024];

int main()
{
  int i = 0;

  for (i=0; i<(1024*1024*1024)/8; i++) {
    asm volatile ("vld1.32 {d0,d1}, [%0]\n\t"
                  "vld1.32 {d2,d3}, [%0]\n\t"
                  "vld1.32 {d0,d1}, [%0]\n\t"
                  "vld1.32 {d2,d3}, [%0]\n\t"

                  "vld1.32 {d0,d1}, [%0]\n\t"
                  "vld1.32 {d2,d3}, [%0]\n\t"
                  "vld1.32 {d0,d1}, [%0]\n\t"
                  "vld1.32 {d2,d3}, [%0]\n\t"

                  "vld1.32 {d0,d1}, [%0]\n\t"
                  "vld1.32 {d2,d3}, [%0]\n\t"
                  "vld1.32 {d0,d1}, [%0]\n\t"
                  "vld1.32 {d2,d3}, [%0]\n\t"

                  "vld1.32 {d0,d1}, [%0]\n\t"
                  "vld1.32 {d2,d3}, [%0]\n\t"
                  "vld1.32 {d0,d1}, [%0]\n\t"
                  "vld1.32 {d2,d3}, [%0]\n\t"

                  :
                  :"r"(data)
                  :"d0","d1","d2","d3");
  }
}

しかたないから自分で書くか…まあマニュアルとか信用できないしね!!(2G 回ロードする)


$ time ./a.out

real    0m1.768s
user    0m1.457s
sys     0m0.013s

は?何そのreal と userの差は。


まあ、1.45秒とすると、 2/1.45 = 1.38 なので、ロードのスループットはちゃんと出る。いや1.38 / 1.805 だぞ…出てると言えるか…?


ごめんなんかtouch_fusionとかいう謎のプロセスがCPU時間10%使っていた。

$ time ./a.out

real    0m1.429s
user    0m1.422s

まあ、うーん…

あ…

$ gcc loadbench.c -mfloat-abi=hard -mfpu=neon -O2
w0@localhost:~/test/arm$ time ./a.out

real    0m1.197s
user    0m1.192s
sys     0m0.003s

$ gcc loadbench.c -mfloat-abi=hard -mfpu=neon -O0
$ time ./a.out

real    0m1.420s
user    0m1.415s
sys     0m0.004s

あのさあ…初歩的なミスとか…やる気あんの?


つまりまとめると…よくわからない?いやわかるよ。最初の3ラインでほぼ時間すべてなんでしょ?

00:34

良くなった(肝心の良くなった時になにをやったか書かないやつ)

f:id:w_o:20141030003312p:image


まあでもこれは真理だと思う。口頭では何回か言ってるが、結局一番大事なのは気合と試行回数だから、文章書きながらとかそもそも無理だったのだ。


でもまだまだだな。なんかL2律速してる印象がある。もう一段ブロッキングがいるな。

結局 02:55

そこから改善しなかった

f:id:w_o:20141030025539p:image

03:22

http://www.7-cpu.com/cpu/Cortex-A15.html

TLBミスかも?L1 32entry で L1ミスが12cycleだとつらい気がする。

そうすると最内はもっと横に伸ばして6x2とかのほうがいいのか?かつK方向ループは16がいいの?

というのは今はRが縦方向に128個ロードしていて、次のループで再利用している。あと64byte使い切ってるから縦でもいいはず。さらにL1が32KB 2way LRUだとすると、512ライン分は入るはずなので、最内は入る気がするんだよな。でも見た感じ入ってる挙動ではない。

トラックバック - http://d.hatena.ne.jp/w_o/20141029

2014-10-28

02:48

そういやルーターちゃん再起動したときにipv6設定面倒で放置してるから俺のことは裏切り者リストに追加しておいてもらっていいよ。

トラックバック - http://d.hatena.ne.jp/w_o/20141028

2014-10-26

99.7msec 13:05

充実した週末だった。


しかしARMやりすぎたせいでCortexA15とSilvermontの差が開きすぎている。SSEも詰め詰めしたくなってくるな…


あとFMA向けにチューンしすぎたせいでAVX版でレジスタスピルしている。

libmatmul-bench 20:11

ほい作った。

http://int.main.jp/files/matmul-bench-w64.exe

http://int.main.jp/files/matmul-bench-w32.exe

w32版はなんかomp入れるとsse版が動かなくてよくわからなかったのでw32版は1threadになっているので、w64版を使った方が良い。



あとインターフェースはライブラリ化しておいたのでみんなも自作アプリにmatmul FLOPS計測機能を組み込もう!

http://int.main.jp/files/libmatmul-bench.zip

使い方はそのうち書くが、今すぐFLOPS計測機能が必要で困っている人は

https://github.com/tanakamura/matmul-bench/blob/master/matmul-bench-main.c

を参考にすると良いよ。

あと 20:13

そろそろ飽きてきたしWin用とAndroid用にUI作って終わりかな。

matmul-bench 使いかた 20:29

基本的にはexeを実行するだけでよい。


出たFLOPSを見てニヤニヤする。


"-o a.csv" とすると、csvが出るので、これをexcelに貼ると、

https://onedrive.live.com/view.aspx?resid=ECB59E566C2D71F1!4318&app=Excel&authkey=!AKCmyGT1nO0VfGI

こういうグラフが描ける(theoretical peak は自分で入力した)


"-I" を付けると、動くベンチマークの情報が出る。size_step は、サイズの制約を示している。サイズはこれの倍数である必要がある。


"-t" で動かすベンチマークを指定する。付けないと全部動く。


"-n" でサイズを一個指定できる。指定しない場合は、"-s" で指定したサイズステップ数ずつ増やしていって、一回の処理時間が "-T" で示す値を超えるまで繰り返す。

size step は、実際には、各ベンチマークのsize_stepに合うように調整されるので、ここで指定した値になるわけではない。


"-O" で omp thread 数を指定する。


"-i" で各ベンチマークを実行する回数を指定する。CSVに出るのは、繰り返して出た値のうち一番大きいものが入っている。

HANAGEHANAGE 2014/10/27 18:32 float * __restrict outなら、素人的には出力を_mm_stream_psとかで書き出す方がいいと思うんですが、直感的に。

HANAGEHANAGE 2014/10/27 18:41 outから読み込んでいましたね、すみません失礼しました。

w_ow_o 2014/10/27 21:46 まあ、最後(k0==n-1)ぐらいはやってもいいかもと思って試しましたが特に変わらなかったですね…

トラックバック - http://d.hatena.ne.jp/w_o/20141026

2014-10-25

01:43

https://twitter.com/takehiro_t/status/525672008869085184

この情報もらってZynq Cortex A9 667MHz 2コアで123[msec]までいった。Epiphany16コアはサンプルのだと170[msec]なのでEpihanyとはなんだったのか。まああのサンプルもまだまだ良くできる気がするけど。

03:37

x86 fmaもTurbo Boost運がよければ300(75%)超えるようになってきた。

結局まだやってる 23:27

103.5msec…まあでもそろそろ限界感ある。

トラックバック - http://d.hatena.ne.jp/w_o/20141025

2014-10-24

09:38

https://github.com/tanakamura/matmul-bench

いちおうHaswellで(Turbo Boostありだけど)50%いった。まあx64版はとりあえずいいだろ。

あとは

  • 512x512 200msecの A9 版を 150msec にしたい(Epiphanyに勝てるまで)
    • A9かなりよくわからないのでちゃんと理解する
  • libにする
  • UIを作る
  • A15かA9をもうちょっと詰める
  • Epiphany版も作って詰める
  • メモリ転送理解して入力巨大なときに性能落ちないようにする
  • HD Graphics版もできるなら
トラックバック - http://d.hatena.ne.jp/w_o/20141024

2014-10-21

beignet 03:37

あ、これ出力asm見れるのか。


Kernel BuilderはGPUだとasm見れないから諦めていた。


$ export OCL_OUTPUT_ASM=1
$ ./a.out
    <.. snip ..>
hello's disassemble begin:
  L0:
    (       0)  mov(16)         g2<1>:UW        0xffffUW                        { align1 WE_all 1H };
    (       2)  mov(16)         g2<1>:UW        0x0UW                           { align1 WE_normal 1H };
    (       4)  mov(1)          g1<2>:UW        0x0UW                           { align1 WE_normal };
    (       6)  mov(1)          g1.2<2>:UW      -1W                             { align1 WE_normal };
    (       8)  cmp.le(16)      null:UW         g2<8,8,1>:UW    0x0UW           { align1 WE_all 1H switch };
    (      10)  (+f0) if(16) 70                                                 { align1 WE_normal 1H };
    <.. snip ..>
    (      95)  nop                                                             ;
    (      97)  nop                                                             ;
    (      99)  nop                                                             ;

とかになる。

vs Parallella 21:17

なんか会社でParallellaを使ってみようみたいな流れになって使っていたのだが、(別に業務とは関係なく)

https://github.com/adapteva/epiphany-examples/tree/master/apps/matmul-16

を動かして、Host 1700msec、Epiphany 170msecとかになって、さすが、16coreだから10倍速いみたいな話になったが、経験的に、こういうのってナイーブCと比較してるから、普通にマルチスレッド & NEON使えば、10倍差ぐらいすぐはやくなんじゃね?と、思ってNEON + スレッド化matmulを探したのだけど、見当たらなくて、探すより書いたほうがはやそうだったので書いた。

https://github.com/tanakamura/matmul-bench

一応 Parallella ボードに付いてる Cortex A9 667MHz 2core で、 330[msec] ぐらいまで行ったので、あと倍になればEpiphanyに追い付けるはず。効率17%だからまあ根気良くチューニングすればいけると思うけど。


しかし巷のBLASの効率とか見てると、gemm効率90%以上出せない奴は人間じゃないみたいな雰囲気だったのでもうちょっとなんとかしたい。


まあ大分わかってきた。レジスタもブロッキングしないといけないんでしょ?まあどうしたらいいかわからんけど。


ちなみに、https://github.com/tanakamura/matmul-bench のホスト側のコードはそんなにナイーブでもなくて、真面目にナイーブに書くと12秒ぐらいだった。


あとEpiphanyは結局全く触れてない。

トラックバック - http://d.hatena.ne.jp/w_o/20141021

2014-10-15

プログラミング難しい 08:17

比較と状態遷移が難しい。


年をとるごとに、自分がプログラマとして能力が高まりつつあるのを感じる。


何故、年を取ると能力が高まっていくのかというと、将来への明るい希望が減ってくると、「自分は優秀だ」という勘違いぐらいしかすがるものが無くなるからなのだった。だから、多分俺は今後も年を取るにつれて能力が高まっていくと思う。


さて、そんな優秀な俺なので、処理内容を理解している場合は、プログラムの実装時に困る、ということはほぼ無くなっているという気がする。

(まあ、実装以外の面で、処理わからんとか、アルゴリズムわからんとか、論文全く読めないとかで停止することはよくあるので、別にプログラミング能力の高まりに対応して仕事がサクサク進むというわけでもないのだけど)


しかし、それでも、やっぱりプログラム難しいと思う点が、最後に二点残った。これは、

  • 比較が < なのか <= なのか?
  • 状態遷移ちゃんと書けたのか?

の、二点である。


まず、比較のほう。

例として、文字列から文字を探して、それが終端を超えてなければ、return 1 という関数があるとする。

// start_ptr 〜 end_ptrの間で'a'の文字が入っているなら1を返す
int find_a(char *start_ptr, char *end_ptr) {
    while (*(start_ptr++) != 'a')
        ;

    if (start_ptr ??? end_ptr) {
        return 1;
    }
    return 0;
}

さて、この時、??? に入る比較は、'<'か? '<='か? という問題である。


なお、どちらとも言えない、が正解だった。end_ptr は、終端の文字を指すか、C言語の雰囲気的に(end_ptr - start_ptr で長さが出るので)終端の次の文字を指すか、が決まっていないので、回答は一意には定まらない。


この問題は、いっっっっっつも、なんか、「えーとーーどっちだっけーーー」とか考えてる気がする。自分だけで書いてるときは習慣として、end_ptrは終端の次の文字、比較は '<' と心に決めているのだが、それでも、いっつも悩んでいるし、他人が書いた処理が返してきたポインタだった場合は、さらに悩んでいる。

そして、さんざん悩んだ割に、一定確率で間違いを挿入しているという気がする。(先週もやった)


同じような問題はあちこちにあって、2D画像に対して畳み込みフィルタ入れるとき、カーネルサイズが奇数だった場合に処理してよい範囲は、いっつも悩んでいるし、時々間違えるし、さらにそれをSIMDにする時に、間違いをもう二回ぐらい追加しているという気がする。




さて、次に、状態遷移だが、これは、もう、俺は覚悟を決めた。プログラミング学習を通じて理解した重要な事実のひとつは、「状態遷移をきれいに書く方法はこの世には存在しない」ということだった。

状態遷移は、どんなに面倒でも、それをプログラムに記述する以外の方法は無いし、新しいイベント、状態を追加するときは、新しいバグを一個追加するのだ、という気持ちで臨むことが大事である。

状態遷移は、処理が最悪の場合、イベント数M、状態数Nだと、処理の数は、O(MN)になる。本来は、状態を追加するときは、全てのイベントの組みあわせと動作確認する必要があるし、イベントを追加する時も同じように状態の数だけ動作確認する必要がある。

もちろん、いくらかは構造化できるから、実際に、O(MN)になることはほぼ無いけど、今度は、そういう構造化を入れてしまうと、それが本当に、全ての状態/イベントの組み合わせを網羅できているのか?という問題が出てくる。そういうのは、コードを見てもよくわからないし、ちょろっとテストするだけではよくわからん場合が多い(そもそもテストパターン思い付くなら対応してるはずだし)

というわけで結論としては、状態遷移処理は、どうやってもバグが入るから、解析とデバッグしやすいように、状態を一個の構造体に入れて、処理を一個の関数でまとめるとわかりやすいということになった。(もちろん、実際には共通部分分けたりぐらいはするが)



このふたつの問題の共通点は、間違っていても大体の場合動いてしまうというのがある。もちろん、正しくは、境界条件探して、テスト作って、動いているかどうか検証すべきなんだけど、関数への入力の整合性とらないといけない場合とか入力データ作るの面倒だし、そんな末端の条件分岐一個のために、全体が正しく動くデータ作るのとか面倒だし…

あと、問題がプリミティブなので、一般的な方法で対応をとるのが難しく、結局一個一個チェックする以外の方法が無いというのもありますね。



というわけで、プログラミング難しいという話だった。


状態遷移難しいという話に関連して、次回、MVCとかのMVなんとかフレームワークが何故クソなのか、という話について書こうと思う。

16.7msecでできる仕事 21:46

時間と金は絶対値を見るべきだと思う。


プログラムの高速化は、真面目にやると計測の問題になる。

高速化の作業は、実際にはプログラムの処理に対して、実際にかかっている時間が妥当なのかどうかを調べて、妥当でない場合は原因を調べるという、一種のデバッグみたいな作業になる。


この作業をするとき、処理時間を絶対値で見ることが重要だと思う。


高速化ではプロファイラを使ってボトルネックを調べましょうとかあるが、あれはあくまで第一歩で、実際に修正するには、かかっている時間が妥当かどうかを知る必要がある。それを調べるには、妥当な絶対時間と、今の処理の絶対時間というのを把握しておかないといけない。

例えば、「100MBのファイルを読む時間が全体の90%を占める、IOネックなのでこれ以上早くならない」とか言われても、それが妥当かどうかはわからない。これが、「100MBのファイルを読むのに、今は5秒かかっている」とか言われると、まあ、今のOSのファイルシステム経由の連続読み書きだと、数十MB/sぐらいなので、100MBで5秒は、妥当かどうかは、調べないとわからない際どいラインだというのがわかる。つまり結局調べないとよくわからないということがわかるのだった(声に出して読みたい不自由な日本語)


僕は、定規としてわかりやすいのは、16.7msecだと思っている。


処理時間を考えるときは、常に最新ゲームのゲーム画面を思い浮かべる。


最新ゲームは、16.7msecで、4Kの画像の各ドット対してそれなりに演算量のあるシェーダを回して、オブジェクトの各頂点にもシェーダを回して、ゲーム中に多数存在する生物のAIが経路探索し、物体の剛体衝突判定を行い、背景の流体を動かして、(だましだましとはいえ)世界中の数十人がネットワークを通じてリアルタイム対戦して、ゲーム実況者が音声を入れているのである。

0.5秒かかる処理があったとする。0.5秒というと一瞬に見えるが、ゲームなら、30フレーム分処理が動いている。はたして、あなたの、その、0.5秒かかっているその処理は、最新ゲーム画面30フレーム分と比べて、同じだけの処理をしていると言えるのか、言えないのか、とか、は、あまり考えないようにしようと思った。まあみんないそがしからね。そんないちーち細かいところまで妥当かどうかとかカンガエナイホウガヨイネ。


同じような問題として、お金も絶対値で見るべきというのがある。


一億円プロジェクトだと、100万円は、1%でしかない、が、100万は大金であるというのを忘れるべきではないと思う。100万円の価値は、1兆円プロジェクトでも、100万円プロジェクトでも、同じ価値だ。常に、100万円あったら何ができるか、というのを考えるべきだと思う。100万あれば、人間ひとり1ヶ月動かせるのである…


と、でも思ったか!??


たかが、100万で、人間を自由に動かせるという考えが甘いブラックだということに気付くべきでは??1ヶ月は30日であって、16.67msec 1フレだとすると、1ヶ月は、155520000 フレーム分の処理である。1人月(ピー)万と言うが、その仕事は、バッフィー 155520000 フレーム分の価値があるのか? 155520000 回分、4Kシェーッダーーーが動いてるんだぞ???そもそも、よく考えろ、俺が、人間が、意思を持った一個の生物が、金で自由に動かせると思ったら大間違いだぞ!!こんな人権を現金に変換する仕事なんてやめてやる!!!俺は!自由に!なるんだ!!!!


まとめ: 一般に、無職のほうが追い詰められて自由度は低くなることが多いので仕事やめて自由になろうという考えは危険です。

訂正 19:42

一ヶ月のフレーム数をまちがえていた

トラックバック - http://d.hatena.ne.jp/w_o/20141015

2014-10-09

子供ブラック 00:04

子供の頃「これが仕事だ」と感じていたような仕事は、ブラックな仕事ばっかだったな、とか思う。

飲食店、教師、警官、公務員、建設現場、消防士、医者…などなど。


まあ、今になってわかるけど、基本的に個人客と触れあうBtoCはブラックだから子供に見える仕事は基本的にブラックだよな。

BtoBだと、客のほうも、金の払う価値のある奴隷を探すのが大変だから、客のほうも問題のある行動をとりづらいけど、BtoCの客は多少無茶を言って店のほうから来るなと言われても別の店に行けばいいだけだから、まあ、そのへんで、BtoC奴隷はランク下に見られるレベルが高い。

単に、代替がある仕事はブラックになりやすという話だが…(いや医者とか代替無くね?)


それはいいとして、大企業に数十年単一製品をOEMしてるとか、なんか、そういう企業の仕事のほうが理不尽な辛さがないというのを就職前の人達に理解してもらうことは可能なんだろうか。


今の僕は、プロジェクトの下回りだけやってればいいSIerとかホワイトすぎる(能力あたりのホワイトレベルの比較です)とかわかるけど、別に最初に就職したときにそういうのをわかっていたわけではないしな。

トラックバック - http://d.hatena.ne.jp/w_o/20141009

2014-10-03

トラックバック - http://d.hatena.ne.jp/w_o/20141003
 |