[avr-gcc] zliczanie modulo

Powiedzmy ze mam licznik: volatile uint8_t licznik;

i powiedzmy ze gdziestam go zliczam, ale chce by dochodzil wylacznie do pewnej liczby i koniec - zapetlal sie. Czyli zliczanie modulo. Gdy liczba zliczana jest potega 2ki-1 to najprosciej chyba jest zrobic to (np dla zliczania 0-15) licznik++; licznik&=0x0f; (czyli licznik&=15, czyli pozostawienie jedynie 4 ostatnich bitow)

Jak (ze swojego doswiadczenia) sadzicie, czy bardziej przyzwoita metoda (jesli chodzi o sposob wykonania) jest pisanie warunku licznik++; if (licznik > wartosc_maksymalna) licznik=0; czy tez zrobienie tego przez modulo licznik++; licznik%=wartosc_maksymalna; Domyslam sie ze warunek powinien dac wiekszy kod, ale czy operacja asm porownania zmiennej do stalej nie bedzie szybsza niz operacja dzielenia (obliczenia reszty z dzielenia)? Mowa oczywiscie o avr klasy tiny2313/mega16 a nie avr32 ;)

Reply to
BartekK
Loading thread data ...
Reply to
Bogdan Gutknecht
Reply to
Bogdan Gutknecht
Reply to
Piotr Wyderski

Piotr Wyderski napisał(a):

Jakos nie chcialo mi sie patrzec w deasemblowany kod po kompilacji, ale jestes pewny ze gcc wie jak to przeksztalcic na if/and ? Po wielkosci kodu wnioskuje ze mimo wszystko uzywa asm-procedurki dzielenia/reszty, moze zalezy to od metody optymalizacji (przewaznie uzywam -Os)

Przyspieszenie jest nie o 100% a chyba pare tysiecy procent (gdy licznik jest np 16bit, i porównujemy & i %), zmiana wielkosci kodu o kilka bajtow przy kazdej operacji, ale dla mnie istotna nie jest predkosc wykonywania kodu, a opoznienie - nawet tylko 50% czas wykonywania fragmentu kodu to 2x szybsza reakcja na zdarzenie wywolujace ten fragment.

Reply to
BartekK

Piotr Wyderski przemówił ludzkim głosem:

Zastępowanie dzielenia modulo operacjami logicznymi (albo porównaniami), to nie zaciemnianie kodu, tylko (celowe) ograniczenie swobody ruchu kompilatorowi. Dzięki temu nie dopuszcza się do sytuacji, w której po zmianie rozmiaru bufora/tablicy, w krytycznym czasowo kawałku kodu pojawia się długotrwała operacja dzielenia (nie każdy procesor ma sprzętowe dzielenie), zamiast operacji logicznej (czy porównania).

Reply to
Zbych

Jesli zmienisz rozmiar bufora na taki przy ktorym tylko dzielenie wchodzi w gre to i tak nic nie poradzisz, a jesli da sie go zastapic operacja logiczna to kompilator IMHO ciagle zastapi. Wiec argument malotrafny. BTW. Wlasnie sprawdzilem w avr-gcc dla ATMegi8 operacje typu int zmienna%=16. Kompilator generuje sekwencje: pos=pos%16;

+00000440: 704F ANDI R20,0x0F Logical AND with immediate +00000441: 7050 ANDI R21,0x00 Logical AND with immediate

a wiec najkrotszy mozliwy kod. Kompilowane z -Qs.

Reply to
T.M.F.

T.M.F. przemówił ludzkim głosem:

Zauważ, że w swoim tekście pisałem także o użyciu porównań. Generalnie stosuję zasadę:

  1. Jeśli dopuszcza się, że rozmiar tablicy/bufora będzie przyjmował tylko wartości będące potęgą 2, to % zastępuję operacjami logicznymi.
  2. W pozostałych przypadkach staram się używać porównań.
Reply to
Zbych

Tylko nie bardzo wiadomo ile z GCC ucieto przy implementowaniu go na tak prosta maszynke.

No i czy aby na pewno na danym procesorku bardziej oplaca sie x>>2 niz x/4 :-)

Eee - robi to. Problem w tym ze skoki zazwyczaj rozwalaja kolejke procesora i wydluzaja kod. Ale .. nie w kazdym :-)

J.

Reply to
J.F.

Piotr Wyderski przemówił ludzkim głosem:

W wielu przypadkach indeks do pobierania danych z tablic/buforów nie zmienia się w sposób dowolny, lecz jest sukcesywnie zwiększany/zmniejszany o ściśle określoną wartość (zazwyczaj nie przekraczającą wielkości bufora). Taką sytuację masz w przypadku obsługi wszelkiej maści buforów cyklicznych itp. I w takich przypadkach można zaoszczędzić czas procesora stosując porównanie i warunkowe odejmowanie/dodawanie zamiast równoważnej operacji dzielenia modulo.

Przykład padł już w jednym z wcześniejszych postów. Taki kod na avr wykona się szybciej:

niż: indeks = (indeks + 1) % rozmiar_tablicy;

w przypadku, gdy rozmiar jest np. 27 (twoja potęga 3 :-)

Reply to
Zbych

Piotr Wyderski przemówił ludzkim głosem:

Idąc twoim tokiem rozumowania, to należałoby wyciąć wszystkie opcje kompilacji kodu, bo ogranicza to swobodę translacji. Ja nie widzę nic złego w tym, że próbuję przy pomocy kodu źródłowego wymusić określone zachowanie kompilatora.

Przykładowy kawałek:

a = (a+1) % STALA;

W zależności od wartości stałej kompilator może użyć dzielenia, albo operacji bitowej. Problem polega na tym, że zmiana stałej diametralnie może zmienić czas wykonania kodu, co w kodzie krytycznym czasowo jest niedopuszczalne.

Wiesz, nie samym gcc programista żyje. Zresztą na niektórych platformach gcc też produkuje koszmarny kod. Przykładem niech będzie R8C, gdzie gcc generuje kod jak dla RISCa, marnując potencjał procesora, albo nie potrafi czasem rozpoznać możliwości użycia operacji zerowania/ustawiania pojedynczych bitów i z uporem maniaka stosuje sumę/iloczyn logiczny. Ale stosując odpowiednią sztuczkę składniową można gcc zmusić, do używania instrukcji zapalania/gaszenia bitów. Gdybym musiał użyć gcc na ten procesor, to nie wahałbym się przed używaniem "sztuczek", gdyby tylko pozwoliło to na uzyskanie bardziej zwięzłego kodu.

Reply to
Zbych

Owszem, pierwszy przyklad wykona sie szybciej, ale te dwie instrukcje nie sa rownowazne. Stad druga postac nie moze byc zoptymalizowana. Ergo, przyklad do kitu.

Reply to
T.M.F.

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.