Makine kodunun tam kopyası orijinal işlevden% 50 daha yavaş çalışır


11

Gömülü sistemlerde RAM ve flash bellekten yürütme ile biraz deney yapıyorum. Hızlı prototipleme ve test için şu anda Arduino Due (SAM3X8E ARM Cortex-M3) kullanıyorum. Görebildiğim kadarıyla, Arduino çalışma zamanı ve bootloader burada bir fark yaratmamalıdır.

İşte sorun: ARM Thumb Assembly ile yazılmış bir fonksiyonum ( calc ) var. calc bir sayıyı hesaplar ve döndürür. (> 1s çalışma zamanı verilen giriş için) Şimdi bu işlevin birleştirilmiş makine kodunu elle ayıkladım ve başka bir işleve ham bayt olarak koydum. Her iki fonksiyonun da flash bellekte kaldığı onaylanmıştır (Adres 0x80149 ve 0x8017D, hemen yan yana). Bu hem sökme hem de çalışma zamanı kontrolü ile onaylanmıştır.

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() {

}

Yukarıdaki programın Arduino Due hedefi üzerindeki çıktısı:

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

Bu nedenle sonuçların eşit olduğunu ve çalışma zamanı boyunca adresin beklendiği gibi olduğunu onaylıyoruz. Manuel olarak girilen makine kodu işlevinin yürütülmesi% 50 daha yavaştır.

Arm-none-eabi-objdump ile sökme işlemi, ilgili adresleri, flash bellek ikametgahını ve makine kodunun eşitliğini doğrular (Not endianness ve bayt gruplaması!):

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
    ...

Benzer şekilde kullanılan çağrı kuralını daha da doğrulayabiliriz:

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

Bu bir tür spekülatif getirme (Cortex-M3 görünüşte var!) Veya kesintiler nedeniyle dışlanabilir. (EDIT: NOPE, yapamıyorum. Muhtemelen bir tür ön getirme) Yürütme sırasını değiştirmek veya arasına işlev çağrıları eklemek sonucu değiştirmez. Burada suçlu ne olabilir?


DÜZENLEME: Makine kodu işlevinin hizalamasını değiştirdikten sonra (düğümleri prolog olarak ekle) Aşağıdaki sonuçları alıyorum:

Calc2 için + 16bit:

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

Calc2 için + 32bit:

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

Calc2 için + 48bit:

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

Calc2 için + 64bit:

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

Calc2 için + 80bit:

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

EDIT2: Sadece calc çalışıyor:

Address: 524617 Res: 3524578: 1102155us

Sadece calc2 çalışıyor:

Address: 524617 Res: 3524578: 1102257us

Siparişin değiştirilmesi:

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

EDIT3: .p2align 4Etiketten önce .inyalnızca hesap için ekleme , ayrı yürütme:

Address: 524625 Res: 3524578: 1413185us

Her ikisi de orijinal karşılaştırmada olduğu gibi:

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

EDIT4: Flaştaki konumu tersine çevirmek sonucu tamamen değiştirir. -> Doğrusal getirme?


Yorumlar uzun tartışmalar için değildir; bu sohbet sohbete taşındı .
Samuel Liew

Yanıtlar:


4

Flaştan kod yürütme hızı, her dal hedefi için bekleme döngüsü sayısına ve kod hizalamasına bağlıdır. Bu ve benzer işlemcilerde, STM32F103 gibi, çekirdek en yüksek frekansta çalıştığında flaşın 3 bekleme döngüsüne ihtiyacı vardır. Bu, alınan her dalın toplam çalışma süresini etkileyebilecek 2 ila 5 döngü alabileceği anlamına gelir.

FLASH yavaşlığını telafi etmek için, bu işlemciler geniş bir FLASH veriyoluna ve bir getirme arabelleğine sahiptir. SAM3X, bir ön getirme düzeninde doldurulmuş gibi görünen bir çift 128 bit komut tamponuna sahiptir [1].

Sıkı bir döngüyü optimize etmek için 32 baytlık bir kod bloğuna sığdırmayı ve 16 baytlık sınırda hizalamayı deneyin (veya her durumda daha iyi 32). Ayrıca, bu MCU'da FLASH parametrelerinin doğru ayarlanıp ayarlanmadığını kontrol etmek iyi bir fikir olabilir, yani ön alma etkin ve veri yolu genişliği 128 bit olarak ayarlanmış olabilir. Kodun RAM'e kopyalanması bir seçenek olabilir, ancak bu bir ağrıdır ve düzgün çalışan getirme tamponlarına kıyasla işleri yavaşlatabilir.

[1] http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf , sayfa 294, Şekiller 18-2, 18-3 .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.