SVETELEKTRO

2. októbra 2015   Verzia pre tlač Verzia pre tlač

Pokročilé programovanie mikropočítačov v jazyku C. Diel č.1


24mato

Float, int alebo char?

Ahojte programátori. Som na tomto fóre zaregistrovaný už pár rokov a mojou oblúbenou diskusnou témou sú mikroprocesory. Každým rokom sa tu objaví množstvo nových programátorov, ktorý hľadajú  rady  a odpovede na jednoduché ale aj zložitejšie otázky ohladne programovania rôznych typov mikropočitačov. Zawinova knižka Programujeme AVR v jazyku C je veľmi užitočným návodom pre začínajúcich programátorov. Ja som si pripravil sériu článkov, ktoré sa venujú ani nie tak pokročilím metódam programovania ale skôr poukazujú na záludnosti alebo krásu programovania mikroprocesorov v jazyku C.

Prvý článok je zameraný na správny výber dátového typu premmenej. Premenné sú základom každého programu. V malých domácich projektoch na výbere správneho dátového typu nemusí až tak veľmi záležať ale pri velkých projektoch sa každý ušetrený byte počíta. Takisto nejde len o šetrenie miesta ale aj o dobu trvania tej ktorej operácie s daným dátovým typom, čo pri niektoržch časovo kritických slučkách hrá dôležitú úlohu.
 Pomocou jednoduchého programu a osciloskopu porovnávam dĺžku trvania výpočtu jednoduchých matematických operácii. Konkrétnejšie, mám dve premenné A a B. V prvom prépade sú dátového typu float. Premenné A a B inicializujem ľubovolným desatinným číslom. Následne vykonám s premennými A a B základne matimatické operácie, sčítanie, odčítanie, násobenie a delenie. Pomocou osciloskopu si odmeriam čas trvania matematickej operácie a to tak, že tesne pred samotnou operáciou nahodím ľubovolný nevyužitý pin mikroprocesora do logickej jednotky a okamžite po skončení operácie ho zase zhodím do logickej nuly. Túto zmenu stavu pinu si odsledujem na osciloskope.
Jednoduchý obslužný program na sledovanie doby operácie:


Doba trvania – celočíselné násobenie int * int

Pre dátové typy char, int a long použijem rovnaký program len premenným A a B zmením datový typ. Tento pokus robím na dobre známom mikropočítači Atmega8. Interný oscilátor mi beží na 8Mhz. Namerané časy jednotlivých matematických operácii su zaznamenané v tabuľke.

Tab.1 Doby trvania matematickýh operácii s rôznymi dátovými typmi

dátový typ float char int long
matematická operácia čas [us] počet cyklov čas [us] počet cyklov čas [us] počet cyklov čas [us] počet cyklov
+ 100 800 1.15 9 2 16 3.6 28
110 880 1.15 9 2 16 3.6 28
* 240 1920 1.5 12 2.9 23 9.1 72
/ 149 1192 10.7 85.6 30 240 78.5 628
>>1  
n/a
0.9 7 1.5 12 2.6 20
<<1 0.9 7 1.5 12 2.6 20
<<2 0.9 7 1.7 13 3.9 31

  
Premenná typu char je 8 bitová celočíselná, int je 16 bitový a long by mal byť 32 bitový. Osem bitové operácie vykonáva Atmega 8 celkom svižne až na operáciu celočíselného delenia. V prípade ak delíme číslom 2,4,8.. je ovela lepšie použiť bitový posun ako operáciu delenia. Čisto teoreticky správny compiler by si to mohol ošetriť aj sám ale v mojom prípade to tak nebolo. Podľa očakávaní operácie s dátovým typom float trvajú najdlhšie. Dovolím si povedať, že veľmi dlho. Preto je veľmi výhodne sa dátovému typu float skôr vyhýbať ako ho využívať. Osobne ma prekvapilo, že operácia delenia trvá kratšie ako operácia násobenia pri floate. Je to zrejme featura atmieg.

