4.19. מאגרי זיכרון

מצלמה שמחזיקה שלושה פריימים ברזולוציה מלאה במאגר של חוצץ הפריימים (frame buffer), מריצה לצדם חוצץ תצוגה מקדימה נפרד, ועדיין נותר בה מקום לסקריפט Python ולאובייקטים שלו, מנהלת יותר זיכרון ממה שבלוק יחיד של RAM ב-MCU יכול לספק. MicroPython מצליחה להכניס את הכול על ידי פיזורו על פני כמה סוגים נבדלים של זיכרון שה-MCU מספק, ועל ידי ניתוב כל סוג הקצאה אל סוג הזיכרון שהוא באמת זקוק לו.

4.19.1. סוגי זיכרון

MCU מודרני של OpenMV Cam חושף ארבעה סוגים נבדלים של זיכרון. הראשון אינו נראה לאפליקציה; שלושת האחרים הם מאגרים שהקצאות יכולות לבוא מהם.

  • מטמון הנתונים של ה-CPU – אזור זיכרון קטן ומהיר מאוד שיושב בין ה-CPU לבין שאר ה-RAM. כאשר ה-CPU קורא או כותב ערך מהזיכרון הראשי, המטמון שומר אוטומטית עותק, כך שגישות חוזרות לאותם נתונים נשארות במטמון ולעולם אינן משלמות את העלות של יציאה אל זיכרון איטי יותר. המטמון אינו מאגר שהקצאות באות ממנו. הוא שקוף לאפליקציה – הוא פשוט גורם לשאר ה-RAM להרגיש מהיר יותר בפועל ממה שההשהיה הגולמית שלו הייתה מרמזת, עד לנקודה שבה קבוצת העבודה מפסיקה להיכנס אליו.

  • זיכרון מעבד צמוד (tightly-coupled) – בלוק קטן של RAM המחווט ישירות ל-CPU ללא אפיק ביניהם. גישה של מחזור יחיד, אף פעם לא מחטיא, אף פעם לא ממתין. הקצאות שזקוקות באמת לזיכרון המהיר ביותר האפשרי – שבהן כל מחזור של השהיה חשוב – באות ממאגר זה.

  • זיכרון מהיר על השבב (on-chip) – כמה מאות קילובייטים ועד כמגה-בייט של RAM, מובנה בתוך מארז ה-MCU. השהיה נמוכה, רוחב פס גבוה, אך מוגבל בגודלו. ערמת ה-MicroPython חיה כאן כדי שגישות לאובייקטים של Python יישארו מהירות; חוצצי עבודה קטנים יותר שה-CPU נוגע בהם הרבה חולקים את המאגר.

  • זיכרון בכמויות גדולות ואיטי יותר – בלוחות שמשלבים את ה-MCU עם דאי זיכרון חיצוני, עשרות מגה-בייטים של RAM מחוץ לשבב הנגישים דרך האפיק החיצוני. גדול בהרבה, אך כל גישה אורכת זמן רב יותר מאשר בזיכרון שעל השבב; מטמון הנתונים מסתיר חלק גדול מעלות זו עבור קבוצות עבודה שהוא יכול להחזיק, והפער מתגלה בפעולות שסורקות נתונים גדולים מכדי להיכנס למטמון. משמש להקצאות שחייבות להיות גדולות ושה-CPU יכול לסבול במהירות נמוכה יותר – ובראש ובראשונה, מאגר חוצץ הפריימים (frame buffer).

הלוחות במשפחה נמצאים על רצף: לחלקם יש רק RAM על השבב; חלקם משלבים RAM על השבב עם בלוק חיצוני גדול בהרבה. כל אחד משלושת הסוגים הניתנים להקצאה מטופל כמאגר זיכרון – נתח שהקצאות באות ממנו – ומתויג כך שכל בקשה יכולה לבקש את סוג הזיכרון שהיא באמת זקוקה לו.

4.19.2. חוצץ הפריימים הראשי

חוצץ הפריימים (frame buffer) שמגבה את snapshot() אינו מבקש זיכרון מהיר. הוא מבקש מספיק זיכרון – לא יותר. הדבר ממקם אותו במאגר הגדול ביותר, כך שבלוח עם זיכרון על השבב וגם חיצוני חוצץ הפריימים נוחת בבלוק החיצוני.

