Zdroják vám nepošlu, Cčko moc neovládám.
Myšlenka je jednoduchá: Vložení (setRepeated), smazání a dokončení tasku (loop) provede vždy výpočet následujícího (= jediný průchod polem), pak už se jen čeká na nextRun (testován primitivním porovnáním s okamžitým millis()) uvedený v dopočteném tasku, nebo nastaví přerušení na uvedený čas.
To ale vyjde podstatně hůř.
- Polem se musí procházet vždycky, aby se zjistilo, co spustit.
- Porovnat dvě čísla znamená odečíst je a testovat zero flag (CMP nebo SUB). INC a DEC taky ovlivní zero flag. Nic na tom neušetřím.
- Countdown nevyžaduje předpočítání dalšího startu.
- Pokud se vejdu s dalším spuštěním tasku do 250 ticků systému, stačí u countdownu 8b, u porovnání času je potřeba 16-32b (sestřelí osmibit až hrůza).
- U countdownu nepotřebuju přepočet před startem.
- U countdownu, pokud není potřeba logování nebo real time, můžu tick count vyhodit úplně.
- Tick count by musel být volatile (mění se v přerušení timeru a i v průběhu té funkce), static (aby se nesmazal po vyskočení z té porovnávací funkce) a register (aby se ušetřila polovina přístupu do RAM) současně. U 32b tick counteru a 8b CPU znamená obětování čtyř registrů.
Na jednočipu, víc než kde jinde, musí být Occamova břitva pěkně ostrá. Nesjou zdroje nazbyt.
při porovnání dvou čísel se nic neodečítá, ale porovnává se po bytech. Tedy při předpočítání dalšího spuštění v každym kroku 1x sada lds (předpočítané nextRun) a pak sada cp/cpc vůči now, bez předpočítání 2x sada lds (lastRun + interval), sada sub/sbc a sada cp/cpc vůči now. Problém při počítání nextRun by byl možná spíš při přetečení při dlouhodobém provozu
Citation needed.
Odečítají se právě ty byte při tom porovnání. Algoritmus porovnání vícbytovýho čísla do puntíku sedí s rozdílem dvou čísel až na to, že se výsledek zahodí.
Při porovnání se stav vždycky musí dostat do flagů. Tzn. nějaká "porovnávací" instrukce, ze které vypadne "rovno", "menší", "větší". Ve druhé je podmíněný skok, který testuje flagy.
Jasně, šlo by na čip zadrátovsat něco ve smyslu 7485, ale proč? Znamenalo by to přivedení dvou sběrnic od registrů jako u ALU, drátovat výstupy do řadiče, další flagy, podmíněný skoky s dalšíma podmínkama pro tytyo flagy a podobný nesmysly.
Pokud ALU podporuje ADD a SUB, tak není potřeba vymýšlet opičárny navíc. Sběrnice ze dvou registrů tam jsou (zadarmo), odpadne blok komparátoru, SUB/SBC nastaví Z flag, pokud je výsledek nula (rovnost, A-B=0 <=> A=B) a N flag pokud B>A. A v dekodéru instrukcí stačí vyhradit varianty SUB a SBC bez ukládání výsledku (nepošle se write signál do cílovýho registru).
V AVR architektuře není o komparátoru ani slovo, popis ALU taktně mlčí a v přehledu ASM je u instrukce CP popis Rd-Rr, ovlivněno Z, N, V, C, H, 1 takt. Podobně CPC a CPI, založeno na rozdílu...
Mrkni třeba na http://www.atmel.com/Images/doc2552.pdf, tabulka na str. 369, instrukce CP, CPC, CPI
No a je z pohledu rychlosti jedno, jestli bude
CP R1, R2
BREQ LaunchTask
nebo
DEC R1
BREQ LaunchTask
Jenom v prvním případě nebude asi 8b stačit a trochu to nabobtná ;)
tohle generuje avr-gcc (bavíme se o konkrétní úpravě konkrétní knihovny která se tím kompiluje)
kód s předpočtem koncové hodnoty
if (x1 > x2) {
190: 50 91 09 01 lds r21, 0x0109
194: 60 91 0a 01 lds r22, 0x010A
198: 70 91 0b 01 lds r23, 0x010B
19c: 80 91 04 01 lds r24, 0x0104
1a0: 90 91 05 01 lds r25, 0x0105
1a4: a0 91 06 01 lds r26, 0x0106
1a8: b0 91 07 01 lds r27, 0x0107
1ac: 84 17 cp r24, r20
1ae: 95 07 cpc r25, r21
1b0: a6 07 cpc r26, r22
1b2: b7 07 cpc r27, r23
1b4: 08 f4 brcc .+2 ; 0x1b8 <main+0x2e>
kód bez předpočtu koncové hodnoty
if (x1 -x3 > x2) {
1ba: 80 91 08 01 lds r24, 0x0108
1be: 90 91 09 01 lds r25, 0x0109
1c2: a0 91 0a 01 lds r26, 0x010A
1c6: b0 91 0b 01 lds r27, 0x010B
1ca: 00 91 00 01 lds r16, 0x0100
1ce: 10 91 01 01 lds r17, 0x0101
1d2: 20 91 02 01 lds r18, 0x0102
1d6: 30 91 03 01 lds r19, 0x0103
1da: 40 91 04 01 lds r20, 0x0104
1de: 50 91 05 01 lds r21, 0x0105
1e2: 60 91 06 01 lds r22, 0x0106
1e6: 70 91 07 01 lds r23, 0x0107
1ea: 80 1b sub r24, r16
1ec: 91 0b sbc r25, r17
1ee: a2 0b sbc r26, r18
1f0: b3 0b sbc r27, r19
1f2: 48 17 cp r20, r24
1f4: 59 07 cpc r21, r25
1f6: 6a 07 cpc r22, r26
1f8: 7b 07 cpc r23, r27
1fa: 08 f4 brcc .+2 ; 0x1fe <main+0x74>
v té nepředpočítané verzi je úplně to samý co v nepředpočítané + něco navíc, takže je úplně jedno jestli alu komparátor obsahuje nebo ne, protože se volá v obou verzích stejně
Jste na sebe moc přísní. Jádro původní rady bylo IMHO v tom vypočítat po změně fronty, který další task by měl být volán, a pak na něj čekat v krátké smyčce. Zatímco současná implementace nehledá další task jen po změně fronty, ale neustále, protože stejně nemá nic lepšího na práci, takže vlastně běhá v delší smyčce.
Ve skutečnosti by to ale dle té rady fungovalo špatně, protože když bych náhodou minul ten přesný okamžik, kdy měl být spuštěn další task, tak bych pak nevěděl, který mám vlastně spustit a musel bych znovu celou frontu procházet a hledat, takže bych prd ušetřil, naopak bych to ještě zpomalil. Ta moje současná implementace prostě funguje správně (tj. tak, jak byla zamýšlena) a nemám důvod ji měnit.
Vůbec netuším, o čem píšete...
Na co budu pořád ve smyčce procházet pole, když v něm je pořát to samé, dokud do něj není něco přidáno, z něj ubráno nebo změněno (což dělají 3 přesně definovaná místa), což jsou k počtu cyklů smyčky zcela ojedinělé události, nehledě na to, že varianta s procházením pole neumožňuje programování časovače na probuzení pro operaci.
Chcete říct, že „now - t.lastRun >= t.interval“ je stejně rychlé jako „now >= t.nextRun“?
Máte potřebu řešit bitová harakiri s tím, že to možná přeteče a možná taky ne?
Dál nemám sílu.
Autorovi Taskeru děkuji za ušetření mi nejhorší práce, už jen doladím.