임베디드 시스템의 RAM 및 플래시 메모리에서 실행하는 것에 대해 약간 실험했습니다. 빠른 프로토 타이핑 및 테스트를 위해 현재 Arduino Due (SAM3X8E ARM Cortex-M3)를 사용하고 있습니다. 내가 아는 한, Arduino 런타임과 부트 로더는 여기서 아무런 차이가 없습니다.
문제는 다음과 같습니다 . ARM Thumb 어셈블리로 작성된 함수 ( calc )가 있습니다. calc 는 숫자를 계산하여 반환합니다. (주어진 입력에 대해 1 초 이상의 런타임) 이제 해당 함수의 조립 된 기계 코드를 수동으로 추출하여 원시 바이트로 다른 함수에 넣었습니다. 두 기능 모두 플래시 메모리 (주소 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이 겉보기에 가지고 있음!) 또는 인터럽트로 인해 이것을 배제 할 수 있습니다. (편집 : NOPE, 불가능합니다. 아마도 어떤 종류의 프리 페치 일 것입니다) 실행 순서를 변경하거나 사이에 함수 호출을 추가해도 결과는 변경되지 않습니다. 여기서 범인은 무엇입니까?
편집 : 기계 코드 기능의 정렬을 변경 한 후 (프롤로그로 nops 삽입) 다음과 같은 결과가 나타납니다.
calc2의 경우 +16 비트 :
Address: 524617 Res: 3524578: 1102257us
Address: 524669 Res: 3524578: 1846968us
calc2의 경우 +32 비트 :
Address: 524617 Res: 3524578: 1102257us
Address: 524669 Res: 3524578: 1535424us
calc2의 경우 +48 비트 :
Address: 524617 Res: 3524578: 1102155us
Address: 524669 Res: 3524578: 1413180us
calc2의 경우 +64 비트 :
Address: 524617 Res: 3524578: 1102155us
Address: 524669 Res: 3524578: 1346606us
calc2의 경우 +80 비트 :
Address: 524617 Res: 3524578: 1102145us
Address: 524669 Res: 3524578: 1180105us
EDIT2 : calc 만 실행 :
Address: 524617 Res: 3524578: 1102155us
calc2 만 실행 :
Address: 524617 Res: 3524578: 1102257us
순서 변경 :
Address: 524669 Res: 3524578: 1554160us
Address: 524617 Res: 3524578: 1102211us
EDIT3 : calc 전용 .p2align 4
레이블 앞에 추가 .in
, 별도 실행 :
Address: 524625 Res: 3524578: 1413185us
원래 벤치 마크에서와 같이 :
Address: 524625 Res: 3524578: 1413185us
Address: 524689 Res: 3524578: 1535424us
EDIT4 : 플래시에서 위치를 반전 시키면 결과가 완전히 변경됩니다. -> 선형 프리 페치?