חוצץ פריימים ברזולוציה מלאה ובחציצה משולשת גדול בהרבה מכדי להיכנס למאגר המהיר שעל השבב ברוב הרכיבים; המאגר הגדול יותר הוא היחיד שמסוגל בכלל להחזיק אותו. מטמון הנתונים של ה-CPU מסתיר חלק גדול מהעלות לכל גישה כאשר האפליקציה מעבדת את התמונה, ומנוע ה-DMA שממלא את חוצץ הפריימים מהחיישן עומד בקצב הנתונים של החיישן בכל מקרה.

הגודל המדויק שחוצץ הפריימים תופס נבחר מתוך pixformat(), framesize() ומניין framebuffers() הנוכחיים; הוא גדל או קטן בכל פעם שאחד מאלה משתנה.

4.19.3. חוצצי פריימים משניים של חיישנים

מופע CSI שני מקבל חוצץ פריימים משלו, המוקצה מאותו מאגר שבו משתמש הראשי. המאגר משותף; החוצצים עצמאיים. טביעת הרגל של המשני בדרך כלל קטנה בהרבה מזו של הראשי, מכיוון שחיישנים משניים פועלים ברזולוציות נמוכות יותר, כך שהזיכרון הנוסף שחוצץ הפריימים השני תופס הוא חלק קטן מזה של הראשי.

4.19.4. חוצץ פריימי הזרם

חוצץ התצוגה המקדימה של התמונה הוא היוצא מן הכלל. הוא אינו מוקצה מאף אחד מהמאגרים בזמן ריצה; הוא אזור קבוע השמור בזמן הבנייה, עם כתובת ידועה וגודל ידוע. הדבר שומר על נתיב התצוגה המקדימה מחוץ לדרכה של כל הקצאה אחרת – האזור קיים מרגע האתחול ולעולם אינו זז.

4.19.5. ערמת ה-MicroPython

אובייקטים של Python – משתנים, רשימות, מילונים, מופעי מחלקות, עוטף ה-Image שמחזירה קריאת snapshot(), כל מחרוזת וכל tuple שהאפליקציה יוצרת – חיים על ערמת ה-MicroPython עם איסוף האשפה (garbage-collected), שהיא נפרדת ממאגרי הזיכרון של המצלמה. ערמת איסוף האשפה (GC) היא אזור זיכרון ש-MicroPython מנהלת בעצמה: קוד Python מקצה ממנה באופן מובלע בכל פעם שאובייקט נוצר, ו-MicroPython סורקת מדי פעם את הערמה ומשחררת את המקום שתופסים אובייקטים שהאפליקציה כבר אינה מפנה אליהם, כך שהאפליקציה לעולם אינה צריכה לשחרר דבר ידנית.

אזור ייעודי מופרש עבור ערמת ה-GC בזמן האתחול, ממוקם בדרך כלל בזיכרון מהיר שעל השבב כדי שגישת Python תישאר מהירה, עם גלישה אופציונלית אל הבלוק החיצוני הגדול יותר בלוחות שזקוקים ליותר מרחב נשימה למבני נתונים גדולים.

ה-Image שמחזירה snapshot() הוא אובייקט עוטף קטן על ערמת ה-GC; נתוני הפיקסלים הבסיסיים חיים בחוצץ הפריימים באחד ממאגרי המצלמה. השניים לעולם אינם מתחרים על אותו זיכרון.

4.19.6. מחברים הכול יחד

ניתוב כל סוג הקצאה אל המאגר הנכון – חוצצים גדולים אל המאגר הגדול יותר שבו הם נכנסים, נתונים רגישי-השהיה אל המאגרים המהירים יותר, ערמת ה-Python אל האזור שלה, התצוגה המקדימה אל החריץ השמור שלה – הוא מה שמאפשר להריץ צינור לכידה ברזולוציה מלאה, ערוץ תצוגה מקדימה וסקריפט Python לא-טריוויאלי זה לצד זה על רכיבים שיש להם רק כמה מגה-בייטים של זיכרון מהיר בסך הכול.