更新:
全部の実験で命令の総数を揃えました。
分かりにくくてすみません……
計算機が全くわからない。
俺たちは勘で計算している。
どなたかこの解釈について教えてほしいです。。。。。
雑にIPCとか測ったらどうなるかなーという願望が湧いたのでひたすら計測する。
doubleの加算
.global main .type main, %function main: push {r3, lr} bl test mov r0, #0 pop {r3, pc} .global test .type test, %function test: mov r0, #0 ldr r3, =70000000 loop: faddd d0, d0, d1 faddd d0, d0, d1 faddd d0, d0, d1 faddd d0, d0, d1 faddd d0, d0, d1 faddd d0, d0, d1 faddd d0, d0, d1 faddd d0, d0, d1 faddd d0, d0, d1 faddd d0, d0, d1 add r0, r0, #1 cmp r0, r3 bne loop bx lr
だいたいやっていることは64bit浮動小数点の加算を10 * 7000000 = 7 * 10 ^ 8
回やる。
パイプラインを止めるためにレジスタに依存があるに書く。
ハードウエアのことはわからないけど多分これはregister renaming出来ない気がする。
ちなみにldr
は疑似命令で、定数のmovみたいなものに見えるが実際はどっかのアドレスからまさにldr
になっているはず(objdumpすればホントに命令が見える)。
push, popも疑似命令でpush {r3, lr}
でldmfd r13!,{r3,lr}
の略記だった気がする。
r13
レジスタはスタックポインタとして使われる汎用レジスタなのでまぁ必要な値を避難するのがpush
で
pop
はlr
に元いた場所のアドレスが書いてあってそれをpc
に書き戻すのだからret
の動きをする。
ラベルloop:
で10回倍精度浮動小数点の加算を行い、cmp
とbne
で1回条件分岐があるけど
これくらいは分岐予測で殆どペナルティはないと考える。
実行命令数を7 * 10 ^ 8
とちょっと変にしているのは狙ってのことで、
雑に/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq
を見てみると
700000
と書いてあるため(ちなみに私のraspi 3のディストリはraspi版のcentos 7.3)。
この表記はKHzなので最大700MHzで動作する設定になっている。
あれ、Cortex A-53って最大1.2GHzで動作するんじゃなかったっけ。。。。。
あれ、これCortex A-7だと認識されている?
$ ls /sys/devices/armv7_cortex_a7/ # なんかある。。。。。
これをCPU稼働率MAXまでなって欲しいなーという気持ちで連続で動かして実行時間を測る。
ほんとはcpupower
とかで周波数を固定するべきな気がする。
[nomaddo@centos-rpi3 tmp]$ for i in `seq 1 100`; do time ./a.out ; done real 0m4.199s user 0m4.190s sys 0m0.000s real 0m4.022s user 0m4.010s sys 0m0.000s real 0m4.022s user 0m4.010s sys 0m0.010s real 0m4.022s user 0m4.000s sys 0m0.010s real 0m4.022s user 0m4.010s sys 0m0.000s real 0m4.022s user 0m4.010s sys 0m0.000s real 0m4.011s user 0m4.000s sys 0m0.000s real 0m4.010s user 0m4.000s sys 0m0.000s real 0m4.010s user 0m4.000s sys 0m0.000s real 0m4.010s user 0m4.000s sys 0m0.000s ...
おお?なんかきれいになった。計算の都合0.4秒かかったことにしよう!
全体の命令数の総数をサイクル数で割って、(7 * 10 ^ 8) / (7 * 10 ^ 8 * 0.4) = 2.5
。
おお? 2.5 命令/cycle? なんか早すぎる気もするんですが現代の計算機ってこんなもんなんですかね……?
命令間の依存がないとどうなる?1
気になったので依存がないモードもやってみる。
.global main .type main, %function main: push {r3, lr} bl test mov r0, #0 pop {r3, pc} .global test .type test, %function test: mov r0, #0 ldr r3, =70000000 loop: faddd d1, d2, d3 faddd d4, d5, d6 faddd d7, d8, d9 faddd d10, d11, d12 faddd d13, d14, d15 faddd d1, d2, d3 faddd d4, d5, d6 faddd d7, d8, d9 faddd d10, d11, d12 faddd d13, d14, d15 add r0, r0, #1 cmp r0, r3 bne loop bx lr
fadd
の列に全く依存がないためきっと内部の計算資源をフルで使える気がする。
[nomaddo@centos-rpi3 tmp]$ for i in `seq 1 100`; do time ./a.out ; done real 0m0.827s user 0m0.820s sys 0m0.000s real 0m0.711s user 0m0.700s sys 0m0.000s real 0m0.708s user 0m0.700s sys 0m0.000s real 0m0.706s user 0m0.700s sys 0m0.000s real 0m0.705s user 0m0.700s sys 0m0.000s real 0m0.708s user 0m0.700s sys 0m0.000s real 0m0.705s user 0m0.700s sys 0m0.000s ...
命令間の依存がないとどうなる?2
こんなすぐに1ループ回ると条件分岐の影響を強く受けそうだなと思ったので別バージョン。
演算総数は変わらないけど、1ループで20回倍精度の浮動小数点数の加算が動くようにする。
.global main .type main, %function main: push {r3, lr} bl test mov r0, #0 pop {r3, pc} .global test .type test, %function test: mov r0, #0 ldr r3, =35000000 loop: faddd d1, d2, d3 faddd d4, d5, d6 faddd d7, d8, d9 faddd d10, d11, d12 faddd d13, d14, d15 faddd d1, d2, d3 faddd d4, d5, d6 faddd d7, d8, d9 faddd d10, d11, d12 faddd d13, d14, d15 faddd d1, d2, d3 faddd d4, d5, d6 faddd d7, d8, d9 faddd d10, d11, d12 faddd d13, d14, d15 faddd d1, d2, d3 faddd d4, d5, d6 faddd d7, d8, d9 faddd d10, d11, d12 faddd d13, d14, d15 add r0, r0, #1 cmp r0, r3 bne loop bx lr
実行してみる。楽しみだなー。
[nomaddo@centos-rpi3 tmp]$ for i in `seq 1 100`; do time ./a.out ; done real 0m0.706s user 0m0.710s sys 0m0.000s real 0m0.710s user 0m0.700s sys 0m0.000s real 0m0.649s user 0m0.640s sys 0m0.000s real 0m0.605s user 0m0.600s sys 0m0.000s real 0m0.608s user 0m0.600s sys 0m0.000s real 0m0.605s user 0m0.600s sys 0m0.000s real 0m0.611s user 0m0.600s sys 0m0.000s real 0m0.606s user 0m0.600s sys 0m0.000s real 0m0.611s user 0m0.600s sys 0m0.000s real 0m0.605s user 0m0.600s sys 0m0.000s
命令間の依存がないとどうなる?3
調子に乗って4展開してみる。
.global main .type main, %function main: push {r3, lr} bl test mov r0, #0 pop {r3, pc} .global test .type test, %function test: mov r0, #0 ldr r3, =17500000 loop: faddd d1, d2, d3 faddd d4, d5, d6 faddd d7, d8, d9 faddd d10, d11, d12 faddd d13, d14, d15 faddd d1, d2, d3 faddd d4, d5, d6 faddd d7, d8, d9 faddd d10, d11, d12 faddd d13, d14, d15 faddd d1, d2, d3 faddd d4, d5, d6 faddd d7, d8, d9 faddd d10, d11, d12 faddd d13, d14, d15 faddd d1, d2, d3 faddd d4, d5, d6 faddd d7, d8, d9 faddd d10, d11, d12 faddd d13, d14, d15 faddd d1, d2, d3 faddd d4, d5, d6 faddd d7, d8, d9 faddd d10, d11, d12 faddd d13, d14, d15 faddd d1, d2, d3 faddd d4, d5, d6 faddd d7, d8, d9 faddd d10, d11, d12 faddd d13, d14, d15 faddd d1, d2, d3 faddd d4, d5, d6 faddd d7, d8, d9 faddd d10, d11, d12 faddd d13, d14, d15 faddd d1, d2, d3 faddd d4, d5, d6 faddd d7, d8, d9 faddd d10, d11, d12 faddd d13, d14, d15 add r0, r0, #1 cmp r0, r3 bne loop bx lr
nomaddo@centos-rpi3 tmp]$ for i in `seq 1 100`; do time ./a.out ; done real 0m0.648s user 0m0.640s sys 0m0.000s real 0m0.622s user 0m0.610s sys 0m0.010s real 0m0.558s user 0m0.550s sys 0m0.010s real 0m0.557s user 0m0.550s sys 0m0.000s real 0m0.556s user 0m0.550s sys 0m0.000s real 0m0.559s user 0m0.550s sys 0m0.000s real 0m0.561s user 0m0.550s sys 0m0.000s real 0m0.561s user 0m0.550s sys 0m0.000s real 0m0.555s user 0m0.550s sys 0m0.000s ...
おもったよりしっかり分岐のペナルティがあったのか????
なんかもう真率がたかいループだからてっきりほぼ100%くらい分岐予測に成功するのかと思っていた。
計算したは良いけどこれがあっているのか全くわからない感じでした。
色々仮説を立てて検証してみよう。。。。。。。