Cześć!
Kontrolery dsPIC33FJ mają instrukcje, dzięki którym można zoptymalizować np. pętle.
__inline__ static void __attribute__((always_inline)) arch_memset(void * s, char c, size_t n)
{
if(n)
__asm__ __volatile__(
"REPEAT %[n] \n" // Repeat next instruction (Wn + 1) times
"MOV.B %[c], [%[s]++] \n"
:[s]"+r"(s)
:[n]"r"(n-1), [c]"r"(c)
);
}
/*
* arch_memset 1024B 26.0us
* memset 1024B 129.0us
*
* arch_memset 5B 450ns
* memset 5B 640ns
*
*/
Powyższa funkcja którą napisałem, jest znacznie szybsza od wbudowanej.
Wersja na dsPIC
Włączając optymalizację (-O1), funkcje tego typu przestają działać.
static char DST[2] = {0, 0};
void __attribute__((always_inline)) copy2(char * dst, const char * src){
__asm__ __volatile__(
"MOV.B [%[src]++], [%[dst]++] \n"
"MOV.B [%[src]++], [%[dst]++] \n"
:[dst]"+r"(dst)
,[src]"+r"(src)
);
}
void disappearing_data(){
char SRC[2] = {0xde, 0xad};
copy2(DST, SRC);
}
int main(){
disappearing_data();
return 0;
}
Przy optymalizacji -O1, tablica SRC jest tworzona, lecz bez wpisanych danych.
000002b2 <_disappearing_data>:
2b2: e4 87 47 add.w w15, #0x4, w15 ; rezerwacja miejsca na stosie dla ramki stosu(2B) oraz 2B zmiennej (w15 to sp)
2b4: 8e 1f 78 mov.w w14, [w15++] ; w14 to bp
000002b6 <.LBB4>:
2b6: 20 80 20 mov.w #0x802, w0 ; adres do statycznej zmiennej globalnej
2b8: 64 87 57 sub.w w15, #0x4, w14 ; adres do zmiennej na stosie (w14 <- w15 - 4)
2ba: 3e 58 78 mov.b [w14++], [w0++] ; wstawka z funkcji copy2
2bc: 3e 58 78 mov.b [w14++], [w0++] ; wstawka z funkcji copy2
000002be <.LBE4>:
2be: 4f 07 78 mov.w [--w15], w14
2c0: 4f 00 b1 sub.w #0x4, w15
2c2: 00 00 06 return
Przy wyłączonej optymalizacji:
000002c2 <_disappearing_data>:
2c2: 06 00 fa lnk #0x6
2c4: 88 9f be mov.d w8, [w15++]
2c6: e0 cd b3 mov.b #0xde, w0 ; dane sa wpisywane
2c8: 40 47 98 mov.b w0, [w14+4]
2ca: d0 ca b3 mov.b #0xad, w0 ; dane sa wpisywane
2cc: 50 47 98 mov.b w0, [w14+5]
2ce: 20 80 20 mov.w #0x802, w0
2d0: 00 0f 78 mov.w w0, [w14]
2d2: 64 00 47 add.w w14, #0x4, w0
2d4: 10 07 98 mov.w w0, [w14+2]
000002d6 <.LBB4>:
2d6: 9e 00 78 mov.w [w14], w1
2d8: 1e 00 90 mov.w [w14+2], w0
2da: 81 04 78 mov.w w1, w9
2dc: 00 04 78 mov.w w0, w8
2de: b8 5c 78 mov.b [w8++], [w9++] ; wstawka z funkcji copy2
2e0: b8 5c 78 mov.b [w8++], [w9++] ; wstawka z funkcji copy2
2e2: 09 0f 78 mov.w w9, [w14]
2e4: 18 07 98 mov.w w8, [w14+2]
000002e6 <.LBE4>:
2e6: 4f 04 be mov.d [--w15], w8
2e8: 00 80 fa ulnk
2ea: 00 00 06 return
Przeanalizowałem też, że jeśli copy2
nie jest inline
to optymalizacja nie wyrzuca danych bo musi przekazać do funkcji copy2
(funkcja copy2 w innym pliku).
Jak wstawka pojawia się w tej samej funkcji co dane źródłowe SRC
, kompilator stwierdza, że wstawka asm nie potrzebuje danych i ich nie inicjalizuje.
Wersja LINUX
Spróbowałem odtworzyć problem na linuxie:
#include <stdio.h>
static char DST[2] = {0, 0};
void
//__attribute__((always_inline))
copy2(char * dst, char * src){
__asm__ __volatile__(
"movb 0x0(%1), %%al \n"
"movb %%al, 0x0(%0) \n"
:"+r"(dst)
: "r"(src)
: "%eax"
);
}
void disappearing_data(){
char SRC[2] = {0xde, 0xad};
copy2(DST, SRC);
}
int main(){
disappearing_data();
printf("0x%x\n", DST[0] & 0xFF );
return 0;
}
Niepoprawny wynik z optymalizacją:
# gcc -O1 -o main main.c && objdump -d main && ./main
...
0000000000400506 <copy2>:
400506: 8a 06 mov (%rsi),%al
400508: 88 07 mov %al,(%rdi)
40050a: c3 retq
000000000040050b <disappearing_data>:
40050b: 48 8d 4c 24 f0 lea -0x10(%rsp),%rcx
400510: ba 71 09 60 00 mov $0x600971,%edx
400515: 8a 01 mov (%rcx),%al
400517: 88 02 mov %al,(%rdx)
400519: c3 retq
...
0x10
Poprawny wynik bez optymalizacji:
# gcc -O0 -o main main.c && objdump -d main && ./main
...
0000000000400506 <copy2>:
400506: 55 push %rbp
400507: 48 89 e5 mov %rsp,%rbp
40050a: 48 89 7d f8 mov %rdi,-0x8(%rbp)
40050e: 48 89 75 f0 mov %rsi,-0x10(%rbp)
400512: 48 8b 4d f0 mov -0x10(%rbp),%rcx
400516: 48 8b 45 f8 mov -0x8(%rbp),%rax
40051a: 48 89 c2 mov %rax,%rdx
40051d: 8a 01 mov (%rcx),%al
40051f: 88 02 mov %al,(%rdx)
400521: 48 89 55 f8 mov %rdx,-0x8(%rbp)
400525: 5d pop %rbp
400526: c3 retq
0000000000400527 <disappearing_data>:
400527: 55 push %rbp
400528: 48 89 e5 mov %rsp,%rbp
40052b: 48 83 ec 10 sub $0x10,%rsp
40052f: c6 45 f0 de movb $0xde,-0x10(%rbp)
400533: c6 45 f1 ad movb $0xad,-0xf(%rbp)
400537: 48 8d 45 f0 lea -0x10(%rbp),%rax
40053b: 48 89 c6 mov %rax,%rsi
40053e: bf b9 09 60 00 mov $0x6009b9,%edi
400543: e8 be ff ff ff callq 400506 <copy2>
400548: c9 leaveq
400549: c3 retq
...
0xde
Czy brakuje mi w wstawce jakiegoś "przełącznika" żeby kompilator nie wywalał tych danych?
Jak w takim razie, implementować wstawki jako funkcje inline przy włączonej optymalizacji?
Większość moich wstawek nie ma więcej niż 6-8 instrukcji, skakanie do funkcji to bezsensowne obniżanie wydajności kodu.