サイクルタイムを測定しよう (12) [FMx]
前回は、ループの配置アドレスを変えながら、サイクル数を測定しました。 今回は、ループ内の命令数によってサイクル数に差が出るかについて調べました。
ソースコード
ループ内の命令数を変化させるために、ループ内に "nop" 命令を追加します。 以下のコードで、 "nop" 命令の数を調整します。
void func9(reg8_t *reg, uint8_t val0, uint8_t val1) @ ".text.func9" { for (;;) { __asm( "nop\n" // 29 "nop\n" // 28 "nop\n" // 27 "nop\n" // 26 "nop\n" // 25 "nop\n" // 24 "nop\n" // 23 "nop\n" // 22 "nop\n" // 21 "nop\n" // 20 "nop\n" // 19 "nop\n" // 18 "nop\n" // 17 "nop\n" // 16 "nop\n" // 15 "nop\n" // 14 "nop\n" // 13 "nop\n" // 12 "nop\n" // 11 "nop\n" // 10 "nop\n" // 9 "nop\n" // 8 "nop\n" // 7 "nop\n" // 6 "nop\n" // 5 "nop\n" // 4 "nop\n" // 3 "nop\n" // 2 "nop\n" // 1 "label_2:" ); *reg = val0; *reg = val1; } }
このコードでは、ループの配置アドレスは固定されますが、代わりに分岐命令のアドレスが変化していきます。
50 void func9(reg8_t *reg, uint8_t val0, uint8_t val1) @ ".text.func9" { \ func9: (+1) \ 00000000 0xB500 PUSH {LR} 83 for (;;) { 84 __asm( 110 "nop\n" // 4 111 "nop\n" // 3 112 "nop\n" // 2 113 "nop\n" // 1 114 "label_2:" 115 ); \ ??func9_0: (+1) \ 00000002 0xBF00 nop \ 00000004 0xBF00 nop \ 00000006 0xBF00 nop \ 00000008 0xBF00 nop 116 *reg = val0; \ ??label_2: (+1) \ 0000000A 0x7001 STRB R1,[R0, #+0] 117 *reg = val1; \ 0000000C 0x7002 STRB R2,[R0, #+0] \ 0000000E 0xE7F8 B ??func9_0 118 } 119 }
上記は、 "nop" 命令を4個入れた場合のコードです。 分岐先アドレスは "0002" のままですが、分岐命令は "000E" に移動しています。
測定結果
サイクル数を測定しました。 "nop" ひとつあたり1サイクルほど命令実行時間が増えますので、 "nop" による影響を除いたサイクル数を計算しました。
NOP数 | サイクル数 (除NOP) | 分岐先アドレス | 分岐命令 |
---|---|---|---|
0 | 4 | 02 | 06 |
1 | 4 | 02 | 08 |
2 | 4 | 02 | 0A |
3 | 4 | 02 | 0C |
4 | 4 | 02 | 0E |
5 | 6 | 02 | 10 |
6 | 6 | 02 | 12 |
7 | 6 | 02 | 14 |
8 | 6 | 02 | 16 |
9 | 6 | 02 | 18 |
10 | 6 | 02 | 1A |
11 | 6 | 02 | 1C |
12 | 6 | 02 | 1E |
13 | 7 | 02 | 20 |
14 | 7 | 02 | 22 |
15 | 7 | 02 | 24 |
16 | 7 | 02 | 26 |
17 | 7 | 02 | 28 |
18 | 7 | 02 | 2A |
19 | 7 | 02 | 2C |
20 | 7 | 02 | 2E |
21 | 8 | 02 | 30 |
22 | 8 | 02 | 32 |
23 | 8 | 02 | 34 |
24 | 8 | 02 | 36 |
25 | 8 | 02 | 38 |
26 | 8 | 02 | 3A |
27 | 8 | 02 | 3C |
28 | 8 | 02 | 3E |
29 | 9 | 02 | 40 |
8命令(16バイト)ごとにサイクル数が一つずつ増えているのが分かります。 言い方を変えると、8個の "NOP" を実行するために9サイクルの時間を要しているのです。 ちょっと、効率が悪くありませんか?
推測するに、16バイト境界を超えるごとに命令フェッチに1サイクルの実行時間が必要となっていると考えられます。 通常、プリフェッチとパイプラインの構造がうまく働いていれば、命令フェッチの時間はパイプラインに隠れてしまうはずです。 しかし、この実験結果からは、命令フェッチが見えてしまっています。 しかも、命令フェッチサイクルが増えるのは、分岐命令アドレスの下4ビットが 0000 になった時です。 本当にプリフェッチしていないのでしょうか?
コメント 0