Odp: C vs. ASM na przykładzie PIC18F

Apr 05, 2014 Last reply: 12 years ago 30 Replies
[...]

Zaliczyłem dość szerokie spectrum uP i uC ale akurat procesorki MicroChipa ominąłem, jakoś mam do nich awersję. Dzisiaj zerknąłem pobieżnie w pdfa, chyba źle postawiłeś tezę wątku, powinna brzmieć: "O wyższości ASM nad C w uC typu PIC". :)

Generalnie, programuję już wyłącznie w językach wysokiego poziomu, asm porzuciłem już dawno temu, kompilatory na "normalne" procesory sprawują się bardzo dobrze i produkują kod porównywalny do ASM, do tego szybciej. Jeżeli zależy mi na optymalizacji, to zerknę na generowany kod, ale najczęściej tylko po to aby złapać się za głowę i wyszeptać "coś ty napisał?". K.

Generalnie, programuję już wyłącznie w językach wysokiego poziomu,

Możesz wrzucić ten przykład do swojego kompilatora, na swój uK i pokazać ile rozkazów ma zlicz()? S.

dla funkcji zlicz: IAR C/C++ Compiler V5.30.1.50284/W32 for MSP430 102 bytes of CODE memory po właczeniu optymalizacji rozmiaru. Procesor MSP430 na pewno 16-bitowy. :) K.

I właśnie dlatego warto się przesiąść dziś na architekturę ARM :-)

W takim razie musiałbym porównywać wtedy do dsPIC30. No, ale w przyszłości może porównam też 16-bitowe. Możesz podać na jaki model kompilowałeś i wkleić kod ? S.

Właśnie sobie patrzę w opis MSP430FE42XA i widzę, że ma fajne adresowanie indexowe: MOV X(Rn),Y(Rm) MOV 2(R5),6(R6) M(2+R5)----> M(6+R6) W związku z tym myślę, że to jest kluczowe dla szybkości i objętości kodu. Ciekawe ile tych operacji ma w pętli sortowania. Szacuję, że 2 powinny wystarczyć, ale pewnie będzie ze 3 razy. S.

On 06-04-2014 10:49, Sylwester Łazar wrote:

Wykonałem kilkadziesiąt kompilacji na różne rodziny MSP430, rozmiar półkompilatu zawsze ten sam: 102 bajty.

Listing kodu funkcji zlicz (zoptymalizowany)

for(i = 0 ; i < k ; ++i) zlicz: ?cstart_end: 00801A 430C clr.w R12 00801C 425D 8000 mov.b &k,R13 008020 3C03 jmp 0x8028 LICZV[i] = 0; // zerowanie tablicy 008022 43CC 111E clr.b 0x111E(R12) for(i = 0 ; i < k ; ++i) 008026 531C inc.w R12 for(i = 0 ; i < k ; ++i) 008028 9D0C cmp.w R13,R12 00802A 3BFB jl 0x8022 for(i = 0 ; i < n ; ++i) 00802C 430C clr.w R12 00802E 425E 8001 mov.b &n,R14 008032 3C05 jmp 0x803E ++LICZV[VDIOD[i]]; // po tych operacjach LICZV[i] będzie zawierała 008034 4C5F 1100 mov.b 0x1100(R12),R15 008038 53DF 111E inc.b 0x111E(R15) for(i = 0 ; i < n ; ++i) 00803C 531C inc.w R12 for(i = 0 ; i < n ; ++i) 00803E 9E0C cmp.w R14,R12 008040 3BF9 jl 0x8034 for(i = 1 ; i < k ; ++i) 008042 431C mov.w #0x1,R12 008044 3C04 jmp 0x804E LICZV[i] += LICZV[i-1]; // teraz LICZV[i] zawiera pozycje w posortowanej 008046 5CDC 111D 111E add.b 0x111D(R12),0x111E(R12) for(i = 1 ; i < k ; ++i) 00804C 531C inc.w R12 for(i = 1 ; i < k ; ++i) 00804E 9D0C cmp.w R13,R12 008050 3BFA jl 0x8046 for(i = n-1 ; i >= 0 ; --i) 008052 4E0C mov.w R14,R12 008054 3C11 jmp 0x8078 j=--LICZV[VDIOD[i]]; // aktualizacja LICZV 008056 4C5E 1100 mov.b 0x1100(R12),R14 00805A 53FE 111E add.b #0xFF,0x111E(R14) 00805E 4E5F 111E mov.b 0x111E(R14),R15 VDOUT[j] = VDIOD[i]; //wstawienie elementu na odpowiednią pozycję 008062 4ECF 1105 mov.b R14,0x1105(R15) ADRDOUT[j][0] = ADRDIOD[i][0]; // sortowanie adresów 008066 5F0F rla.w R15 008068 4C0E mov.w R12,R14 00806A 5E0E rla.w R14 00806C 4EDF 110A 1114 mov.b 0x110A(R14),0x1114(R15) ADRDOUT[j][1] = ADRDIOD[i][1]; // sortowanie adresów 008072 4EDF 110B 1115 mov.b 0x110B(R14),0x1115(R15) for(i = n-1 ; i >= 0 ; --i) 008078 533C add.w #0xFFFF,R12 for(i = n-1 ; i >= 0 ; --i) 00807A 930C tst.w R12 00807C 37EC jge 0x8056 } 00807E 4130 ret

