4.19. Memóriakészletek

Egy kamera, amely három teljes felbontású képkockát tárol egy képkocka-puffer készletben, emellett külön előnézeti puffert futtat, és még a Python szkriptnek és objektumainak is helyet biztosít, több memóriát zsonglőrködik, mint amennyit az MCU egyetlen RAM-blokkja nyújtani tudna. A MicroPython úgy fér el mindezzel, hogy szétosztja az MCU által biztosított több különálló memóriafajta között, és minden allokációt arra a memóriafajtára irányít, amelyre valóban szüksége van.

4.19.1. Memóriafajták

Egy modern OpenMV Cam MCU négy különálló memóriafajtát tesz elérhetővé. Az első láthatatlan az alkalmazás számára; a másik három olyan készlet, amelyből az allokációk származhatnak.

  • A CPU adatgyorsítótára – egy kicsi, nagyon gyors memóriaterület, amely a CPU és a RAM többi része között helyezkedik el. Amikor a CPU egy értéket olvas vagy ír a fő memóriából, a gyorsítótár automatikusan megőriz egy másolatot, így ugyanazon adatok ismételt elérése a gyorsítótárban marad, és soha nem kell megfizetnie a lassabb memóriához való kifordulás költségét. A gyorsítótár nem olyan készlet, amelyből az allokációk származnak. Átlátszó az alkalmazás számára – egyszerűen csak gyorsabbnak érezteti a gyakorlatban a RAM többi részét, mint amit nyers késleltetése sugallna, egészen addig a pontig, ahol egy munkahalmaz már nem fér el benne.

  • Szorosan csatolt processzormemória – egy kicsi RAM-blokk, amely közvetlenül a CPU-hoz van bekötve, busz nélkül közöttük. Egyetlen ciklusú elérés, soha nincs találati hiba, soha nincs várakozás. Azok az allokációk, amelyeknek valóban a lehető leggyorsabb memóriára van szükségük – ahol a késleltetés minden ciklusa számít – ebből a készletből származnak.

  • Gyors chipen belüli memória – néhány száz kilobájttól nagyjából egy megabájtnyi RAM, amely az MCU tokozásába van beépítve. Alacsony késleltetés, nagy sávszélesség, de korlátozott méret. A MicroPython kupac itt található, így a Python objektumok elérése gyors marad; a kisebb munkapufferek, amelyeket a CPU gyakran érint, osztoznak a készleten.

  • Lassabb tömeges memória – azokon a kártyákon, amelyek az MCU-t külső memóriacsippel párosítják, több tízmegabájtnyi chipen kívüli RAM, amely a külső buszon keresztül érhető el. Sokkal nagyobb, de minden elérés tovább tart, mint a chipen belüli memóriánál; az adatgyorsítótár elrejti ennek a költségnek a nagy részét azoknál a munkahalmazoknál, amelyeket meg tud tartani, és a különbség azoknál a műveleteknél jelentkezik, amelyek a gyorsítótárba nem férő, túl nagy adatokon söpörnek végig. Olyan allokációkhoz használatos, amelyeknek nagynak kell lenniük, és amelyeket a CPU lassabb sebességen is elvisel – a legfontosabb, hogy a képkocka-puffer készlethez.

A család kártyái egy spektrumon helyezkednek el: néhányuknak csak chipen belüli RAM-ja van; némelyik a chipen belüli RAM-ot egy sokkal nagyobb külső blokkal párosítja. A három allokálható fajta mindegyike memóriakészletként kezelendő – egy darabként, amelyből az allokációk származnak –, és úgy van címkézve, hogy minden kérés azt a memóriafajtát kérheti, amelyre valóban szüksége van.

4.19.2. Az elsődleges képkocka-puffer

A képkocka-puffer, amely a snapshot() mögött áll, nem kér gyors memóriát. Elegendő memóriát kér – semmi többet. Ez abba a készletbe helyezi, amelyik a legnagyobb, így egy olyan kártyán, ahol chipen belüli és külső memória is van, a képkocka-puffer a külső blokkba kerül.

