מאגרי זיכרון

בדף הזה מוסבר על מבני הנתונים והשיטות שמשמשים להעברת מאגרי אופרנדים בין מנהל ההתקן לבין המסגרת בצורה יעילה.

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

  • אם משך החיים הוא CONSTANT_COPY, הערכים נמצאים בשדה operandValues של מבנה המודל. מכיוון שהערכים בווקטור HIDL מועתקים במהלך תקשורת בין תהליכים (IPC), בדרך כלל משתמשים בו רק כדי להכיל כמות קטנה של נתונים, כמו אופרנדים סקלריים (לדוגמה, הסקלר של ההפעלה ב-ADD) ופרמטרים של טנסורים קטנים (לדוגמה, הטנסור של הצורה ב-RESHAPE).
  • אם משך החיים הוא CONSTANT_REFERENCE, הערכים נמצאים בשדה pools של מבנה המודל. במהלך תקשורת בין תהליכים (IPC), רק ה-handles של מאגרי הזיכרון המשותפים משוכפלים, ולא הערכים הגולמיים. לכן, יעיל יותר להשתמש במאגרי זיכרון משותפים כדי להחזיק כמות גדולה של נתונים (לדוגמה, פרמטרים של משקלים בקונבולוציות) מאשר ב-HIDL vectors.

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

סוג הנתונים HIDL‏ hidl_memory משמש גם בהידור וגם בהרצה כדי לייצג מאגר של זיכרון משותף שלא ממופה. הדרייבר צריך למפות את הזיכרון בהתאם כדי שאפשר יהיה להשתמש בו על סמך השם של סוג הנתונים hidl_memory. שמות הזיכרון הנתמכים הם:

  • ashmem: זיכרון משותף ב-Android. פרטים נוספים זמינים במאמר בנושא זיכרון.
  • mmap_fd: זיכרון משותף שמגובה על ידי מתאר קובץ דרך mmap.
  • hardware_buffer_blob: זיכרון משותף שמגובה על ידי AHardwareBuffer בפורמט AHARDWARE_BUFFER_FORMAT_BLOB. זמין מ-Neural Networks (NN) HAL 1.2. מידע נוסף זמין במאמר בנושא AHardwareBuffer.
  • hardware_buffer: זיכרון משותף שמגובה על ידי AHardwareBuffer כללי שלא משתמש בפורמט AHARDWARE_BUFFER_FORMAT_BLOB. מאגר החומרה במצב שאינו BLOB נתמך רק בהרצת מודלים.הוא זמין מ-NN HAL 1.2. מידע נוסף זמין במאמר בנושא AHardwareBuffer.

מגרסה NN HAL 1.3, ‏ NNAPI תומך בדומיינים של זיכרון שמספקים ממשקי הקצאה למאגרי נתונים שמנוהלים על ידי מנהל ההתקן. אפשר להשתמש במאגרי הנתונים הזמניים שמנוהלים על ידי מנהל ההתקן גם כקלט או כפלט של ביצוע. פרטים נוספים זמינים במאמר בנושא תחומי זיכרון.

מנהלי ההתקנים של NNAPI חייבים לתמוך במיפוי של שמות הזיכרון ashmem ו-mmap_fd. מגרסה NN HAL 1.3, מנהלי ההתקנים צריכים לתמוך גם במיפוי של hardware_buffer_blob. התמיכה במצב כללי שאינו BLOB‏ hardware_buffer ובדומיינים של זיכרון היא אופציונלית.

AHardwareBuffer

‫HardwareBuffer הוא סוג של זיכרון משותף שעוטף מאגר Gralloc. ב-Android 10, ‏ Neural Networks API ‏ (NNAPI) תומך בשימוש ב-AHardwareBuffer, ומאפשר לדרייבר לבצע פעולות בלי להעתיק נתונים, מה שמשפר את הביצועים ואת צריכת החשמל של האפליקציות. לדוגמה, מחסנית של Camera HAL יכולה להעביר אובייקטים של AHardwareBuffer ל-NNAPI עבור עומסי עבודה של למידת מכונה באמצעות נקודות אחיזה של AHardwareBuffer שנוצרו על ידי ממשקי ה-API של Camera NDK ו-Media NDK. מידע נוסף זמין במאמר ANeuralNetworksMemory_createFromAHardwareBuffer.

