मेमोरी पूल

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

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

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

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

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