miłego analizowania. K.

Dzięki. No faktycznie fajny kod. W pętli głównej sortowania użyli tylko 14 instrukcji. Jest to zrozumiałe, ze względu na możliwość adresowania indexowego. I faktycznie użyli X(Rn),Y(Rm). Tyle, że szacowałem, że użyją 3x, a użyli 5x. No i w wersji bajtowej.

1) Pokazuje to niefrasobliwość tego co robi kompilator, bo mając do dyspozycji przesuwanie całych słów, korzysta z bajtu. Ślepy indianin już zauważy, że te dwie instrukcje: ADRDOUT[j][0] = ADRDIOD[i][0] ADRDOUT[j][1] = ADRDIOD[i][1] mogą być wykonane za pomocą jednego rozkazu, a nie jak tutaj: mov.b 0x110A(R14),0x1114(R15) mov.b 0x110B(R14),0x1115(R15) Nigdy nie pisałem na MSP430, ale zapewne należałoby użyć: mov 0x110A(R14),0x1114(R15) 2) Albo tutaj. Kasowanie w pętli: 008022 clr.b 0x111E(R12) for(i = 0 ; i < k ; ++i) inc.w R12 cmp.w R13,R12 jl 0x8022 Tutaj 2 błędy: a) po co kasować po bajcie, skoro w pętli można kasować po słowie? i zmniejszyć liczbę obiegów o połowę! b) dlaczego nie jest użyta autoinkrementacja. 16-bitowy MSP430 nie ma rozkazów z inkrementacją R12++?

Reasumując: Kompilatory, mając do dyspozycji świetne rozwiązania techniczne producenta, doskonale je marnują. Dla zmylenia przeciwnika dają w opcjach rzekomą możliwość wybrania metody optymalizacji i żadna nie zauważa takiej gafy? S.

W dniu 06.04.2014 o 00:47 Sylwester Łazar snipped-for-privacy@alpro.pl pisze:

Tu masz jkeszcze w najnowszym As6.2, co prawda dał trochę ostrzeżeń ale skonpilował różnic w zasadzie nie ma, kod jest przesunięty bo inaczej zainicjowana jest tabela przerwań, zrobione to jest na nową ATmega88.

test6.elf: file format elf32-avr

Sections: Idx Name Size VMA LMA File off Algn 0 .data 00000000 00800100 0000013c 000001d0 2**0 CONTENTS, ALLOC, LOAD, DATA 1 .text 0000013c 00000000 00000000 00000094 2**1 CONTENTS, ALLOC, LOAD, READONLY, CODE 2 .bss 00000023 00800100 00800100 000001d0 2**0 ALLOC 3 .comment 00000030 00000000 00000000 000001d0 2**0 CONTENTS, READONLY 4 .debug_aranges 00000028 00000000 00000000 00000200 2**0 CONTENTS, READONLY, DEBUGGING 5 .debug_info 0000016e 00000000 00000000 00000228 2**0 CONTENTS, READONLY, DEBUGGING 6 .debug_abbrev 0000009b 00000000 00000000 00000396 2**0 CONTENTS, READONLY, DEBUGGING 7 .debug_line 00000096 00000000 00000000 00000431 2**0 CONTENTS, READONLY, DEBUGGING 8 .debug_frame 00000034 00000000 00000000 000004c8 2**2 CONTENTS, READONLY, DEBUGGING 9 .debug_str 00000129 00000000 00000000 000004fc 2**0 CONTENTS, READONLY, DEBUGGING 10 .debug_loc 00000094 00000000 00000000 00000625 2**0 CONTENTS, READONLY, DEBUGGING 11 .debug_ranges 00000018 00000000 00000000 000006b9 2**0 CONTENTS, READONLY, DEBUGGING

