मेमोरी पूल

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

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

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

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

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

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

AHardwareBuffer, शेयर की गई मेमोरी का एक टाइप है. यह Gralloc बफ़र को रैप करता है. Android 10 में, न्यूरल नेटवर्क एपीआई (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 या Grallocbuffer के बारे में सभी ज़रूरी मेटाडेटा को कोड में बदलता है.

ड्राइवर को दिए गए hidl_handle फ़ील्ड को सही तरीके से डिकोड करना चाहिए और hidl_handle में बताई गई मेमोरी को ऐक्सेस करना चाहिए. getSupportedOperations_1_2, getSupportedOperations_1_1 या getSupportedOperations तरीके का इस्तेमाल करने पर, ड्राइवर को यह पता लगाना चाहिए कि वह दिए गए hidl_handle को डिकोड कर सकता है या नहीं. साथ ही, यह भी पता लगाना चाहिए कि वह hidl_handle में बताई गई मेमोरी को ऐक्सेस कर सकता है या नहीं. अगर किसी कॉन्स्टेंट ऑपरेंड के लिए इस्तेमाल किया गया hidl_handle फ़ील्ड काम नहीं करता है, तो मॉडल तैयार करने की प्रोसेस पूरी नहीं हो पाएगी. अगर एक्सीक्यूशन के इनपुट या आउटपुट ऑपरेंड के लिए इस्तेमाल किया गया hidl_handle फ़ील्ड काम नहीं करता है, तो एक्सीक्यूशन पूरा नहीं होगा. हमारा सुझाव है कि अगर मॉडल तैयार करने या उसे लागू करने में कोई गड़बड़ी होती है, तो ड्राइवर को GENERAL_FAILURE गड़बड़ी कोड दिखाना चाहिए.

मेमोरी डोमेन

Android 11 या इसके बाद के वर्शन वाले डिवाइसों के लिए, NNAPI, मेमोरी डोमेन के साथ काम करता है. ये डोमेन, ड्राइवर के मैनेज किए जाने वाले बफ़र के लिए, एलोकेटर इंटरफ़ेस उपलब्ध कराते हैं. इससे, एक ही ड्राइवर पर एक के बाद एक कई बार चलाए जाने वाले प्रोग्राम के बीच, डिवाइस की नेटिव मेमोरी को पास किया जा सकता है. साथ ही, ज़रूरत के बिना डेटा को कॉपी करने और बदलाव करने से रोका जा सकता है. इस फ़्लो को पहली इमेज में दिखाया गया है.

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

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

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

मेमोरी डोमेन की सुविधा का इस्तेमाल करने के लिए, 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 में दिए गए डाइमेंशन से लगाया जा सकता है. सभी डाइमेंशन की जानकारी को एक साथ जोड़ने पर, हो सकता है कि बफ़र में अज्ञात डाइमेंशन या रैंक हो. ऐसे में, बफ़र में डाइमेंशन की स्थिति बदल सकती है. मॉडल इनपुट के तौर पर इस्तेमाल करने पर, डाइमेंशन की स्थिति तय रहती है. वहीं, मॉडल आउटपुट के तौर पर इस्तेमाल करने पर, डाइमेंशन की स्थिति डाइनैमिक हो जाती है. एक ही बफ़र का इस्तेमाल, अलग-अलग एक्सीक्यूशन में आउटपुट के अलग-अलग शेप के साथ किया जा सकता है. साथ ही, ड्राइवर को बफ़र के साइज़ में बदलाव को सही तरीके से मैनेज करना चाहिए.

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

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

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