Odp: C vs. ASM na przykładzie PIC18F

[...]

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.

Reply to
John Smith
Loading thread data ...

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.

Reply to
Sylwester Łazar

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.

Reply to
John Smith

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

Reply to
Pszemol

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.

Reply to
Sylwester Łazar

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.

Reply to
Sylwester Łazar

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.

Reply to
John Smith

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.

Reply to
Sylwester Łazar

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>

Reply to
janusz_k

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.
Reply to
Sylwester Łazar

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.