Disassembly of section .text:

00000000 <__vectors>: 0: 19 c0 rjmp .+50 ; 0x34 <__ctors_end> 2: 33 c0 rjmp .+102 ; 0x6a <__bad_interrupt> 4: 32 c0 rjmp .+100 ; 0x6a <__bad_interrupt> 6: 31 c0 rjmp .+98 ; 0x6a <__bad_interrupt> 8: 30 c0 rjmp .+96 ; 0x6a <__bad_interrupt>

a: 2f c0 rjmp .+94 ; 0x6a <__bad_interrupt>

c: 2e c0 rjmp .+92 ; 0x6a <__bad_interrupt>

e: 2d c0 rjmp .+90 ; 0x6a <__bad_interrupt>

10: 2c c0 rjmp .+88 ; 0x6a <__bad_interrupt> 12: 2b c0 rjmp .+86 ; 0x6a <__bad_interrupt> 14: 2a c0 rjmp .+84 ; 0x6a <__bad_interrupt> 16: 29 c0 rjmp .+82 ; 0x6a <__bad_interrupt> 18: 28 c0 rjmp .+80 ; 0x6a <__bad_interrupt> 1a: 27 c0 rjmp .+78 ; 0x6a <__bad_interrupt> 1c: 26 c0 rjmp .+76 ; 0x6a <__bad_interrupt> 1e: 25 c0 rjmp .+74 ; 0x6a <__bad_interrupt> 20: 24 c0 rjmp .+72 ; 0x6a <__bad_interrupt> 22: 23 c0 rjmp .+70 ; 0x6a <__bad_interrupt> 24: 22 c0 rjmp .+68 ; 0x6a <__bad_interrupt> 26: 21 c0 rjmp .+66 ; 0x6a <__bad_interrupt> 28: 20 c0 rjmp .+64 ; 0x6a <__bad_interrupt> 2a: 1f c0 rjmp .+62 ; 0x6a <__bad_interrupt> 2c: 1e c0 rjmp .+60 ; 0x6a <__bad_interrupt> 2e: 1d c0 rjmp .+58 ; 0x6a <__bad_interrupt> 30: 1c c0 rjmp .+56 ; 0x6a <__bad_interrupt> 32: 1b c0 rjmp .+54 ; 0x6a <__bad_interrupt>

00000034 <__ctors_end>: 34: 11 24 eor r1, r1 36: 1f be out 0x3f, r1 ; 63 38: cf ef ldi r28, 0xFF ; 255 3a: d4 e0 ldi r29, 0x04 ; 4 3c: de bf out 0x3e, r29 ; 62 3e: cd bf out 0x3d, r28 ; 61

00000040 <__do_copy_data>: 40: 11 e0 ldi r17, 0x01 ; 1 42: a0 e0 ldi r26, 0x00 ; 0 44: b1 e0 ldi r27, 0x01 ; 1 46: ec e3 ldi r30, 0x3C ; 60 48: f1 e0 ldi r31, 0x01 ; 1 4a: 02 c0 rjmp .+4 ; 0x50 <__do_copy_data+0x10> 4c: 05 90 lpm r0, Z+ 4e: 0d 92 st X+, r0 50: a0 30 cpi r26, 0x00 ; 0 52: b1 07 cpc r27, r17 54: d9 f7 brne .-10 ; 0x4c <__do_copy_data+0xc>

00000056 <__do_clear_bss>: 56: 21 e0 ldi r18, 0x01 ; 1 58: a0 e0 ldi r26, 0x00 ; 0 5a: b1 e0 ldi r27, 0x01 ; 1 5c: 01 c0 rjmp .+2 ; 0x60 <.do_clear_bss_start>

0000005e <.do_clear_bss_loop>: 5e: 1d 92 st X+, r1

