मेमोरी पूल

इस पेज पर, डेटा स्ट्रक्चर और तरीकों के बारे में बताया गया है. इनका इस्तेमाल, ड्राइवर और फ़्रेमवर्क के बीच ऑपरेंड बफ़र को असरदार तरीके से कम्यूनिकेट करने के लिए किया जाता है.

मॉडल कंपाइलेशन के समय, फ़्रेमवर्क ड्राइवर को कॉन्स्टेंट ऑपरेटिंग की वैल्यू देता है. कॉन्स्टेंट ऑपरेंड के लाइफ़टाइम के आधार पर, इसकी वैल्यू HIDL वेक्टर या शेयर की गई मेमोरी पूल में होती हैं.

  • अगर लाइफ़टाइम CONSTANT_COPY है, तो वैल्यू मॉडल स्ट्रक्चर के operandValues फ़ील्ड में मौजूद होती हैं. इंटरप्रोसेस कम्यूनिकेशन (आईपीसी) के दौरान, HIDL वेक्टर में मौजूद वैल्यू कॉपी की जाती हैं. इसलिए, इसका इस्तेमाल आम तौर पर सिर्फ़ कम डेटा को सेव करने के लिए किया जाता है. जैसे, स्केलर ऑपरेंड (उदाहरण के लिए, ADD में ऐक्टिवेशन स्केलर) और छोटे टेंसर पैरामीटर (उदाहरण के लिए, RESHAPE में शेप टेंसर).
  • अगर लाइफ़टाइम CONSTANT_REFERENCE है, तो वैल्यू मॉडल स्ट्रक्चर के pools फ़ील्ड में मौजूद होती हैं. आईपीसी के दौरान, रॉ वैल्यू को कॉपी करने के बजाय, सिर्फ़ शेयर की गई मेमोरी पूल के हैंडल का डुप्लीकेट बनाया जाता है. इसलिए, HIDL वैक्टर के बजाय, शेयर किए गए मेमोरी पूल का इस्तेमाल करके ज़्यादा डेटा (उदाहरण के लिए, कॉन्वोल्यूशन में वेट पैरामीटर) को सेव करना ज़्यादा बेहतर होता है.

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

HIDL डेटा टाइप hidl_memory का इस्तेमाल, कंपाइलेशन और एक्सीक्यूशन, दोनों में किया जाता है. इससे, मैप नहीं किए गए शेयर किए गए मेमोरी पूल को दिखाया जाता है. ड्राइवर को मेमोरी को इस हिसाब से मैप करना चाहिए कि उसे hidl_memory डेटा टाइप के नाम के आधार पर इस्तेमाल किया जा सके. मेमोरी के लिए ये नाम इस्तेमाल किए जा सकते हैं:

  • ashmem: Android ने आपके साथ मेमोरी शेयर की है. ज़्यादा जानकारी के लिए, मेमोरी देखें.
  • mmap_fd: mmap की मदद से, फ़ाइल डिस्क्रिप्टर के ज़रिए शेयर की गई मेमोरी.
  • hardware_buffer_blob: AHARDWARE_BUFFER_FORMAT_BLOB फ़ॉर्मैट वाली AHardwareBuffer के ज़रिए बैक अप की गई शेयर की गई मेमोरी. यह सुविधा, नेटल नेटवर्क (एनएन) एचएएल 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

AHardwareBuffer, शेयर की गई मेमोरी का एक टाइप है. यह Gralloc बफ़र को रैप करता है. Android 10 में, Neural Networks API (NNAPI) की मदद से AHardwareBuffer का इस्तेमाल किया जा सकता है. इससे ड्राइवर, डेटा को कॉपी किए बिना डेटा को कॉपी कर सकता है. इससे ऐप्लिकेशन की परफ़ॉर्मेंस बेहतर होती है और ऊर्जा की खपत ज़्यादा होती है. उदाहरण के लिए, कैमरा एचएएल स्टैक, मशीन लर्निंग के वर्कलोड के लिए, NNAPI को AHardwareBuffer ऑब्जेक्ट पास कर सकता है. इसके लिए, कैमरा एनडीके और मीडिया एनडीके एपीआई से जनरेट किए गए AHardwareBuffer हैंडल का इस्तेमाल किया जाता है. ज़्यादा जानकारी के लिए, ANeuralNetworksMemory_createFromAHardwareBuffer देखें.

