MicroPython sztring-internálás¶
A MicroPython string interning (sztring-internálás) technikát használ a RAM és a ROM megtakarítása érdekében. Ezzel elkerülhető, hogy ugyanazon sztring duplikált példányait kelljen tárolni. Ez elsősorban a kódban szereplő azonosítókra vonatkozik, mivel egy függvény- vagy változónévhez hasonló dolog nagyon valószínűen több helyen is megjelenik a kódban. A MicroPythonban az internált sztringet QSTR-nek (uniQue STRing) nevezik.
Egy QSTR érték (qstr típussal) egy index a QSTR-poolok láncolt listájában. A QSTR-ek tárolják a hosszukat és a tartalmuk hash-ét a gyors összehasonlítás érdekében a deduplikációs folyamat során. Minden, sztringekkel dolgozó bájtkód-művelet QSTR argumentumot használ.
Fordítási idejű QSTR-generálás¶
A MicroPython C kódjában minden olyan sztring, amelyet a végleges firmware-ben internálni kell, MP_QSTR_Foo formában van leírva. Fordítási időben ez egy qstr értékké értékelődik ki, amely a "Foo" indexére mutat a QSTR-poolban.
Egy többlépcsős folyamat a Makefile-ban gondoskodik erről. Összefoglalva, ez a folyamat három részből áll:
Az összes
MP_QSTR_Footoken megkeresése a kódban.Egy statikus QSTR-pool generálása, amely tartalmazza az összes sztringadatot (beleértve a hosszokat és hash-eket).
Az összes
MP_QSTR_Foolecserélése (az előfeldolgozón keresztül) a hozzájuk tartozó indexre.
Az MP_QSTR_Foo tokeneket két forrásban keresi:
Az összes, a
$(SRC_QSTR)-ben hivatkozott fájl. Ez az összes C kód (azazpy,extmod,ports/stm32), de nem tartalmazza a harmadik féltől származó kódot, mint például alib.További
$(QSTR_GLOBAL_DEPENDENCIES)(amely tartalmazza azmpconfig*.h-t).
Megjegyzés: a frozen_mpy.c (amelyet az mpy-tool.py generál) saját QSTR-generálással és pool-lal rendelkezik.
Néhány további sztring, amely nem fejezhető ki az MP_QSTR_Foo szintaxissal (pl. nem alfanumerikus karaktereket tartalmaz), explicit módon a qstrdefs.h és qstrdefsport.h fájlokban van megadva a $(QSTR_DEFS) változón keresztül.
A feldolgozás a következő szakaszokban történik:
A
qstr.i.lastaz összes bemeneti fájl C előfeldolgozón való átfuttatásának összefűzése. Ez azt jelenti, hogy minden feltételesen letiltott kód eltávolításra kerül, és a makrók kifejtődnek. Ez azt jelenti, hogy nem adunk hozzá a poolhoz olyan sztringeket, amelyek nem lesznek használva a végleges firmware-ben. Mivel ebben a szakaszban (aQSTR_GEN_CFLAGSáltal hozzáadottNO_QSTRmakrónak köszönhetően) nincs definíció azMP_QSTR_Foo-ra, az érintetlenül halad át ezen a szakaszon. Ez a fájl tartalmazza az előfeldolgozótól származó megjegyzéseket is, amelyek sorszám-információkat tartalmaznak. Vegye figyelembe, hogy ez a lépés csak a megváltozott fájlokat használja, ami azt jelenti, hogy aqstr.i.lastcsak az utolsó fordítás óta megváltozott fájlokból tartalmaz adatokat.A
qstr.splitegy üres fájl, amelyet amakeqstrdefs.py splitqstr.i.last-on való futtatása után hoz létre. Csupán függőségként szolgál annak jelzésére, hogy a lépés lefutott. Ez a szkript bemeneti C fájlonként egy fájlt ad ki,genhdr/qstr/...file.c.qstrnéven, amely csak az illeszkedő QSTR-eket tartalmazza. Minden QSTRQ(Foo)formában kerül kiírásra. Erre a lépésre a meglévő fájlok és aqstr.i.last-ben lévő inkrementális frissítésből generált új adatok kombinálásához van szükség.A
qstrdefs.collected.hagenhdr/qstr/*fájlokmakeqstrdefs.py cat-tel való összefűzésének kimenete. Ez most a kódban találtMP_QSTR_Foo-k teljes halmaza, immárQ(Foo)formátumban, soronként egy, duplikátumokkal. Ez a fájl csak akkor frissül, ha a qstr-ek halmaza megváltozott. A QSTR-adatok hash-e egy másik fájlba (qstrdefs.collected.h.hash) kerül kiírásra, amely lehetővé teszi a változások nyomon követését a buildek között.Egy felsorolás (enum) generálása, amelynek minden bejegyzése egy
MP_QSTR_Foo-t leképez a hozzá tartozó indexre. Összefűzi aqstrdefs.collected.h-t aqstrdefs*.h-val, majd minden sortQ(Foo)-ból"Q(Foo)"-ra alakít, hogy változatlanul áthaladjanak az előfeldolgozón. Ezután az előfeldolgozóval kezeli aqstrdefs*.h-ban lévő feltételes fordítást. Majd az átalakítást visszafordítjaQ(Foo)-ra, ésqstrdefs.preprocessed.hnéven menti.A
qstrdefs.generated.hamakeqstrdata.pykimenete. A qstrdefs.preprocessed.h-ban lévő mindenQ(Foo)-ra (plusz néhány extra, kódban rögzítettre) kiad egyQDEF(MP_QSTR_Foo, (const byte*)"hash" "Foo")-t.
Ezután a fő fordítás során két dolog történik a qstrdefs.generated.h-val:
A qstr.h-ban minden QDEF egy enum-bejegyzéssé válik, ami az
MP_QSTR_Foo-t elérhetővé teszi a kód számára, és egyenlővé teszi az adott sztring QSTR-táblában lévő indexével.A qstr.c-ben a tényleges QSTR-adattábla az
mp_qstr_const_pool->qstrselemeiként generálódik.
Futásidejű QSTR-generálás¶
Futásidőben további QSTR-poolok hozhatók létre, hogy sztringeket lehessen hozzájuk adni. Például a következő kód:
foo[x] = 3
QSTR-t kell létrehoznia az x értékéhez, hogy a „load attr” bájtkód használni tudja.
Továbbá Python kód fordításakor az azonosítókhoz és literálokhoz QSTR-eket kell létrehozni. Megjegyzés: csak a 10 karakternél rövidebb literálok válnak QSTR-ekké. Ennek oka, hogy egy szokásos sztring a heap-en mindig legalább 16 bájtot foglal el (egy GC-blokk), míg a QSTR-ek lehetővé teszik a hatékonyabb betömörítésüket a poolba.
A QSTR-poolok (és az alapjául szolgáló „chunk”-ok, amelyek a sztringadatokat tárolják) igény szerint, minimális mérettel kerülnek lefoglalásra a heap-en.