00000060 <.do_clear_bss_start>: 60: a3 32 cpi r26, 0x23 ; 35 62: b2 07 cpc r27, r18 64: e1 f7 brne .-8 ; 0x5e <.do_clear_bss_loop>

66: 44 d0 rcall .+136 ; 0xf0 <main> 68: 67 c0 rjmp .+206 ; 0x138 <_exit>

0000006a <__bad_interrupt>: 6a: ca cf rjmp .-108 ; 0x0 <__vectors>

0000006c <zlicz>: ADRDIOD[4][0]=2; ADRDIOD[4][1]=1; zlicz(); }

void zlicz(){ 6c: ee e1 ldi r30, 0x1E ; 30 6e: f1 e0 ldi r31, 0x01 ; 1 char i; // zmienna pomocnicza char j; // zmienna pomocnicza for(i = 0 ; i < k ; ++i) LICZV[i] = 0; // zerowanie tablicy 70: 11 92 st Z+, r1 void zlicz(){ char i; // zmienna pomocnicza char j; // zmienna pomocnicza for(i = 0 ; i < k ; ++i) 72: 81 e0 ldi r24, 0x01 ; 1 74: e5 32 cpi r30, 0x25 ; 37 76: f8 07 cpc r31, r24 78: d9 f7 brne .-10 ; 0x70 <zlicz+0x4>

7a: a0 e0 ldi r26, 0x00 ; 0 7c: b1 e0 ldi r27, 0x01 ; 1 LICZV[i] = 0; // zerowanie tablicy for(i = 0 ; i < n ; ++i) ++LICZV[VDIOD[i]]; // po tych operacjach LICZV[i] będzie zawierała 7e: ed 91 ld r30, X+ 80: f0 e0 ldi r31, 0x00 ; 0 82: e2 5e subi r30, 0xE2 ; 226 84: fe 4f sbci r31, 0xFE ; 254 86: 80 81 ld r24, Z 88: 8f 5f subi r24, 0xFF ; 255 8a: 80 83 st Z, r24 char j; // zmienna pomocnicza for(i = 0 ; i < k ; ++i) LICZV[i] = 0; // zerowanie tablicy for(i = 0 ; i < n ; ++i) 8c: 81 e0 ldi r24, 0x01 ; 1 8e: a5 30 cpi r26, 0x05 ; 5 90: b8 07 cpc r27, r24 92: a9 f7 brne .-22 ; 0x7e <zlicz+0x12> 94: ef e1 ldi r30, 0x1F ; 31 96: f1 e0 ldi r31, 0x01 ; 1 98: ae e1 ldi r26, 0x1E ; 30 9a: b1 e0 ldi r27, 0x01 ; 1 ++LICZV[VDIOD[i]]; // po tych operacjach LICZV[i] będzie zawierała // liczbę wystąpień elementów o kluczu i for(i = 1 ; i < k ; ++i) LICZV[i] += LICZV[i-1]; // teraz LICZV[i] zawiera pozycje w posortowanej 9c: 9d 91 ld r25, X+ 9e: 80 81 ld r24, Z a0: 89 0f add r24, r25 a2: 81 93 st Z+, r24 LICZV[i] = 0; // zerowanie tablicy for(i = 0 ; i < n ; ++i) ++LICZV[VDIOD[i]]; // po tych operacjach LICZV[i] będzie zawierała // liczbę wystąpień elementów o kluczu i for(i = 1 ; i < k ; ++i) a4: 81 e0 ldi r24, 0x01 ; 1 a6: e5 32 cpi r30, 0x25 ; 37 a8: f8 07 cpc r31, r24 aa: c1 f7 brne .-16 ; 0x9c <zlicz+0x30>

ac: 84 e0 ldi r24, 0x04 ; 4 LICZV[i] += LICZV[i-1]; // teraz LICZV[i] zawiera pozycje w posortowanej // tablicy ostatniego elementu o kluczu i for(i = n-1 ; i >= 0 ; --i) { j=--LICZV[VDIOD[i]]; // aktualizacja LICZV ae: a8 2f mov r26, r24 b0: b0 e0 ldi r27, 0x00 ; 0 b2: fd 01 movw r30, r26 b4: e0 50 subi r30, 0x00 ; 0 b6: ff 4f sbci r31, 0xFF ; 255 b8: 90 81 ld r25, Z ba: c9 2f mov r28, r25 bc: d0 e0 ldi r29, 0x00 ; 0 be: c2 5e subi r28, 0xE2 ; 226 c0: de 4f sbci r29, 0xFE ; 254 c2: e8 81 ld r30, Y c4: e1 50 subi r30, 0x01 ; 1 c6: e8 83 st Y, r30 VDOUT[j] = VDIOD[i]; //wstawienie elementu na odpowiednią pozycję c8: f0 e0 ldi r31, 0x00 ; 0 ca: ef 01 movw r28, r30 cc: c7 5e subi r28, 0xE7 ; 231 ce: de 4f sbci r29, 0xFE ; 254 d0: 98 83 st Y, r25 ADRDOUT[j][0] = ADRDIOD[i][0]; // sortowanie adresów d2: ee 0f add r30, r30 d4: ff 1f adc r31, r31 d6: eb 5f subi r30, 0xFB ; 251 d8: fe 4f sbci r31, 0xFE ; 254 da: aa 0f add r26, r26 dc: bb 1f adc r27, r27 de: a1 5f subi r26, 0xF1 ; 241 e0: be 4f sbci r27, 0xFE ; 254 e2: 9c 91 ld r25, X e4: 90 83 st Z, r25 ADRDOUT[j][1] = ADRDIOD[i][1]; // sortowanie adresów e6: 11 96 adiw r26, 0x01 ; 1 e8: 9c 91 ld r25, X ea: 91 83 std Z+1, r25 ; 0x01 ++LICZV[VDIOD[i]]; // po tych operacjach LICZV[i] będzie zawierała // liczbę wystąpień elementów o kluczu i for(i = 1 ; i < k ; ++i) LICZV[i] += LICZV[i-1]; // teraz LICZV[i] zawiera pozycje w posortowanej // tablicy ostatniego elementu o kluczu i for(i = n-1 ; i >= 0 ; --i) ec: 81 50 subi r24, 0x01 ; 1 ee: df cf rjmp .-66 ; 0xae <zlicz+0x42>

000000f0 <main>: char ADRDIOD[5][2];//tablica adresów diod char ADRDOUT[5][2];//tablica adresów diod po posegregowaniu char LICZV[5]; // zawiera liczbę elementów o danej wartości

void main (void){ VDIOD[0]=1; f0: 81 e0 ldi r24, 0x01 ; 1 f2: 80 93 00 01 sts 0x0100, r24 VDIOD[1]=2; f6: 92 e0 ldi r25, 0x02 ; 2 f8: 90 93 01 01 sts 0x0101, r25 VDIOD[2]=6; fc: 26 e0 ldi r18, 0x06 ; 6 fe: 20 93 02 01 sts 0x0102, r18 VDIOD[3]=4; 102: 24 e0 ldi r18, 0x04 ; 4 104: 20 93 03 01 sts 0x0103, r18 VDIOD[4]=3; 108: 23 e0 ldi r18, 0x03 ; 3 10a: 20 93 04 01 sts 0x0104, r18 ADRDIOD[0][0]=1; 10e: 80 93 0f 01 sts 0x010F, r24 ADRDIOD[0][1]=0; 112: 10 92 10 01 sts 0x0110, r1 ADRDIOD[1][0]=1; 116: 80 93 11 01 sts 0x0111, r24 ADRDIOD[1][1]=1; 11a: 80 93 12 01 sts 0x0112, r24 ADRDIOD[2][0]=1; 11e: 80 93 13 01 sts 0x0113, r24 ADRDIOD[2][1]=2; 122: 90 93 14 01 sts 0x0114, r25 ADRDIOD[3][0]=2; 126: 90 93 15 01 sts 0x0115, r25 ADRDIOD[3][1]=0; 12a: 10 92 16 01 sts 0x0116, r1 ADRDIOD[4][0]=2; 12e: 90 93 17 01 sts 0x0117, r25 ADRDIOD[4][1]=1; 132: 80 93 18 01 sts 0x0118, r24 zlicz(); 136: 9a df rcall .-204 ; 0x6c <zlicz>

00000138 <_exit>: 138: f8 94 cli

0000013a <__stop_program>: 13a: ff cf rjmp .-2 ; 0x13a <__stop_program>

OK. Dzięki.

33 instrukcje w pętli sortowania. Czyli jedna mniej jeśli policzyłem poprawnie. Tak czy inaczej 2,6us/pętlę. S.

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required