एक साथ कई टास्क पूरा करना और मैसेज की तेज़ी से चलने वाली कतारें

न्यूरल नेटवर्क HAL 1.2 में, एक साथ कई निर्देशों को लागू करने की सुविधा का कॉन्सेप्ट शामिल किया गया है. एक ही मॉडल को बार-बार चलाने की सुविधा, एक ही मॉडल को एक के बाद एक चलाने की सुविधा है. जैसे, कैमरे से कैप्चर किए गए फ़्रेम या एक के बाद एक ऑडियो सैंपल पर काम करने वाले मॉडल. बर्स्ट ऑब्जेक्ट का इस्तेमाल, बर्स्ट रन के सेट को कंट्रोल करने के लिए किया जाता है. साथ ही, रन के बीच संसाधनों को बनाए रखने के लिए भी इसका इस्तेमाल किया जाता है. इससे रन के लिए कम ओवरहेड की ज़रूरत होती है. बर्स्ट ऑब्जेक्ट की मदद से, तीन ऑप्टिमाइज़ेशन चालू किए जा सकते हैं:

  1. बर्स्ट ऑब्जेक्ट, एक से ज़्यादा बार चलने वाले फ़ंक्शन के क्रम से पहले बनाया जाता है और क्रम खत्म होने पर उसे रिलीज़ कर दिया जाता है. इसलिए, बर्स्ट ऑब्जेक्ट का लाइफ़टाइम, ड्राइवर को यह बताता है कि उसे कितनी देर तक बेहतर परफ़ॉर्मेंस वाली स्थिति में रहना चाहिए.
  2. बर्स्ट ऑब्जेक्ट, एक के बाद एक कई बार लागू होने के दौरान रिसॉर्स को सेव रख सकता है. उदाहरण के लिए, कोई ड्राइवर पहले एक्सीक्यूशन पर किसी मेमोरी ऑब्जेक्ट को मैप कर सकता है और बाद के एक्सीक्यूशन में फिर से इस्तेमाल करने के लिए, बर्स्ट ऑब्जेक्ट में मैपिंग को कैश मेमोरी में सेव कर सकता है. कैश मेमोरी में सेव किया गया कोई भी संसाधन, बर्स्ट ऑब्जेक्ट के मिटने या NNAPI रनटाइम के बर्स्ट ऑब्जेक्ट को यह सूचना देने पर रिलीज़ किया जा सकता है कि संसाधन की अब ज़रूरत नहीं है.
  3. बर्स्ट ऑब्जेक्ट, ऐप्लिकेशन और ड्राइवर प्रोसेस के बीच कम्यूनिकेट करने के लिए, फ़ास्ट मैसेज क्यू (एफ़एमक्यू) का इस्तेमाल करता है. इससे, इंतज़ार का समय कम हो सकता है, क्योंकि एफ़एमक्यू, एचआईडीएल को बायपास करता है और शेयर की गई मेमोरी में मौजूद एटॉमिक सर्कुलर एफ़आईओ की मदद से, डेटा को सीधे दूसरी प्रोसेस को भेजता है. फ़्लो कंट्रोल के लिए, फ़्लो में मौजूद आइटम को हटाने और प्रोसेस शुरू करने का तरीका, कंज़्यूमर प्रोसेस को पता होता है. यह तरीका, FIFO में मौजूद एलिमेंट की संख्या को पोल करके या FMQ के इवेंट फ़्लैग का इंतज़ार करके शुरू किया जाता है. इवेंट फ़्लैग को प्रोड्यूसर से सिग्नल मिलता है. यह इवेंट फ़्लैग, फ़ास्ट यूज़रस्पेस म्यूटेक्स (futex) है.

एफ़एमक्यू एक लो-लेवल डेटा स्ट्रक्चर है, जो सभी प्रोसेस के लिए लाइफ़टाइम की कोई गारंटी नहीं देता. साथ ही, इसमें यह तय करने के लिए कोई बिल्ट-इन मेकेनिज्म नहीं है कि एफ़एमक्यू के दूसरे छोर पर प्रोसेस उम्मीद के मुताबिक चल रही है या नहीं. इसलिए, अगर एफ़एमक्यू के लिए प्रोड्यूसर की प्रोसेस बंद हो जाती है, तो हो सकता है कि डेटा पाने के लिए उपभोक्ता को इंतज़ार करना पड़े. इस समस्या को हल करने का एक तरीका यह है कि ड्राइवर, एफएमक्यू को हाई-लेवल बर्स्ट ऑब्जेक्ट से जोड़ें, ताकि बर्स्ट एक्सीक्यूशन खत्म होने का पता लगाया जा सके.

बर्स्ट एक्सीक्यूशन एक ही आर्ग्युमेंट पर काम करते हैं और अन्य एक्सीक्यूशन पाथ के जैसे ही नतीजे दिखाते हैं. इसलिए, फ़ंडामेंटल एमकेयू को NNAPI सेवा ड्राइवर को वही डेटा पास करना चाहिए और उनसे वही डेटा पाना चाहिए. हालांकि, एफ़एमक्यू सिर्फ़ सामान्य डेटा टाइप ट्रांसफ़र कर सकते हैं. जटिल डेटा को सीधे एफ़एमक्यू में नेस्ट किए गए बफ़र (वेक्टर टाइप) को सीरियलाइज़ और डी-सीरियलाइज़ करके ट्रांसफ़र किया जाता है. साथ ही, मांग पर मेमोरी पूल हैंडल ट्रांसफ़र करने के लिए, एचआईडीएल कॉलबैक ऑब्जेक्ट का इस्तेमाल किया जाता है. अगर कतार ब्लॉक हो रही है, तो एफ़एमक्यू के प्रोड्यूसर साइड को MessageQueue::writeBlocking का इस्तेमाल करके, अनुरोध या नतीजे के मैसेज को उपभोक्ता को एक-एक करके भेजना चाहिए. अगर कतार ब्लॉक नहीं हो रही है, तो MessageQueue::write का इस्तेमाल करके ऐसा किया जाना चाहिए.

