मेमोरी पूल

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

मॉडल कंपाइल करने के समय, फ़्रेमवर्क ड्राइवर को कॉन्स्टेंट ऑपरेंड की वैल्यू देता है. कॉन्स्टेंट ऑपरेंड की लाइफ़टाइम के आधार पर, इसकी वैल्यू 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 से बैक अप लिया गया है. यह Neural Networks (NN) HAL 1.2 से उपलब्ध है. ज़्यादा जानकारी के लिए, AHardwareBuffer देखें.
  • hardware_buffer: शेयर की गई मेमोरी, एक सामान्य AHardwareBuffer पर आधारित होती है. यह AHARDWARE_BUFFER_FORMAT_BLOB फ़ॉर्मैट का इस्तेमाल नहीं करती है. नॉन-ब्लॉब मोड वाला हार्डवेयर बफ़र, सिर्फ़ मॉडल एक्ज़ीक्यूशन में काम करता है.यह NN HAL 1.2 से उपलब्ध है. ज़्यादा जानकारी के लिए, AHardwareBuffer देखें.

NN HAL 1.3 से, NNAPI ऐसे मेमोरी डोमेन के साथ काम करता है जो ड्राइवर मैनेज किए गए बफ़र के लिए, ऐलोकेटर इंटरफ़ेस उपलब्ध कराते हैं. ड्राइवर मैनेज किए गए बफ़र का इस्तेमाल, एक्ज़ीक्यूशन इनपुट या आउटपुट के तौर पर भी किया जा सकता है. ज़्यादा जानकारी के लिए, मेमोरी डोमेन देखें.

NNAPI ड्राइवर में, ashmem और mmap_fd मेमोरी के नामों को मैप करने की सुविधा होनी चाहिए. NN HAL 1.3 से, ड्राइवर को hardware_buffer_blob की मैपिंग की सुविधा भी देनी होगी. सामान्य नॉन-ब्लॉब मोड hardware_buffer और मेमोरी डोमेन के लिए सहायता ज़रूरी नहीं है.

AHardwareBuffer

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

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

फ़्रेमवर्क के लिए ज़रूरी जानकारी, hidl_handle struct के hidl_handle फ़ील्ड में कोड की जाती है.hidl_memory 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 ऐसे मेमोरी डोमेन के साथ काम करता है जो ड्राइवर मैनेज किए गए बफ़र के लिए, ऐलोकेटर इंटरफ़ेस उपलब्ध कराते हैं. इससे डिवाइस की नेटिव मेमोरी को एक से ज़्यादा बार इस्तेमाल किया जा सकता है. साथ ही, एक ही ड्राइवर पर लगातार इस्तेमाल करने के दौरान, डेटा को ज़रूरत से ज़्यादा कॉपी और ट्रांसफ़ॉर्म करने से रोका जा सकता है. इस फ़्लो को पहली इमेज में दिखाया गया है.

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

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

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

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

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

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

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

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

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

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

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

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

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

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