1

Téma: Best practices

Mám několik dotazů k obecným zásadám psaní programu:

a) Jak přesně dochází k alokaci promněnných (oddíl variables, zásobník, halda)? Kde jsou umístěné globální proměnné, definované mimo tělo funkce (včetně main) a kde dočasné proměnné, definované uvnitř funkcí? Předpokládám z dřívějších diskuzí, že parametry volání funkcí jsou na zásobníku.

b) Mohu být textové sdílené proměnné alokovány na haldě? Předpokládám, že ne, protože z principu u nich musí být známá pevná délka. Takže např. T00[20] je jediná správná definice.

c) Dá se nějak při novém startu programu po jeho ukončení chybou ve zdrojovém kódu zjistit, ve kterém řádku k chybě došlo? Taková chyba se může objevit náhodou po průchodu téměř nepoužívanou větví, reset watchdogu sice program znovu spustí, ale přijdu o informaci o chybě z ladicího výpisu na konzolu.

d) Ve FULL-C je informace o důvodu resetu systému umístěna v systémové proměnné 40 (SoCresetReason). Ta mi vrací např. 0x1460000, ale podle příkladu na Wiki, by to měly být spodní bity. Mám si to posunout  >> 16?

e) Je načítání programu nějak optimalizováno (přeskakování řádků, kde není splněna podmínka) nebo je lepší dlouhé úseky ve smyčce nahradit raději voláním funkcí - zrychlí se tím zpracování smyčky?

2

Re: Best practices

Odpovědi doplním na https://wiki.merenienergie.cz/subdom/wiki/index.php?title=FULL-C:_tips_and_tricks .

a)
Veškerá pamět je fyzicky společná (RAM procesoru), pro účely FC je pak logicky rozdělena - rozdělení je pěkně vidět po prvním překladu ve FULLC.exe, je tam interaktivní proužek s barvami, které ukazují, kde co jak bude (a jak to bude velké).
Globální proměnné jsou tedy na pevném místě v RAM. Lokální jsou na zásobníku.
Parametry volání funkce také na zásobníku. Pozor ale nejsou tam uloženy "jen tak" tedy jen surové hodnoty za sebou apod., u FC jsou všechny proměnné na stacku obaleny dalšími informacemi (např. o rozsahu atd., protože SDS si interně hlídá např. chyby typu buffer overflow).

b)
U sdílených proměnných je potřeba znát (a tedy zadat) pevnou délku, využívá to kód který to integruje vůči webovému rozhraní.
Už z  hlediska přehlednosti programu je vhodné je definovat na jeho začátku, jako globální proměnné.

c)
Tohle je funkcionalita, která teprve bude implementována. Ano, bude to užitečné.
Při znovuspuštění programu, bude text poslední chyby schován a zobrazen, tak aby se neztratil.

d)
Způsob kódování hodnoty je rozdílný dle typu SDS. Dokumentace bude aktualizována.
Řada 64 a 128 to už má interně posunuté, řada 512 ne (protože je tam více platných bitů s významy).

e)
Volání funkce má nepatrně větší overhead než postupné vykonávání kódu.
Prakticky by ani u jednoho z řešení neměl být významně měřitelný rozdíl.
Zkuste si napsat benchmark.

Pochlubte se - popište jak využíváte své zařízení SDS zde ! Můžete si bezplatně přidat svou reklamu !

3

Re: Best practices

Děkuji za vyčerpávající odpověď.

Nicméně ještě k bodu e): Jedná se mi o to, jakým způsobem se přeskakují bloky instrukcí pro neplněnou podmínku a zda je tedy lepší než:

   if (obcas_se_stane)
   {
      ...
      hodně řádků...
      ...
   }

není lepší psát:

    if (obcas_se_stane)
      proved_hodne_radku();

    void proved_hodne_radku()
    {
      ...
      hodně řádků...
      ...
    }

Podmínka nastane jednou za čas a přeskakování velkého množství řádků ve druhém případě odpadne.
Podobně např. ve funkci se dá místo:

   void fce()
   {
       if (podminka)
       {
         ...
         hodně řádků...
         ...
       }
   }

napsat:

   void fce()
   {
      if (!podminka)
         return;
      ...
      hodně řádků...
      ...
   }

Samozřejmě si mohu udělat benchmark.

4

Re: Best practices

Rozdíl tam bude zcela minimální, určitě zanedbatelný.
Hlubší optimalizace se neprovádí (prakticky odpovídá O0), takže bych to celkově bral spíše jako otázku Code Style a obecného způsobu zápisu programu, tedy především čitelnost.

Takže pro první příklad bych volil druhou možnost (dát to do své funkce),
pro druhý příklad (to bude časově nastejno), bych z důvodu čitelnosti kódu, určitě použil vyskočení podle podmínky (return).

Pochlubte se - popište jak využíváte své zařízení SDS zde ! Můžete si bezplatně přidat svou reklamu !

5

Re: Best practices

Děkuji, to mi zcela stačí.

Ještě k těch alokacím. Je tedy jedno, jakým způsobem alokuji velký globální buffer, tedy:

    char *buffer = (char *)malloc(5000);
    char buffer[5000];

V prvním případě alokuji na haldě, kde mám místa dost, ve druhém případě se místo přidělí staticky při kompilaci (uploadu/startu) a ubere to na velikosti haldy, v obou případech mi to ukousne stejnou část paměti. Asi je to úplně jedno, co použiji, ten druhý zápis vypadá možná lépe.

6

Re: Best practices

V případě definice na zásobníku, je potřeba pohlídat že zásobník má dostatečnou velikost (nastavenou při překladu programu).

Pokud se dá vyhnout alokaci (heap), tak je to dobře, protože to snižuje eventuální fragmentaci.

Pochlubte se - popište jak využíváte své zařízení SDS zde ! Můžete si bezplatně přidat svou reklamu !