NNAPI में इस्तेमाल किए गए AHardwareBuffer ऑब्जेक्ट, ड्राइवर को hardware_buffer या hardware_buffer_blob नाम वाले hidl_memory स्ट्रक्चर के ज़रिए पास किए जाते हैं. hidl_memory स्ट्रक्चर hardware_buffer_blob, सिर्फ़ AHARDWAREBUFFER_FORMAT_BLOB फ़ॉर्मैट वाले AHardwareBuffer ऑब्जेक्ट दिखाता है.

फ़्रेमवर्क के लिए ज़रूरी जानकारी को hidl_memory स्ट्रक्चर के hidl_handle फ़ील्ड में एन्कोड किया गया है. hidl_handle फ़ील्ड में native_handle शामिल होता है, जो AHardwareBuffer या Gralloc बफ़र के बारे में सभी ज़रूरी मेटाडेटा को कोड में बदल देता है.

ड्राइवर को दिए गए 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 में यह फ़्लो दिखाया गया है.

मेमोरी डोमेन के साथ और बिना मेमोरी डोमेन के डेटा फ़्लो को बफ़र करना

पहली इमेज. मेमोरी डोमेन का इस्तेमाल करके, डेटा फ़्लो को बफ़र करना

मेमोरी डोमेन की सुविधा, उन टेंसर के लिए है जो ड्राइवर के लिए ज़्यादातर इंटरनल होते हैं और जिन्हें क्लाइंट साइड पर बार-बार ऐक्सेस करने की ज़रूरत नहीं होती. ऐसे टेंसर के उदाहरणों में, सीक्वेंस मॉडल में स्टेट टेंसर शामिल हैं. जिन टेंसर को क्लाइंट-साइड पर बार-बार सीपीयू ऐक्सेस की ज़रूरत होती है उनके लिए, शेयर की गई मेमोरी पूल का इस्तेमाल करना बेहतर होता है.

मेमोरी डोमेन की सुविधा का इस्तेमाल करने के लिए, IDevice::allocate को लागू करें. इससे फ़्रेमवर्क, ड्राइवर के मैनेज किए गए बफ़र के लिए अनुरोध कर पाएगा. ऐलोकेशन के दौरान, फ़्रेमवर्क बफ़र के लिए ये प्रॉपर्टी और इस्तेमाल के पैटर्न उपलब्ध कराता है:

  • BufferDesc, बफ़र की ज़रूरी प्रॉपर्टी के बारे में बताता है.
  • BufferRole यह बताता है कि बफ़र को पहले से तैयार मॉडल के इनपुट या आउटपुट के तौर पर इस्तेमाल किया जा सकता है या नहीं. बफ़र ऐलोकेशन के दौरान एक से ज़्यादा भूमिकाएं दी जा सकती हैं और तय किए गए बफ़र का इस्तेमाल, सिर्फ़ उन भूमिकाओं के तौर पर किया जा सकता है.

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

IDevice::allocate का टोकन तब दिया जाता है, जब बफ़र को एक्ज़ीक्यूट करने के Request स्ट्रक्चर में MemoryPool ऑब्जेक्ट में से एक के तौर पर रेफ़रंस दिया जाता है. किसी प्रोसेस को किसी दूसरी प्रोसेस में असाइन किए गए बफ़र को ऐक्सेस करने से रोकने के लिए, ड्राइवर को बफ़र के हर इस्तेमाल पर सही पुष्टि करनी होगी. ड्राइवर को यह पुष्टि करनी होगी कि बफ़र का इस्तेमाल, ऐलोकेशन के दौरान दी गई BufferRole भूमिकाओं में से एक है. अगर बफ़र का इस्तेमाल गैर-कानूनी है, तो उसे तुरंत बंद कर देना चाहिए.

