Neural Networks HAL 1.2 מציג את הרעיון של ביצוע פרץ להורג. ביצוע פרץ הוא רצף של ביצועים מאותו דגם מוכן המתרחשות ברצף מהיר, כגון אלו הפועלות על פריימים של לכידת מצלמה או דגימות אודיו עוקבות. אובייקט פרץ משמש כדי לשלוט על קבוצה של ביצוע פרץ, ולשמור משאבים בין ביצועים להורג, מה שמאפשר לביצוע תקורה נמוכה יותר. אובייקטים מתפרצים מאפשרים שלוש אופטימיזציות:
- אובייקט פרץ נוצר לפני רצף של ביצועים, ומשוחרר כשהרצף הסתיים. בשל כך, משך החיים של האובייקט המתפרץ מרמז לנהג כמה זמן עליו להישאר במצב בעל ביצועים גבוהים.
- אובייקט מתפרץ יכול לשמר משאבים בין ביצועים להורג. לדוגמה, מנהל התקן יכול למפות אובייקט זיכרון בביצוע הראשון ולשמור את המיפוי באובייקט הפרץ לשימוש חוזר בביצועים הבאים. ניתן לשחרר כל משאב שמור במטמון כאשר אובייקט ה-Burst מושמד או כאשר זמן הריצה של NNAPI מודיע לאובייקט ה-Burst שהמשאב אינו נדרש עוד.
- אובייקט מתפרץ משתמש בתורי הודעות מהירים (FMQs) כדי לתקשר בין תהליכי האפליקציה והנהגים. זה יכול להפחית את השהיה מכיוון שה-FMQ עוקף את HIDL ומעביר נתונים ישירות לתהליך אחר דרך FIFO מעגלי אטומי בזיכרון משותף. תהליך הצרכן יודע להעמיד פריט בתור ולהתחיל בעיבוד או על ידי סקר מספר האלמנטים ב-FIFO או על ידי המתנה לדגל האירוע של ה-FMQ, אשר מסומן על ידי המפיק. דגל האירוע הזה הוא mutex מהיר של מרחב משתמש (futex).
FMQ הוא מבנה נתונים ברמה נמוכה שאינו מציע ערבויות לכל החיים על פני תהליכים ואין לו מנגנון מובנה לקביעה אם התהליך בקצה השני של ה-FMQ פועל כמצופה. כתוצאה מכך, אם המפיק של ה-FMQ מת, הצרכן עלול להיתקע בהמתנה לנתונים שלעולם לא יגיעו. אחד הפתרונות לבעיה זו הוא שמנהל ההתקן ישיך FMQs לאובייקט ה-Burst ברמה גבוהה יותר כדי לזהות מתי ביצוע ה-Burst הסתיים.
מכיוון שביצועי פרץ פועלים על אותם ארגומנטים ומחזירים את אותן תוצאות כמו נתיבי ביצוע אחרים, ה-FMQs הבסיסיים חייבים להעביר את אותם נתונים אל מנהלי ההתקן של שירות NNAPI וממנו. עם זאת, FMQs יכולים להעביר רק סוגי נתונים רגילים-ישנים. העברת נתונים מורכבים מתבצעת על ידי הסדרת וסידריאליזציה של מאגרים מקוננים (סוגי וקטור) ישירות ב-FMQs, ושימוש באובייקטי התקשרות HIDL להעברת נקודות אחיזה של מאגר זיכרון לפי דרישה. צד המפיק של ה-FMQ חייב לשלוח את הודעות הבקשה או התוצאה לצרכן בצורה אטומית באמצעות MessageQueue::writeBlocking
אם התור חוסם, או באמצעות MessageQueue::write
אם התור אינו חוסם.
ממשקי פרץ
ממשקי התפרצות עבור ה-Neural Networks HAL נמצאים hardware/interfaces/neuralnetworks/1.2/
ומתוארים להלן. למידע נוסף על ממשקי פרץ בשכבת NDK, ראה frameworks/ml/nn/runtime/include/NeuralNetworks.h
.
types.hal
types.hal
מגדיר את סוג הנתונים שנשלחים על פני ה-FMQ.
-
FmqRequestDatum
: אלמנט בודד של ייצוג סדרתי של אובייקטRequest
לביצוע וערךMeasureTiming
, שנשלח על פני תור ההודעות המהיר. -
FmqResultDatum
: אלמנט בודד של ייצוג סדרתי של הערכים המוחזרים מביצוע (ErrorStatus
,OutputShapes
Timing
), המוחזר דרך תור ההודעות המהיר.
IBurstContext.hal
IBurstContext.hal
מגדיר את אובייקט ממשק HIDL שחי בשירות Neural Networks.
-
IBurstContext
: אובייקט הקשר לניהול המשאבים של פרץ.
IBurstCallback.hal
IBurstCallback.hal
מגדיר את אובייקט ממשק HIDL עבור התקשרות חוזרת שנוצרה על ידי זמן הריצה של Neural Networks ומשמש את שירות Neural Networks כדי לאחזר אובייקטי hidl_memory
התואמים למזהי חריץ.
- IBurstCallback : אובייקט Callback המשמש שירות לאחזור אובייקטי זיכרון.
IPreparedModel.hal
IPreparedModel.hal
מורחב ב-HAL 1.2 עם שיטה ליצירת אובייקט IBurstContext
ממודל מוכן.
-
configureExecutionBurst
: מגדיר אובייקט פרץ המשמש לביצוע מסקנות מרובות על מודל מוכן ברצף מהיר.
תמיכה בהוצאה להורג מתפרצת במנהל ההתקן
הדרך הפשוטה ביותר לתמוך באובייקטים מתפרצים בשירות HIDL NNAPI היא להשתמש בפונקציית השירות burst ::android::nn::ExecutionBurstServer::create
, שנמצאת ב- ExecutionBurstServer.h
וארוזה ב- libneuralnetworks_common
ו- libneuralnetworks_util
static. לפונקציית היצרן הזו יש שני עומסים:
- עומס יתר אחד מקבל מצביע לאובייקט
IPreparedModel
. פונקציית עזר זו משתמשת בשיטתexecuteSynchronously
באובייקטIPreparedModel
כדי להפעיל את המודל. - עומס יתר אחד מקבל אובייקט
IBurstExecutorWithCache
הניתן להתאמה אישית, שניתן להשתמש בו כדי לשמור משאבים במטמון (כגון מיפויhidl_memory
) הנמשכים לאורך מספר ביצועים.
כל עומס יתר מחזיר אובייקט IBurstContext
(המייצג את אובייקט ה-burst) המכיל ומנהל פתיל מאזין ייעודי משלו. השרשור הזה מקבל בקשות מה- requestChannel
FMQ, מבצע את ההסקה, ואז מחזיר את התוצאות דרך ה- resultChannel
FMQ. שרשור זה וכל שאר המשאבים הכלולים באובייקט IBurstContext
משוחררים אוטומטית כאשר הלקוח של הפרץ מאבד את ההתייחסות שלו ל- IBurstContext
.
לחלופין, אתה יכול ליצור יישום משלך של IBurstContext
שמבין כיצד לשלוח ולקבל הודעות על פני requestChannel
ו- resultChannel
FMQs שהועברו ל- IPreparedModel::configureExecutionBurst
.
פונקציות השירות Burst נמצאות ב- ExecutionBurstServer.h
.
/**
* Create automated context to manage FMQ-based executions.
*
* This function is intended to be used by a service to automatically:
* 1) Receive data from a provided FMQ
* 2) Execute a model with the given information
* 3) Send the result to the created FMQ
*
* @param callback Callback used to retrieve memories corresponding to
* unrecognized slots.
* @param requestChannel Input FMQ channel through which the client passes the
* request to the service.
* @param resultChannel Output FMQ channel from which the client can retrieve
* the result of the execution.
* @param executorWithCache Object which maintains a local cache of the
* memory pools and executes using the cached memory pools.
* @result IBurstContext Handle to the burst context.
*/
static sp<ExecutionBurstServer> create(
const sp<IBurstCallback>& callback, const FmqRequestDescriptor& requestChannel,
const FmqResultDescriptor& resultChannel,
std::shared_ptr<IBurstExecutorWithCache> executorWithCache);
/**
* Create automated context to manage FMQ-based executions.
*
* This function is intended to be used by a service to automatically:
* 1) Receive data from a provided FMQ
* 2) Execute a model with the given information
* 3) Send the result to the created FMQ
*
* @param callback Callback used to retrieve memories corresponding to
* unrecognized slots.
* @param requestChannel Input FMQ channel through which the client passes the
* request to the service.
* @param resultChannel Output FMQ channel from which the client can retrieve
* the result of the execution.
* @param preparedModel PreparedModel that the burst object was created from.
* IPreparedModel::executeSynchronously will be used to perform the
* execution.
* @result IBurstContext Handle to the burst context.
*/
static sp<ExecutionBurstServer> create(const sp<IBurstCallback>& callback,
const FmqRequestDescriptor& requestChannel,
const FmqResultDescriptor& resultChannel,
IPreparedModel* preparedModel);
להלן יישום ייחוס של ממשק פרץ שנמצא במנהל ההתקן לדוגמה של Neural Networks ב- frameworks/ml/nn/driver/sample/SampleDriver.cpp
.
Return<void> SamplePreparedModel::configureExecutionBurst(
const sp<V1_2::IBurstCallback>& callback,
const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
configureExecutionBurst_cb cb) {
NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
"SampleDriver::configureExecutionBurst");
// Alternatively, the burst could be configured via:
// const sp<V1_2::IBurstContext> burst =
// ExecutionBurstServer::create(callback, requestChannel,
// resultChannel, this);
//
// However, this alternative representation does not include a memory map
// caching optimization, and adds overhead.
const std::shared_ptr<BurstExecutorWithCache> executorWithCache =
std::make_shared<BurstExecutorWithCache>(mModel, mDriver, mPoolInfos);
const sp<V1_2::IBurstContext> burst = ExecutionBurstServer::create(
callback, requestChannel, resultChannel, executorWithCache);
if (burst == nullptr) {
cb(ErrorStatus::GENERAL_FAILURE, {});
} else {
cb(ErrorStatus::NONE, burst);
}
return Void();
}