אובייקטים של HardwareBuffer שמשמשים ב-NNAPI מועברים לדרייבר דרך מבנה hidl_memory שנקרא hardware_buffer או hardware_buffer_blob. המבנה hidl_memory hardware_buffer_blob מייצג רק אובייקטים של AHardwareBuffer בפורמט AHARDWAREBUFFER_FORMAT_BLOB.

המידע שנדרש על ידי המסגרת מקודד בשדה hidl_handle של מבנה הנתונים hidl_memory. השדה hidl_handle מכיל את native_handle, שמקודד את כל המטא-נתונים הנדרשים לגבי AHardwareBuffer או Gralloc buffer.

מנהל ההתקן צריך לפענח בצורה נכונה את השדה hidl_handle ולגשת לזיכרון שמתואר על ידי hidl_handle. כשקוראים לשיטה getSupportedOperations_1_2, getSupportedOperations_1_1 או getSupportedOperations, מנהל ההתקן צריך לזהות אם הוא יכול לפענח את hidl_handle שסופק ולגשת לזיכרון שמתואר על ידי hidl_handle. הכנת המודל צריכה להיכשל אם השדה hidl_handle שמשמש כאופרנד קבוע לא נתמך. הביצוע צריך להיכשל אם השדה hidl_handle שמשמש כאופרנד קלט או פלט של הביצוע לא נתמך. מומלץ שהדרייבר יחזיר קוד שגיאה GENERAL_FAILURE אם ההכנה או ההפעלה של המודל נכשלות.

דומיינים של זיכרונות

במכשירים עם Android מגרסה 11 ואילך, ‏ NNAPI תומך בדומיינים של זיכרון שמספקים ממשקי הקצאה למאגרי נתונים זמניים שמנוהלים על ידי מנהל ההתקן. ההגדרה הזו מאפשרת להעביר זיכרונות מקוריים של מכשירים בין הפעלות, לדכא העתקה והמרה של נתונים מיותרים בין הפעלות עוקבות באותו מנהל התקן. התהליך הזה מוסבר באיור 1.

הוספת מאגר זמני לזרימת נתונים עם או בלי דומיינים של זיכרון

איור 1. שימוש בדומיינים של זיכרון כדי ליצור מאגר זמני לנתונים

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

כדי לתמוך בתכונה של דומיין הזיכרון, צריך להטמיע את IDevice::allocate כדי לאפשר למסגרת לבקש הקצאת מאגר בניהול מנהל ההתקן. במהלך ההקצאה, המסגרת מספקת את המאפיינים הבאים ואת דפוסי השימוש הבאים עבור המאגר:

  • BufferDesc מתאר את המאפיינים הנדרשים של המאגר.
  • BufferRole מתאר את דפוס השימוש הפוטנציאלי של המאגר כקלט או כפלט של מודל מוכן. אפשר לציין כמה תפקידים במהלך הקצאת המאגר, ואפשר להשתמש במאגר שהוקצה רק בתפקידים שצוינו.

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

הטוקן מ-IDevice::allocate מסופק כשמפנים אל המאגר כאחד מאובייקטי MemoryPool במבנה Request של ביצוע. כדי למנוע מתהליך מסוים לנסות לגשת למאגר שהוקצה בתהליך אחר, מנהל ההתקן צריך להחיל אימות מתאים בכל שימוש במאגר. הדרייבר צריך לוודא שהשימוש במאגר הוא אחד מBufferRole התפקידים שסופקו במהלך ההקצאה, ואם השימוש לא חוקי, הוא צריך להפסיק את ההרצה באופן מיידי.

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

  • אתחול טנסור המצב
  • שמירת תוצאות ביניים במטמון
  • ביצוע חזרה למצב ראשוני במעבד

כדי לתמוך בתרחישי השימוש האלה, מנהל ההתקן צריך להטמיע את IBuffer::copyTo ואת IBuffer::copyFrom עם ashmem,‏ mmap_fd ו-hardware_buffer_blob אם הוא תומך בהקצאת זיכרון לדומיין. התמיכה במצב שאינו BLOB היא אופציונלית עבור מנהל ההתקן hardware_buffer.

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

דומיין זיכרון הוא תכונה אופציונלית. יכולות להיות כמה סיבות לכך שנהג יקבע שהוא לא יכול לתמוך בבקשת הקצאה מסוימת. לדוגמה:

  • גודל המאגר המבוקש הוא דינמי.
  • לדרייבר יש מגבלות זיכרון שמונעות ממנו לטפל במאגרי נתונים גדולים.

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