बर्स्ट मोड के इंटरफ़ेस

न्यूरल नेटवर्क एचएएल के लिए बर्स्ट इंटरफ़ेस, hardware/interfaces/neuralnetworks/1.2/ में मिलते हैं. इनके बारे में यहां बताया गया है. NDK लेयर में बर्स्ट इंटरफ़ेस के बारे में ज़्यादा जानकारी के लिए, frameworks/ml/nn/runtime/include/NeuralNetworks.h देखें.

types.hal

types.hal इससे यह तय होता है कि एफ़एमक्यू पर किस तरह का डेटा भेजा जाए.

  • FmqRequestDatum: एक्सीक्यूशन Request ऑब्जेक्ट और MeasureTiming वैल्यू के क्रम में लगाए गए एलिमेंट का एक एलिमेंट, जिसे फ़ास्ट मैसेज कतार में भेजा जाता है.
  • FmqResultDatum: ErrorStatus, OutputShapes, और Timing जैसे एक्सीक्यूशन से मिली वैल्यू को क्रम से लगाकर दिखाने वाला एक एलिमेंट. यह वैल्यू, फ़ास्ट मैसेज क्यू के ज़रिए दिखती है.

IBurstContext.hal

IBurstContext.hal Neural Networks सेवा में मौजूद HIDL इंटरफ़ेस ऑब्जेक्ट के बारे में बताता है.

  • IBurstContext: बर्स्ट के संसाधनों को मैनेज करने के लिए कॉन्टेक्स्ट ऑब्जेक्ट.

IBurstCallback.hal

IBurstCallback.hal, Neural Networks के रनटाइम से बनाए गए कॉलबैक के लिए, HIDL इंटरफ़ेस ऑब्जेक्ट तय करता है. साथ ही, Neural Networks सेवा इसका इस्तेमाल, स्लॉट आइडेंटिफ़ायर से जुड़े hidl_memoryऑब्जेक्ट को वापस पाने के लिए करती है.

  • IBurstCallback: मेमोरी ऑब्जेक्ट वापस पाने के लिए, सेवा का इस्तेमाल करने वाला कॉलबैक ऑब्जेक्ट.

IPreparedModel.hal

IPreparedModel.hal को HAL 1.2 में, पहले से तैयार किए गए मॉडल से IBurstContext ऑब्जेक्ट बनाने के तरीके के साथ जोड़ा गया है.

  • configureExecutionBurst: बर्स्ट ऑब्जेक्ट को कॉन्फ़िगर करता है. इसका इस्तेमाल, पहले से तैयार किए गए मॉडल पर एक के बाद एक कई अनुमान लगाने के लिए किया जाता है.

ड्राइवर में एक साथ कई निर्देशों को लागू करने की सुविधा

HIDL NNAPI सेवा में बर्स्ट ऑब्जेक्ट का इस्तेमाल करने का सबसे आसान तरीका, बर्स्ट यूटिलिटी फ़ंक्शन ::android::nn::ExecutionBurstServer::create का इस्तेमाल करना है. यह ExecutionBurstServer.h में मिलता है और libneuralnetworks_common और libneuralnetworks_util स्टैटिक लाइब्रेरी में पैकेज किया जाता है. इस फ़ैक्ट्री फ़ंक्शन के दो ओवरलोड हैं:

  • एक ओवरलोड, IPreparedModel ऑब्जेक्ट का पॉइंटर स्वीकार करता है. यह यूटिलिटी फ़ंक्शन, मॉडल को लागू करने के लिए IPreparedModel ऑब्जेक्ट में executeSynchronously तरीके का इस्तेमाल करता है.
  • एक ओवरलोड, पसंद के मुताबिक बनाए जा सकने वाले IBurstExecutorWithCache ऑब्जेक्ट को स्वीकार करता है. इसका इस्तेमाल, hidl_memory मैपिंग जैसे रिसॉर्स को कैश मेमोरी में सेव करने के लिए किया जा सकता है. ये रिसॉर्स, कई बार चलने वाले फ़ंक्शन के दौरान सेव रहते हैं.

हर ओवरलोड, एक IBurstContext ऑब्जेक्ट दिखाता है (जो बर्स्ट ऑब्जेक्ट को दिखाता है). इसमें एक खास लिसनर थ्रेड होती है, जिसे ऑब्जेक्ट मैनेज करता है. इस थ्रेड को requestChannel एफ़एमक्यू से अनुरोध मिलते हैं. इसके बाद, यह अनुमान लगाता है और resultChannel एफ़एमक्यू के ज़रिए नतीजे दिखाता है. जब बर्स्ट के क्लाइंट के पास IBurstContext का रेफ़रंस नहीं होता, तब IBurstContext ऑब्जेक्ट में मौजूद यह थ्रेड और अन्य सभी संसाधन अपने-आप रिलीज़ हो जाते हैं.

इसके अलावा, IBurstContext को अपने हिसाब से लागू किया जा सकता है. यह requestChannel और resultChannel एफ़एमक्यू के ज़रिए, IPreparedModel::configureExecutionBurst को मैसेज भेजने और पाने का तरीका जानता है.

बर्स्ट यूटिलिटी फ़ंक्शन, 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);

यहां बर्स्ट इंटरफ़ेस को लागू करने का रेफ़रंस दिया गया है. यह इंटरफ़ेस, 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();
}