Bản sao chính xác của mã máy chạy chậm hơn 50% so với chức năng ban đầu


11

Tôi đã thử nghiệm một chút với việc thực thi từ RAM và bộ nhớ flash trên các hệ thống nhúng. Để tạo mẫu và thử nghiệm nhanh, tôi hiện đang sử dụng Arduino Do (SAM3X8E ARM Cortex-M3). Theo như tôi có thể thấy, bộ thực thi và bộ nạp khởi động Arduino sẽ không có sự khác biệt nào ở đây.

Đây là vấn đề: Tôi có một hàm ( calc ) được viết trong ARM Thumb hội. calc tính một số và trả về nó. (Thời gian chạy> 1 giây cho đầu vào đã cho) Bây giờ tôi đã trích xuất thủ công mã máy đã lắp ráp của hàm đó và đặt nó dưới dạng byte thô vào hàm khác. Cả hai chức năng được xác nhận nằm trong bộ nhớ flash (Địa chỉ 0x80149 và 0x8017D, ngay cạnh nhau). Điều này đã được xác nhận cả thông qua việc tháo gỡ và kiểm tra thời gian chạy.

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

}

Đầu ra của chương trình trên trên mục tiêu Arduino Do là:

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

Vì vậy, chúng tôi xác nhận kết quả là bằng nhau và địa chỉ trong thời gian chạy là như mong đợi. Thực thi chức năng mã máy được nhập thủ công chậm hơn 50%.

Việc tháo gỡ với arm-none-eabi-objdump xác nhận thêm các địa chỉ tương ứng, cư trú bộ nhớ flash và tính công bằng của mã máy (Lưu ý về tuổi thọ và nhóm byte!):

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

Chúng ta có thể xác nhận thêm quy ước gọi được sử dụng tương tự:

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

Tôi có thể loại trừ trường hợp này do một số loại tìm nạp đầu cơ (mà Cortex-M3 dường như có!) Hoặc bị gián đoạn. (EDIT: NOPE, tôi không thể. Có lẽ một số loại tìm nạp trước) Thay đổi thứ tự thực hiện hoặc thêm các lệnh gọi hàm ở giữa không thay đổi kết quả. Điều gì có thể là thủ phạm ở đây?


EDIT: Sau khi thay đổi căn chỉnh của chức năng mã máy (chèn nops làm phần mở đầu) Tôi nhận được các kết quả sau:

+ 16 bit cho calc2:

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

+ 32 bit cho calc2:

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

+ 48 bit cho calc2:

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

+ 64 bit cho calc2:

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

+ 80 bit cho calc2:

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

EDIT2: Chỉ chạy calc:

Address: 524617 Res: 3524578: 1102155us

Chỉ chạy calc2:

Address: 524617 Res: 3524578: 1102257us

Thay đổi thứ tự:

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

EDIT3: Thêm .p2align 4trước nhãn chỉ .incho calc, thực hiện riêng:

Address: 524625 Res: 3524578: 1413185us

Cả hai như trong điểm chuẩn ban đầu:

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

EDIT4: Đảo ngược vị trí trong flash thay đổi hoàn toàn kết quả. -> Tìm nạp trước tuyến tính?


Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Samuel Liew

Câu trả lời:


4

Tốc độ thực thi mã từ flash phụ thuộc vào số chu kỳ chờ và căn chỉnh mã cho từng mục tiêu chi nhánh. Trong bộ xử lý này và các bộ xử lý tương tự, như STM32F103, flash cần 3 chu kỳ chờ khi lõi chạy ở tần số cao nhất. Điều này có nghĩa là mỗi nhánh lấy có thể mất từ ​​2 đến 5 chu kỳ, điều này có thể ảnh hưởng đến tổng thời gian chạy.

Để bù đắp cho sự chậm chạp của FLASH, các bộ xử lý này có một bus FLASH rộng và bộ đệm tìm nạp. SAM3X có một cặp bộ đệm lệnh 128 bit, dường như được điền vào mẫu tìm nạp trước [1].

Để tối ưu hóa một vòng lặp chặt chẽ, hãy thử khớp với khối mã 32 byte và căn chỉnh nó ở ranh giới 16 byte (hoặc tốt hơn là 32, chỉ trong trường hợp). Ngoài ra, có thể là một ý tưởng tốt để kiểm tra xem các tham số FLASH có được thiết lập chính xác hay không, tức là tính năng tìm nạp trước được bật và độ rộng của bus được đặt thành 128 bit, trong MCU này. Sao chép mã vào RAM có thể là một lựa chọn, nhưng thật khó khăn và thực sự có thể làm mọi thứ chậm lại, so với bộ đệm tìm nạp hoạt động đúng.

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.