מאגרי זיכרון

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

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

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

בזמן ההפעלה של המודל, המסגרת מספקת לדרייבר את המאגרים של אופרנדים הקלט והפלט. בניגוד לקבועים בזמן הקומפילציה שאולי נשלחים בווקטור 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, ומאפשר לדרייבר לבצע פעולות בלי להעתיק נתונים. כך משתפרים הביצועים וצריכת החשמל באפליקציות. לדוגמה, מחסנית HAL של מצלמה יכולה להעביר אובייקטים של AHardwareBuffer ל-NNAPI עבור עומסי עבודה של למידת מכונה באמצעות נקודות אחיזה של AHardwareBuffer שנוצרו על ידי ממשקי API של NDK של מצלמה ו-NDK של מדיה. מידע נוסף זמין במאמר ANeuralNetworksMemory_createFromAHardwareBuffer.

אובייקטים של HardwareBuffer שמשמשים ב-NNAPI מועברים לדרייבר דרך מבנה hidl_memory שנקרא hardware_buffer או hardware_buffer_blob. המבנה hidl_memoryhardware_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. אחרי שמשלבים את כל המידע על המאפיינים, יכול להיות שיהיו מאפיינים או דירוג לא ידועים במאגר. במקרה כזה, המאגר נמצא במצב גמיש שבו המאפיינים קבועים כשמשתמשים בו כקלט למודל, ובמצב דינמי כשמשתמשים בו כפלט למודל. אפשר להשתמש באותו מאגר נתונים זמני עם צורות שונות של פלטים בהרצות שונות, והדרייבר צריך לטפל בשינוי הגודל של מאגר הנתונים הזמני בצורה נכונה.

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

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

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