-
1. Data: 2014-04-05 05:20:27
Temat: C vs. ASM na przykładzie PIC18F
Od: Sylwester Łazar <i...@a...pl>
Tak jak opisałem wcześniej, przygotowałem procedurę, która sortuje mi
kilka napięć metodą zliczania:
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0023.php
Jako, że ostatnio toczą się dyskusje o kompilatorach C,
pozwoliłem sobie zrobić prosty test.
Napisałem procedurę w ASM i w C.
Oczywiście obie działają poprawnie.
1) Objętość kodu ma się następująco (dla otymalizacji Debug):
470 bajty kodu w C
128 bajty kodu w ASM
3,67x więcej w C
Poniżej wklejam oba kody.
Kodu w C po kompilacji z oczywistych względów nie wklejam.
2) Testów czasowych _nie robiłem_, ale główna pętla przepisywania rekordów
ma w asm: 20 instrukcji,
a w C po przekompilowaniu: 121 instrukcji.
Wygląda na to, że w C program działa jakieś 6x wolniej.
3) Ciekawe jest, że prosta procedura, która kasuje wstępnie tablicę typu
char,
ma w pętli 10 rozkazów na samo kasowanie
i 16 rozkazów na obsługę pętli.
No i jest taka perełka:
MOVLW 0
ADDWFC 0xfea, F, ACCESS
Najpierw do w wpisuje 0, a potem dodaje do rejestru 0xFEA, czyli do
starszego bajtu FSR0H.
Operacja kompletnie bezsensowna.
4) Kompilator to MPLAB C18 v3.12 (demo)
5) Po włączeniu optymalizacji "Enable on" kod zmniejszył się do 342 bajtów
i w ten sposób współczynnik ilości bajtów C vs. ASM wyniósł: 2,67.
Główna pętla zwiększyła się ze 121instrukcji na 182!
Przypominam: w asm 20 instrukcji.
Możliwe, że ma to swoje umotywowanie czasowe, ale trudno mi sobie je
wytłumaczyć,
skoro da się to ze spokojem przeprowadzić w 20 instrukcjach, a niech będzie,
że i w 40,
jeśli jakiś student by się pokusił o nieoptymalne napisanie kodu.
Ale 182 to już gruba przesada.
Wnioski:
1) Wydaje mi się, że takie wyniki to porażka, jeśli chodzi o możliwości
kompilatora C.
2) Ja w swoim kodzie nie używałem żadnych sztuczek.
Wszystkie ewentualne optymalizację, mógłby wykonać kompilator -
mechanicznie.
3) Być może są jakieś inne kompilatory, które potrafią wydusić z siebie coś
więcej.
4) Ten przykład sortował 5 liczb.
Nie jestem w stanie sobie wyobrazić sprawnego sortowania 5000 liczb,
pobieranych np. z pendrive'a,
za pomocą kodu napisanego w C.
5) Podglądnąłem też wcześniej kod po kompilacji dla 32-bitowego
mikrokontrolera MX32. Też byłem załamany,
ale nie analizowałem wtedy tak dokładnie, co tam za cuda się dzieją.
--
-- .
pozdrawiam
Sylwester Łazar
http://www.alpro.pl Systemy elektroniczne.
http://www.rimu.pl -oprogramowanie do edycji schematów
i projektowania PCB.
Kod w C:
void zlicz(void);
const char k = 7; // elementami tablicy VDIOD są liczby całkowite z
przedziału 0..6
const char n = 5;
char VDIOD[5]; // tablica zawierająca elementy do posortowania
char VDOUT[5]; // tablica zawierająca elementy posortowane
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;
VDIOD[1]=2;
VDIOD[2]=6;
VDIOD[3]=4;
VDIOD[4]=3;
ADRDIOD[0][0]=1;
ADRDIOD[0][1]=0;
ADRDIOD[1][0]=1;
ADRDIOD[1][1]=1;
ADRDIOD[2][0]=1;
ADRDIOD[2][1]=2;
ADRDIOD[3][0]=2;
ADRDIOD[3][1]=0;
ADRDIOD[4][0]=2;
ADRDIOD[4][1]=1;
zlicz();
}
void zlicz(){
char i; // zmienna pomocnicza
char j; // zmienna pomocnicza
for(i = 0 ; i < k ; ++i)
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)
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
VDOUT[j] = VDIOD[i]; //wstawienie elementu na odpowiednią pozycję
ADRDOUT[j][0] = ADRDIOD[i][0]; // sortowanie adresów
ADRDOUT[j][1] = ADRDIOD[i][1]; // sortowanie adresów
}
}
Kod w asm:
;***************************************************
************************
;PROJEKT: TDIODA
;DATA: 2014-04-04
;PROCESOR: PIC18F2320
;PROCEDURA:ZLICZ
;PLIK:ZLICZ201446.SDA
;DANE WE: TABLICA NAPIĘĆ DIODY
;KMIN-wartość minimalna napięcia
;KMAX-wartość maksymalna napięcia
;N-liczba diod
;
;DVOLT- tablica napięć diod (1 bajt)
;LICZV - tablica ilości takich samych próbek napięć
;ADRDIOD - ADRES <PAR PORT;PIN> (2 bajty)
;
;DANE WY: posortowane obie tablica:
;DWOLT2
;ADRDIOD2
;OPIS:
;Procedura sortuje diody w kolejności rosnącego
;napięcia.
;METODA: ZLICZAJĄCA
;Uwagi:
;1) Dla dłuższych danych należy zastosować mikrokontroler 16 lub 32 lub 64
bitowy.
;2) TABLICE wyników muszą następować bezpośrednio po tablicy
nieposortowanej.
;np. ADRDIOD2 musi być po ADRDIOD
;3) pozycje tablicy numerujemy od 1
;4) Sortowane elementy mogą mieć maksymalnie 128 pozycji (dwubajtowe dane).
;Tutaj N to liczba diod.
;Muszą się zaczynać na adresie pamięci xx00. (ALIGNED)
;Trochę to bez sensu, gdyż dla N=128 pozycji to sortowanie szybkie byłoby
szybsze.
; Jednak po przeniesieniu na większy uC liczba N może ulec zmianie.
;***************************************************
************************
;ZLICZ.
ZLICZ
MOVF KMIN,w ;zacznij zerować tablicę liczników od KMIN
SUBWF KMAX,w ;oblicz rozpiętość napięć
MOVWF COUNTER ;zapisz rozpiętość na stosie
INCF WREG ;liczba bajtów do sumowania wystąpień o 1 większa
MOVWF CNT ;zapisz do CNT
LFSR 1,LICZV ;zapisz do wskaźnika początek tablicy zliczającej
ZCLR
CLRF POSTINC1 ;LICZV[CNT++]:=0
DECFSZ CNT ;Czy cała tablica wyzerowana?
BRA ZCLR ;NIE
LFSR 1,VDIOD ;początek tablicy napięć diod
LFSR 2,LICZV ;początek tablicy liczników wartości
movlf NR,CNT ;ustal liczbę działań równą liczbie diod
ZLICZN
MOVF KMIN,w ;od każdej wartości odejmiemy KMIN
SUBWF POSTINC1,w ;odczytaj klucz z tablicy VDIOD++[]
INCF PLUSW2,F ;zwiększ o 1 licznik dla tego napięcia
DECFSZ CNT ;Czy wszystkie napięcia zliczone?
BRA ZLICZN ;NIE
LFSR 1,LICZV ;początek tablicy liczb wartości napięć
LFSR 2,LICZV+1 ;następna pozycja
ZSUMUJ
MOVF POSTINC1,w ;odczytaj poprzednią sumę
ADDWF POSTINC2,F ;dodaj do bieżącej
DECFSZ COUNTER ;Czy wszystkie wartości posumowane?
BRA ZSUMUJ ;NIE
LFSR 0,NR+VDIOD-1;koniec tablicy napięć diod
LFSR 1,LICZV ;POCZĄTEK tablicy liczników wartości
LFSR 2,ADRDIOD ;adres początku tablicy adresów diod
movlf 2*NR,CNT ;ustal liczbę działań (po 2 bajty adresu) liczbie diod*2
NEGF KMIN ;zaneguj KMIN, aby w pętli móc dodawać zamiast odejmować
ZSORTUJ
MOVF POSTDEC0,w ;odczytaj wartość napięcia i cofnij wskaźnik
MOVWF VOLTAGE ;zapamiętaj bieżącą wartość napięcia
ADDWF KMIN,w ;zrób klucz: dodaj zanegowaną wartość minimalna
DECF PLUSW1,F ;zmniejsz ostatnią POZYCJĘ dla tego napięcia w LICZV[VOLTAGE]
MOVF PLUSW1,w ;odczytaj nową pozycję dla tej wartości
MULLW 2 ;zapamiętaj numer nowej pozycji*2 w rejestrze wyniku mnożenia
;----------------------
ADDLW -NR ;przesuń wskaźnik do tablicy wyników VDOUT[]- musi być
koniecznie przed ADRDIOD[]
MOVFF VOLTAGE,PLUSW2;zapisz wartość napięcia
VDOUT[ADRDIOD-NR+LICZV[klucz]:=VOLTAGE
;----------------------
DECF CNT,F ;zmniejsz pozycję z tablicy ADRESÓW
MOVF CNT,w ;przepisz do w
;-------------TUTAJ KOPIOWANIE CAŁEGO REKORDU - ODCZYT:
MOVFF PLUSW2,BIT ;odczytaj ADRES BITU
DECF WREG,F ;zmniejsz wskaźnik
MOVFF PLUSW2,PORT ;odczytaj ADRES PORTU
;-------------TUTAJ KOPIOWANIE CAŁEGO REKORDU - ZAPIS:
MOVF PRODL,w ;odzyskaj numer nowej pozycji
ADDLW 2*NR ;dodanie offsetu, aby uzyskać adres tablicy wynikowej ADRDOUT
MOVFF PORT,PLUSW2 ;zapisz ADRES BITU na nowej pozycji
INCF WREG,F ;zmniejsz wskaźnik
MOVFF BIT,PLUSW2 ;zapisz ADRES PORTU na nowej pozycji
DECFSZ CNT ;Czy wszystkie wartości posortowane?
BRA ZSORTUJ ;NIE
NEGF KMIN ;przywróć poprawna wartość dla porządku
RETURN
-
2. Data: 2014-04-05 06:40:26
Temat: Re: C vs. ASM na przykładzie PIC18F
Od: John Smith <d...@b...pl>
On 05-04-2014 05:20, Sylwester Łazar wrote:
> Tak jak opisałem wcześniej, przygotowałem procedurę, która sortuje mi
> kilka napięć metodą zliczania:
> http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0023.php
>
> Jako, że ostatnio toczą się dyskusje o kompilatorach C,
> pozwoliłem sobie zrobić prosty test.
>
> Napisałem procedurę w ASM i w C.
> Oczywiście obie działają poprawnie.
[ciach]
Ja bym się aż tak bardzo nie ekscytował uzyskanymi wynikami.
CPU procesora PIC18F2320 jest 16 bitowe a Ty napisałeś program
z pomieszaniem adresowania 8 i 16 bitowego. Nic dziwnego, że
kompilator dodał instrukcje konwersji typów. Bądż uprzejmy
przepisać ten program na liczby i adresowanie 16 bitowe
i dopiero wtedy porównać uzyskane rezultaty.
Oczywiście optymalizacja w C powinna być dla "release" a nie "debug".
Współcześnie sprzedawane kompilatory są często generowane przez
metakompilatory, gdyż koszta zabiłyby producenta oprogramowania.
K.
-
3. Data: 2014-04-05 10:02:24
Temat: Odp: C vs. ASM na przykładzie PIC18F
Od: Sylwester Łazar <i...@a...pl>
> > Napisałem procedurę w ASM i w C.
> > Oczywiście obie działają poprawnie.
> [ciach]
>
> Ja bym się aż tak bardzo nie ekscytował uzyskanymi wynikami.
Spokojnie. Wcale się nie ekscytuje. Przecież znałem z góry wynik.
Nie spodziewałem się tylko, że mechanika, jak na "głupie" układanie kody,
osiągnie nawet takie wyniki. Za naszego życia maszyna nie będzie lepsza od
człowieka.
> CPU procesora PIC18F2320 jest 16 bitowe
No tak w połowie, bym rzekł.
Jeśli mówisz o adresowaniu, no to rejestry FSRxH:L mają tylko 12-bitów,
więc do 4096 może i procedura się nie zmieni zbyt wiele po kompilacji.
O 100, może więcej instrukcji?
Spójrz na rysunek adresowania pośredniego:
ww1.microchip.com/downloads/en/DeviceDoc/39605b.pdf
strona 58AR (Acrobat Reader)/56DS.
Jak zapewne zauważyłeś, podałem w rozważaniach liczbę 5000.
Dla 5000 to naprawdę może kompilator przygotuje coś 16 bitowego.
Wtedy obawiam się, będzie dopiero tragedia.
A szyna danych jest z kolei 8-bitowa.
Jednak jeśli czytać ją z tablicy ROM to wtedy jest dopiero 16-bitowy procek.
Trudno tak rzec jaki on właściwie jest.
Na stronie 55(AC)/55DS Microchip podaje świetny przykład kasowania pamięci.
Dlaczego sam go nie stosuje?
Przecież można by go lepiej adaptować, nawet na kasowanie 16-bitowe.
Example 5-5
LFSR FSR0 ,0x100 ;
NEXT
CLRF POSTINC0 ; Clear INDF register then inc pointer
BTFSS FSR0H, 1 ; All done with Bank1?
GOTO NEXT ; NO, clear next
CONTINUE ; YES,
Oczywiście jest tutaj sprawdzany tylko przeniesienie z bitu 8 na 7 FSR,
ale przecież kompilator mógłby sobie poukładać w pamięci tak zmienne, aby
ostatnia kończyła się
na adresie modulo 256.
Przynajmniej do dwóch pętli w tym maleństwie dałoby się tak zrobić.
Ale ja też tak nie zrobiłem u siebie, a wyszło mi to krótsze.
> a Ty napisałeś program
> z pomieszaniem adresowania 8 i 16 bitowego.
Sam procesor juz pomieszany. Robiłem co mogłem.
Ja to nawet zrobiłem to 7-bitowo.
> Nic dziwnego, że
> kompilator dodał instrukcje konwersji typów. Bądż uprzejmy
> przepisać ten program na liczby i adresowanie 16 bitowe
> i dopiero wtedy porównać uzyskane rezultaty.
Taki szkielet. Nie widzę problemu (poza RAMem), aby go rozbudować.
Ale to będzie walka mojej głowy z głupim metakompilatorem.
Wynik obawiam się będzie jeszcze bardziej przerażający.
Myślę, że Ty z łatwością byś sobie też poradził.
> Oczywiście optymalizacja w C powinna być dla "release" a nie "debug".
Była ALL w drugim etapie.
Nie bawiłem się selektywnym klikaniem na checkboxy,
bo w totolotka nie gram :-)
Jednak nie sądzę, że przy jakiejś kombinacji da się coś uzyskać sensownego.
> Współcześnie sprzedawane kompilatory są często generowane przez
> metakompilatory, gdyż koszta zabiłyby producenta oprogramowania.
> K.
A tak zabijają nas kosztami:-)
Myślę, że wiesz, że musiałem spędzić wielokrotnie więcej godzin nad ASM.
W tym C to bajka - tylko kilka linijek :-)
S.
-
4. Data: 2014-04-05 10:10:45
Temat: Re: C vs. ASM na przykładzie PIC18F
Od: Marek <f...@f...com>
On Sat, 5 Apr 2014 05:20:27 +0200, Sylwester Łazar<i...@a...pl>
wrote:
> 1) Objętość kodu ma się następująco (dla otymalizacji Debug):
Zakładanie, że mniejsza objętość kodu to szybszy kod często jest
mylne, gcc na pic32 dla optymalizacji na szybkosc generuje 3x większy
kod niż dla optymalizacji na rozmiar.
Oczywiście pic16 wiekszosc rozkazów robi w tym samym czasie
zegarowym, więc teoretycznie na tej arch mniejszy z reguły będzie
szybszy.
> 2) Testów czasowych _nie robiłem_, ale główna pętla przepisywania
rekordów
> Wygląda na to, że w C program działa jakieś 6x wolniej.
No i co z tego? To nie jest żaden argument. W programowaniu istotne
jest ile dana implementacja kosztuje i ile czasu będzie tworzona.
Nikt nie będzie inwestował w nieprzenośny asm jak taniej GOOTOWY już
kod skompilować na szybszy procek.
--
Marek
-
5. Data: 2014-04-05 10:15:34
Temat: Re: C vs. ASM na przykładzie PIC18F
Od: Marek <f...@f...com>
On Sat, 5 Apr 2014 05:20:27 +0200, Sylwester Łazar<i...@a...pl>
wrote:
> 5) Po włączeniu optymalizacji "Enable on" kod zmniejszył się do 342
bajtów
Wyłączyłeś optymalizację _czego_? Czytając to co napisałeś ma się
wrażenie, że kompletnie nie znasz narzędzia i nie wiesz jak je
prawidłowo użyć.
--
Marek
-
6. Data: 2014-04-05 10:25:09
Temat: Re: C vs. ASM na przykładzie PIC18F
Od: Marek <f...@f...com>
On Sat, 5 Apr 2014 05:20:27 +0200, Sylwester Łazar<i...@a...pl>
wrote:
> Nie jestem w stanie sobie wyobrazić sprawnego sortowania 5000 liczb,
> pobieranych np. z pendrive'a,
Natomiast ja bardzo chętnie chciałbym takie zadanie zobaczyć napisane
przez Ciebie w asm, o które proszę już od pół roku, założenia do
kodu:
- obsługa pendrive usb
- obsługa vfat
- sortowanie 5000 liczb ze wskazanego pliku
- sensowny czas wykonania (krótki timetomarket) - powiedzmy tydzień
Na marginesie, do czego sensownego może się akurat przydać sortowanie
w 8bitowym mcu 5000 liczb... pachnie mi to znowu jak nieekonomiczne
użycie mcu do realizacji danego zadania.
--
Marek
-
7. Data: 2014-04-05 10:32:02
Temat: Re: C vs. ASM na przykładzie PIC18F
Od: Marek <f...@f...com>
On Sat, 05 Apr 2014 06:40:26 +0200, John Smith <d...@b...pl>
wrote:
> CPU procesora PIC18F2320 jest 16 bitowe a Ty napisałeś program
Ten proc jest 8 bit, co miałeś na myśli pisząc cpu 16 bit? 18f nie
jest w stanie jako argument rozkazu przyjac bezpośrednio 16 bitowy
adres docelowy lub źródłowy,.
--
Marek
-
8. Data: 2014-04-05 11:01:20
Temat: Odp: C vs. ASM na przykładzie PIC18F
Od: Sylwester Łazar <i...@a...pl>
> > 5) Po włączeniu optymalizacji "Enable on" kod zmniejszył się do 342
> bajtów
>
> Wyłączyłeś optymalizację _czego_? Czytając to co napisałeś ma się
> wrażenie, że kompletnie nie znasz narzędzia i nie wiesz jak je
> prawidłowo użyć.
>
> --
> Marek
Na razie odpowiem na ten post, bo musze sprostować.
Jednak zastanów się, czy chcesz na poważnie dyskutować, czy wpadłeś w furię.
Wysłałeś 4 agresywne posty. Do końca dnia jeszcze możesz takich wysłać
sporo,
więc nie wiem czy warto...
Co do optymalizacji.
Zapewniam Cię, że umiem i rozumiem zaznaczać checkboxy z różnymi opcjami
narzędzi do kompilacji kodu.
Pomyliłem się i napisałem "Enable On", a powinno być "Enable all".
Przepraszam.
W każdym razie włączyłem poprawnie wszystkie opcje optymalizacji.
I jak napisałem - nie grałem w totolotka.
S.
-
9. Data: 2014-04-05 11:04:59
Temat: Re: Odp: C vs. ASM na przykładzie PIC18F
Od: Marek <f...@f...com>
On Sat, 5 Apr 2014 11:01:20 +0200, Sylwester Łazar<i...@a...pl>
wrote:
> Jednak zastanów się, czy chcesz na poważnie dyskutować, czy wpadłeś
w furię.
Ja jestem oceanem spokoju, bądź łaskaw porzucić osobiste uprzedzenia
i komentować merytorycznie i na temat wątku :)
--
Marek
-
10. Data: 2014-04-05 11:10:32
Temat: Re: C vs. ASM na przykładzie PIC18F
Od: jacek pozniak <j...@f...pl>
Sylwester Łazar wrote:
> Tak jak opisałem wcześniej, przygotowałem procedurę, która sortuje mi
> kilka napięć metodą zliczania:
> http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0023.php
>
> Jako, że ostatnio toczą się dyskusje o kompilatorach C,
> pozwoliłem sobie zrobić prosty test.
>
> Napisałem procedurę w ASM i w C.
> Oczywiście obie działają poprawnie.
> 1) Objętość kodu ma się następująco (dla otymalizacji Debug):
> 470 bajty kodu w C
> 128 bajty kodu w ASM
> ...
To ja podam wyniki kompilacji Twojego kodu:
1. Kompilator HiTech 8.05PL2 -O -Zg, procesor pic16f876A:
149 words(słów, nie bajtów) ROM, 38 bytes RAM
bez funkcji zlicz(), odpowiednio 54 słów ROM, 35 RAM
2. Kompilator HiTech 9.63PL2 --opt=ALL , procesor pic18f252:
284 bytes ROM, 37 RAM
bez funkcji zlicz(),75 bytes ROM, 15 bytes RAM
3. Kompilator XC8, ver 1.3 --opt=ALL (60-dniowa) procesor pic18f252: nie
wiem do końca, ćzy jest właczone MODE PRO niby w ciągu 60 dni powinno być
właczone ale coś kod zbyt duży wychodzi):
606 bytes ROM, 43 RAM
bez funkcji zlicz() 160 ROM, 35 RAM
4. AVR-GCC na atmega32 -O2 (nie znam jeszcze dokładnie avr-gcc i jego opcji)
376 ROM, 37 RAM
bez funkcji zlicz() 222 ROM, 37 RAM
Wśród PICów jaki widać zwycięzcą jest 8.05 na PIC16.
jp