マシンコードの正確なコピーは、元の関数よりも50%遅く実行されます


11

組み込みシステムのRAMとフラッシュメモリからの実行を少し試しました。迅速なプロトタイピングとテストのために、私は現在Arduino Due(SAM3X8E ARM Cortex-M3)を使用しています。私が見る限り、Arduinoランタイムとブートローダーはここでは何の違いもないはずです。

ここに問題があります:ARM Thumb Assemblyで記述された関数(calc)があります。calcは数値を計算して返します。(> 1s runtime for the given input)次に、その関数のアセンブルされたマシンコードを手動で抽出し、それを生のバイトとして別の関数に入れました。両方の機能がフラッシュメモリに存在することが確認されています(アドレス0x80149および0x8017D、隣り合っています)。これは、逆アセンブルとランタイムチェックの両方で確認されています。

void setup() {
  Serial.begin(115200);
  timeFnc(calc);
  timeFnc(calc2);
}

void timeFnc(int (*functionPtr)(void)) {
  unsigned long time1 = micros();

  int res = (*functionPtr)();

  unsigned long time2 = micros();
  Serial.print("Address: ");
  Serial.print((unsigned int)functionPtr);
  Serial.print(" Res: ");
  Serial.print(res);
  Serial.print(": ");
  Serial.print(time2-time1);
  Serial.println("us");

}

int calc() {
   asm volatile(
      "movs r1, #33 \n\t"
      "push {r1,r4,r5,lr} \n\t"
      "bl .in \n\t"
      "pop {r1,r4,r5,lr} \n\t"
      "bx lr \n\t"

      ".in: \n\t"
      "movs r5,#1 \n\t"
      "subs r1, r1, #1 \n\t"
      "cmp r1, #2 \n\t"
      "blo .lblb \n\t"
      "movs r5,#1 \n\t"

      ".lbla: \n\t"
      "push {r1, r5, lr} \n\t"
      "bl .in \n\t"
      "pop {r1, r5, lr} \n\t"
      "adds r5,r0 \n\t"
      "subs r1,#2 \n\t"
      "cmp r1,#1 \n\t"
      "bhi .lbla \n\t"
      ".lblb: \n\t"
      "movs r0,r5 \n\t"
      "bx lr \n\t"
      ::
   ); //redundant auto generated bx lr, aware of that
}

int calc2() {
  asm volatile(
    ".word  0xB5322121 \n\t"
    ".word  0xF803F000 \n\t"
    ".word  0x4032E8BD \n\t"
    ".word  0x25014770 \n\t"

    ".word  0x29023901 \n\t"
    ".word  0x800BF0C0 \n\t"
    ".word  0xB5222501 \n\t"
    ".word  0xFFF7F7FF \n\t"
    ".word  0x4022E8BD \n\t"
    ".word  0x3902182D \n\t"
    ".word  0xF63F2901 \n\t"
    ".word  0x0028AFF6 \n\t"
    ".word  0x47704770 \n\t"
  );
}

void loop() {

}

Arduino Dueターゲットでの上記のプログラムの出力は次のとおりです。

Address: 524617 Res: 3524578: 1338254us
Address: 524669 Res: 3524578: 2058819us

したがって、結果が等しくなり、実行時のアドレスが期待どおりであることを確認します。手動で入力したマシンコード機能の実行は50%遅くなります。

arm-none-eabi-objdumpを使用して逆アセンブルすると、それぞれのアドレス、フラッシュメモリの常駐、マシンコードの同等性(エンディアンとバイトのグループ化に注意!)がさらに確認されます。

00080148 <_Z4calcv>:
   80148:   2121        movs    r1, #33 ; 0x21
   8014a:   b532        push    {r1, r4, r5, lr}
   8014c:   f000 f803   bl  80156 <.in>
   80150:   e8bd 4032   ldmia.w sp!, {r1, r4, r5, lr}
   80154:   4770        bx  lr

00080156 <.in>:
   80156:   2501        movs    r5, #1
   80158:   3901        subs    r1, #1
   8015a:   2902        cmp r1, #2
   8015c:   f0c0 800b   bcc.w   80176 <.lblb>
   80160:   2501        movs    r5, #1

00080162 <.lbla>:
   80162:   b522        push    {r1, r5, lr}
   80164:   f7ff fff7   bl  80156 <.in>
   80168:   e8bd 4022   ldmia.w sp!, {r1, r5, lr}
   8016c:   182d        adds    r5, r5, r0
   8016e:   3902        subs    r1, #2
   80170:   2901        cmp r1, #1
   80172:   f63f aff6   bhi.w   80162 <.lbla>

00080176 <.lblb>:
   80176:   0028        movs    r0, r5
   80178:   4770        bx  lr
}
   8017a:   4770        bx  lr