IBuffer ऑब्जेक्ट का इस्तेमाल, साफ़ तौर पर मेमोरी कॉपी करने के लिए किया जाता है. कुछ मामलों में, ड्राइवर के क्लाइंट को शेयर किए गए मेमोरी पूल से, ड्राइवर के मैनेज किए गए बफ़र को शुरू करना होगा या बफ़र को शेयर किए गए मेमोरी पूल में कॉपी करना होगा. इस्तेमाल के उदाहरणों में ये शामिल हैं:

  • स्टेट टेंसर को शुरू करना
  • बीच के लेवल पर मिलने वाले नतीजों को कैश मेमोरी में सेव करना
  • सीपीयू पर फ़ॉलबैक लागू करना

इस्तेमाल के इन उदाहरणों के काम करने के लिए, ड्राइवर को ashmem, mmap_fd, और hardware_buffer_blob के साथ IBuffer::copyTo और IBuffer::copyFrom को लागू करना होगा. ऐसा तब करना होगा, जब यह मेमोरी डोमेन असाइन करने की सुविधा के साथ काम करता हो. ड्राइवर के लिए, नॉन-BLOB मोड का इस्तेमाल करना ज़रूरी नहीं है hardware_buffer.

बफ़र के लिए जगह तय करते समय, बफ़र के डाइमेंशन का पता BufferRole में बताई गई सभी भूमिकाओं के मॉडल ऑपरेंड और BufferDesc में दिए गए डाइमेंशन से लगाया जा सकता है. सभी डाइमेंशन की जानकारी को एक साथ जोड़ने पर, हो सकता है कि बफ़र में अज्ञात डाइमेंशन या रैंक हो. ऐसे में, बफ़र में डाइमेंशन की स्थिति बदलती रहती है. मॉडल इनपुट के तौर पर इस्तेमाल करने पर, डाइमेंशन की स्थिति में कोई बदलाव नहीं होता. वहीं, मॉडल आउटपुट के तौर पर इस्तेमाल करने पर, डाइमेंशन की स्थिति डाइनैमिक हो जाती है. एक ही बफ़र का इस्तेमाल, अलग-अलग एक्सीक्यूशन में आउटपुट के अलग-अलग शेप के साथ किया जा सकता है. साथ ही, ड्राइवर को बफ़र के साइज़ में बदलाव को सही तरीके से मैनेज करना चाहिए.

मेमोरी डोमेन की सुविधा का इस्तेमाल करना ज़रूरी नहीं है. ड्राइवर कई वजहों से यह तय कर सकता है कि वह किसी खास ऐलोकेशन अनुरोध को पूरा नहीं कर सकता. उदाहरण के लिए:

  • अनुरोध किए गए बफ़र का साइज़ डाइनैमिक है.
  • ड्राइवर में मेमोरी की कमी है, जिसकी वजह से बड़े बफ़र को मैनेज नहीं किया जा सकता.

ड्राइवर के मैनेज किए जाने वाले बफ़र से, एक साथ कई अलग-अलग थ्रेड पढ़ सकते हैं. बफ़र को एक साथ लिखने या पढ़ने/लिखने के लिए ऐक्सेस करना, तय नहीं है. हालांकि, इससे ड्राइवर सेवा क्रैश नहीं होनी चाहिए या कॉल करने वाले को अनलिमिटेड तौर पर ब्लॉक नहीं करना चाहिए. ड्राइवर कोई गड़बड़ी दिखा सकता है या बफ़र के कॉन्टेंट को अनिश्चित स्थिति में छोड़ सकता है.