Možno si poviete. Dobre to som vedel aj predtým že matematické operácie s float premennými trvajú najdlhšie. A keď budem potrebovať zrýchliť výpočet tak použijem modernejší mikrokontrolér s FPU jednotkou a nie Atmegu8. Tak trochu súhlasim. No veľmi dolezitú úlohu v tabuľke hra aj stĺpček, ktorý popisuje počet cyklov potrebný pre danú operáciu. Počet cyklov je vypočítaný podľa veľmi jednoduchého vzorca.
Počet cyklov = čas trvania operácie x frekvencia jadra (u mňa 8 Mhz)
Pre atmegu8 a pre malé domáce projekty to moc nehrá veľkú úlohu, ale v prípade že je dôležité brať ohlad aj na spotrebu čipu v danej aplikacií je dobre vedieť či sa mi náhodou nevyplatí znížiť frekvenciu jadra a šetriť energiu batérie (pozri tabulku na strana 292 z datasheetu Atmegy 8).
Toľko prvý článok. Teším sa na ďalšie pokračovanie a na vaše postrehy k tomuto článku.
Prajem štastnú ruku pri výbere dátových typov premenných.       

Ako sa vám páčil tento článok?
  • Páči sa mi (1)
  • Súhlasím (0)
  • Zábavné (0)
  • Informatívne (0)