0008017c <_Z5calc2v>:
   8017c:   b5322121    .word   0xb5322121
   80180:   f803f000    .word   0xf803f000
   80184:   4032e8bd    .word   0x4032e8bd
   80188:   25014770    .word   0x25014770
   8018c:   29023901    .word   0x29023901
   80190:   800bf0c0    .word   0x800bf0c0
   80194:   b5222501    .word   0xb5222501
   80198:   fff7f7ff    .word   0xfff7f7ff
   8019c:   4022e8bd    .word   0x4022e8bd
   801a0:   3902182d    .word   0x3902182d
   801a4:   f63f2901    .word   0xf63f2901
   801a8:   0028aff6    .word   0x0028aff6
   801ac:   47704770    .word   0x47704770
}
   801b0:   4770        bx  lr
    ...

同様に使用される呼び出し規約をさらに確認できます。

00080234 <setup>:
void setup() {
   80234:   b508        push    {r3, lr}
  Serial.begin(115200);
   80236:   4806        ldr r0, [pc, #24]   ; (80250 <setup+0x1c>)
   80238:   f44f 31e1   mov.w   r1, #115200 ; 0x1c200
   8023c:   f000 fcb4   bl  80ba8 <_ZN9UARTClass5beginEm>
  timeFnc(calc);
   80240:   4804        ldr r0, [pc, #16]   ; (80254 <setup+0x20>)
   80242:   f7ff ffb7   bl  801b4 <_Z7timeFncPFivE>
}
   80246:   e8bd 4008   ldmia.w sp!, {r3, lr}
  timeFnc(calc2);
   8024a:   4803        ldr r0, [pc, #12]   ; (80258 <setup+0x24>)
   8024c:   f7ff bfb2   b.w 801b4 <_Z7timeFncPFivE>
   80250:   200705cc    .word   0x200705cc
   80254:   00080149    .word   0x00080149
   80258:   0008017d    .word   0x0008017d

これは、ある種の投機的なフェッチ(Cortex-M3には一見ありえる!)または割り込みが原因である可能性があります。(編集:いいえ、できません。おそらく何らかのプリフェッチ)実行の順序を変更したり、間に関数呼び出しを追加しても、結果は変わりません。ここで犯人は何でしょうか?


編集:マシンコード機能のアラインメントを変更した後(プロローグとしてnopsを挿入)、私は次の結果を得ます:

+16ビット(calc2の場合):

Address: 524617 Res: 3524578: 1102257us
Address: 524669 Res: 3524578: 1846968us

+32ビット(calc2の場合):

Address: 524617 Res: 3524578: 1102257us
Address: 524669 Res: 3524578: 1535424us

calc2の+48ビット:

Address: 524617 Res: 3524578: 1102155us
Address: 524669 Res: 3524578: 1413180us

+64ビット(calc2の場合):

Address: 524617 Res: 3524578: 1102155us
Address: 524669 Res: 3524578: 1346606us

calc2の+80ビット:

Address: 524617 Res: 3524578: 1102145us
Address: 524669 Res: 3524578: 1180105us

EDIT2:実行中の計算のみ:

Address: 524617 Res: 3524578: 1102155us

calc2のみ実行:

Address: 524617 Res: 3524578: 1102257us

順序の変更:

Address: 524669 Res: 3524578: 1554160us
Address: 524617 Res: 3524578: 1102211us

EDIT3:計算のみの.p2align 4前にラベル.inを追加、個別に実行:

Address: 524625 Res: 3524578: 1413185us

元のベンチマークと同様:

Address: 524625 Res: 3524578: 1413185us
Address: 524689 Res: 3524578: 1535424us

EDIT4:フラッシュ内の位置を反転すると、結果が完全に変わります。->線形プリフェッチ?


コメントは詳細な議論のためのものではありません。この会話はチャットに移動さました
Samuel Liew

回答:


4

フラッシュからのコード実行の速度は、各分岐ターゲットの待機サイクル数とコードアライメントに依存します。このプロセッサおよびSTM32F103などの同様のプロセッサでは、コアが最も高い周波数で実行される場合、フラッシュは3ウェイトサイクルを必要とします。つまり、分岐ごとに2〜5サイクルかかる可能性があり、合計実行時間に影響を与える可能性があります。

FLASHの速度低下を補正するために、これらのプロセッサには広いFLASHバスとフェッチバッファーがあります。SAM3Xには、プリフェッチパターン[1]に格納されているように見える、128ビットの命令バッファーのペアがあります。

タイトなループを最適化するには、32バイトのコードブロックに適合させ、16バイトの境界で整列させます(念のため、32を推奨)。また、このMCUでFLASHパラメータが正しく設定されているかどうか、つまりプリフェッチが有効でバス幅が128ビットに設定されているかどうかを確認することをお勧めします。コードをRAMにコピーすることもできますが、適切に機能するフェッチバッファーと比較すると、面倒で実際には処理が遅くなる可能性があります。

[1] http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf、ページ294、図18-2、18-3 。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.