Egy teljes felbontású, háromszorosan pufferelt képkocka-puffer messze túl nagy ahhoz, hogy a gyors chipen belüli készletbe férjen a legtöbb alkatrészen; a nagyobb készlet az egyetlen, amely egyáltalán meg tudja tartani. A CPU adatgyorsítótára elrejti az elérésenkénti költség nagy részét, amikor az alkalmazás feldolgozza a képet, a DMA-motor pedig, amely az érzékelőből feltölti a képkocka-puffert, mindenképpen lépést tart az érzékelő adatütemével.

A pontos méret, amelyet a képkocka-puffer elfoglal, az aktuális pixformat(), framesize() és framebuffers() értékből kerül kiválasztásra; minden alkalommal nő vagy zsugorodik, amikor ezek bármelyike megváltozik.

4.19.3. Másodlagos érzékelő képkocka-pufferek

Egy második CSI példány saját képkocka-puffert kap, amely ugyanabból a készletből kerül allokálásra, amelyet az elsődleges használ. A készlet közös; a pufferek függetlenek. A másodlagos lábnyoma általában jóval kisebb, mint az elsődlegesé, mert a másodlagos érzékelők alacsonyabb felbontáson futnak, így a második képkocka-puffer által elfoglalt többletmemória az elsődlegesének csak egy kis töredéke.

4.19.4. A folyam képkocka-puffer

A kép-előnézet puffer a kivétel. Futásidőben nem a készletek egyikéből kerül allokálásra; ez egy rögzített terület, amelyet fordítási időben foglalnak le, ismert címmel és ismert mérettel. Ez kívül tartja az előnézeti útvonalat minden más allokáció útjából – a terület az indítástól létezik, és soha nem mozdul el.

4.19.5. A MicroPython kupac

A Python objektumok – változók, listák, szótárak, osztálypéldányok, az Image csomagoló, amelyet egy snapshot() hívás visszaad, minden karakterlánc és tuple, amelyet az alkalmazás létrehoz – a MicroPython szemétgyűjtéses kupacán élnek, amely elkülönül a kamera memóriakészleteitől. A szemétgyűjtéses (GC) kupac egy olyan memóriaterület, amelyet a MicroPython maga kezel: a Python kód minden objektum létrehozásakor implicit módon allokál belőle, a MicroPython pedig időnként végigpásztázza a kupacot, és visszaszerzi azon objektumok által elfoglalt helyet, amelyekre az alkalmazás már nem hivatkozik, így az alkalmazásnak soha semmit nem kell kézzel felszabadítania.

Az indításkor egy dedikált terület kerül félretételre a GC kupac számára, amelyet jellemzően a gyors chipen belüli memóriában helyeznek el, hogy a Python elérés gyors maradjon, opcionális túlcsordulással a nagyobb külső blokkba azokon a kártyákon, amelyeknek több mozgástérre van szükségük a nagy adatszerkezetekhez.

Az snapshot() által visszaadott Image egy kis csomagoló objektum a GC kupacon; a mögöttes képpontadatok a képkocka-pufferben élnek, a kamera egyik készletében. A kettő soha nem versenyez ugyanazért a memóriáért.

4.19.6. Összerakva

Az, hogy minden allokációfajtát a megfelelő készletbe terelünk – a nagy puffereket a nagyobb készletbe, ahol elférnek, a késleltetésérzékeny adatokat a gyorsabb készletekbe, a Python kupacot a saját területére, az előnézetet a fenntartott helyére –, az teszi lehetővé, hogy egy teljes felbontású rögzítési folyamatot, egy előnézeti csatornát és egy nem triviális Python szkriptet egymás mellett futtassunk olyan alkatrészeken, amelyeknek összesen csak néhány megabájt gyors memóriájuk van.