Komentáre (9)

  1. peterple píše:

    Ahoj.
    Zaujali ma niektoré tvoje tvrdenia a výsledky. Rozhodol som sa preto opakovať tvoje pokusy.

    V prvom rade je drobná chybička v kóde. Riadok 16 premenná b1 nie je deklarovaná. Ale to je jasné že to je preklep a má tam byť iba b.

    Na zistenie počtov cyklov jednotlivých operácií som použil trochu iný prístup, hádam aj trochu jednoduchší pretože nevyžaduje osciloskop, dokonca ani reálny procesor. Samozrejme ide to ľahko a rýchlo v simulátore. Takže použil som AVR studio verziu 6.0.1843. F7 kompilácia potom pár krát F10 až som na inkriminovamom riadku matematickej operácie. V okne procesora pozrem Cycle Counter, stisnem F10 odčítam od novej hodnoty Cycle Counter tú predchádzajúcu.

    No a napríklad delenie int (3/3) trvalo 243 cyklov. Jasne to bude asi chyba odčítania z osciloskopu.

    Zaujímavú hodnotu uvádzaš pre delenie char – 85.6 cyklov. Neviem si dosť dobre predstaviť ako procesor urobí 0,6 strojového cykla. Je to maličkosť, ale je jasné že také nemôže byť.

    Najviac ma zaujímalo to delenie a násobenie float. Takže delil a násobil som 3.5 a 3.8. Nasobenie trvalo 153 cyklov a delenie 495 cyklov. Čo už mi pasuje trochu viacej ako tvoje výsledky. Aj keď vo float nie som doma, pretože som ich ešte nikdy nepotreboval.

    Na záver už len trochu upresním, že daným spôsobom sa nedajú zistiť presné dĺžky trvania matematických operácií, ale sa tam pridáva ešte réžia na manipuláciu s parametrami. Ktorá je napríklad pri char až 800 percent. Pretože také spočítanie v skutočnosti trvá iba jeden strojový cyklus. Ale to už je mimo C, takže ako by som nič nepovedal.

    Teším sa na ďalšie časti lebo v sekcii programovanie nebol príspevok ani nepamätám. Dúfam že sa to tu trochu rozhýbe.

    • 24mato píše:

      Peperle , plne s tebou suhlasim vo vsetkych bodoch.

      Zabudol som spomenut aky hardver som pouzil. Meral som to osciloskopom Hantek DSO5102P 100 MHz, 1 GSa/s. Tomu 1GSa/s ani sam moc neverim.

      Ja som pouzil este AVR studio 4. Myslim ze aj tam je v simulatore cycle counter ale reku ked mam pri sebe atmegu aj programator tak preco to nerobit priamo na nom. Suhlasim aj tvoje riesenie je dobre riesenie este dokonca aj jednoduchsie.

      Jasne 85.6 cyklov je blbost :). To si nevsimajte :).

      Tie floaty si nasobil a delil tiez v simulatore? Ja som tomu tiez nechcel verit ked mi taketo vysledky daval osciloskop. Meral som dva krat a stale to iste. Mam taky pocit ze zalezi aj ake cisla pouzijes. Myslim tym ze 5.1/5.0 bude mat iny cas vypoctu ako 452.23564/4265.2545. To este musim odskusat.

      Inak diky za pripomienky.

      • peterple píše:

        Všetko som robil v simulátore. Teraz som hodil tie tvoje čísla a rovnaký výsledok 495 cyklov. Prekrokoval som si to celé delenie v asm a žiadne špeciálne inštrukcie som tam nevidel (ROL LSL). Ale možno sa zoptimalizoval kompilátor. GCC 4.6.1

        • 24mato píše:

          Hej compiler moze byt pricinou. Ja som to robil s AVR studiom 4.19 a toolchain 3.4.0.1146.

          • dxr píše:

            Co tak si pozret to AN co dava ATmel k 8bit jadram
            je tam porovnanie algoritmov a optimalizacia – velkost a pocet cyklov a este jeden parameter. inak ked compilujete este uvazdajte options lebo tam sa daju nietkore typy pretypovat a zapnut rozlicnu optimalizaciu.
            Peter pouzil lepsiu metodu ten clock counter sa da pouzivat aj ako Profiler.

            Inak pekny clanok cakam na dalsi diel lebo zawinov serial potrebuje update niektore veci sa opakuju na fore len preto ze ludia idu step by step podla toho serialu.

  2. speed píše:

    Pekný článok, len tak ďalej. Ak by si sa nudil mohol by si pomocou osciloskopu zmerať aj také veci ako napr. aký čas je potrebný na prepnutie pinu z Log1 do Log0 alebo za ako dlho sa vyhodnotí podmienka if.

  3. martin63 píše:

    Pekne pekne, mohol by si napisat nieco o tom, ako sa vyhnut pouzitiu float, ked uz je potrebne nieco prepocitavat na desatinne miesta.

  4. ok2jnj píše:

    Ahoj,
    pěkný článek. Přemýšlím o tom, v jaké úloze vhodné pro mikroprocesor řady ATmega je potřebný float? Napadá mě jenom univerzální kalkulačka, kde nevím, jak velké bude číslo s kterým počítám.
    S ATmega jsem zatím řešil úkoly z automatizace a měření, kde je na vstupu číslo s předem definovaným rozlišením – např. 12-ti bitový AD převodník. Potom se provádí nějaké výpočty. V mezním případě mocnina, potřebuji 24 bitů. Nebo odmocnina, v tom případě číslo z AD převodníku vynásobím konstantou 10000 a pak udělám odmocninu. Výsledek – celé číslo v datovém typu long bude mít rozlišení vždy minimálně 12 bitů, takže neztrácím přesnost původního rozlišení zaokrouhlováním. Při zobrazení výsledku na displeji vložím desetinnou čárku tam kam patří podle konstant, kterými jsem vynásobil vstupní hodnoty.
    Nebo znáte úlohu, kterou jste řešili s ATmega a mnou popsaný postup výpočtu byl nevýhodný, bylo nutné mít pohyblivou desetinnou čárku a použít datový typ float?

  5. berk píše:

    Díky za zajímavý článek.

    Jen podle mě nebylo dostatečně zdůrazněno, že je potřeba si dávat pozor na přetečení/podtečení. Největší riziko je asi při násobení char c = 3 * 60; výsledek je bez nejmenšího varování -76.

    Pokud někoho téma optimalizace psaní kódu v c zajímá do hloubky, tak doporučuju vynikající knihu „Computer Systems – A Programmer’s Perspective“ ISBN-13: 978-0-13-610804-7

Pridaj komentár