शेड्यूल करने की प्रोसेस
MediaPipe ग्राफ़ में डेटा प्रोसेसिंग, CalculatorBase
सब-क्लास के रूप में बताए गए प्रोसेसिंग नोड के अंदर होती है. शेड्यूलिंग सिस्टम यह तय करता है कि हर कैलकुलेटर को कब चलाना चाहिए.
हर ग्राफ़ में कम से कम एक शेड्यूलर सूची होती है. शेड्यूलर की हर सूची में सिर्फ़ एक executor होता है. नोड, स्टैटिक रूप से सूची में असाइन किए जाते हैं (और इसके बाद एग्ज़ेक्यूर को असाइन किए जाते हैं). डिफ़ॉल्ट रूप से एक सूची होती है, जिसका एक्ज़ेक्यूटर एक थ्रेड पूल होता है, जिसमें सिस्टम की क्षमताओं के आधार पर कई थ्रेड होते हैं.
हर नोड की एक शेड्यूलिंग स्थिति होती है, जो तैयार नहीं, तैयार या चल सकती है. रेडीनेस फ़ंक्शन से यह तय होता है कि नोड चलाने के लिए तैयार है या नहीं. जब भी नोड का चलना पूरा हो जाता है और जब किसी नोड के इनपुट की स्थिति बदलती है, तो यह फ़ंक्शन, ग्राफ़ शुरू करने के दौरान शुरू किया जाता है.
इस्तेमाल किया जाने वाला रेडीनेस फ़ंक्शन, नोड के टाइप पर निर्भर करता है. बिना स्ट्रीम इनपुट वाले नोड को सोर्स नोड कहा जाता है. सोर्स नोड, चलाने के लिए हमेशा तैयार रहते हैं. ऐसा तब तक होता है, जब तक वे फ़्रेमवर्क के बारे में यह नहीं बता देते कि आउटपुट के लिए उनके पास ज़्यादा डेटा नहीं है और उन्हें बंद कर दिया जाता है.
नॉन-सोर्स नोड तब तैयार होते हैं, जब उनमें प्रोसेस करने के लिए इनपुट हों. साथ ही, अगर वे इनपुट, नोड की इनपुट नीति की ओर से तय की गई शर्तों के हिसाब से मान्य इनपुट सेट बनाते हैं (इसके बारे में नीचे बताया गया है). ज़्यादातर नोड डिफ़ॉल्ट इनपुट नीति का इस्तेमाल करते हैं, लेकिन कुछ नोड किसी दूसरी नीति के बारे में बताते हैं.
जब नोड तैयार हो जाता है, तो उससे जुड़े शेड्यूलर सूची में टास्क जोड़ दिया जाता है. यह एक प्राथमिकता वाली सूची होती है. फ़िलहाल, प्राथमिकता फ़ंक्शन तय है और वह नोड की स्टैटिक प्रॉपर्टी और ग्राफ़ में उनके टॉपोलॉजिकल क्रम को ध्यान में रखता है. उदाहरण के लिए, ग्राफ़ के आउटपुट साइड के पास मौजूद नोड की प्राथमिकता ज़्यादा होती है, जबकि सोर्स नोड की प्राथमिकता सबसे कम होती है.
हर सूची को एक एक्ज़ेक्यूटर की ओर से दिया जाता है, जो कैलकुलेटर के कोड का इस्तेमाल करके इस टास्क को असल में पूरा करता है. अलग-अलग एक्ज़ेक्यूटर दिए और कॉन्फ़िगर किए जा सकते हैं; इसका इस्तेमाल, एक्ज़ीक्यूशन संसाधनों के इस्तेमाल को पसंद के मुताबिक बनाने के लिए किया जा सकता है. उदाहरण के लिए, कम प्राथमिकता वाले थ्रेड पर कुछ नोड चलाकर.
टाइमस्टैंप सिंक करने की सुविधा
MediaPipe ग्राफ़ का निष्पादन डीसेंट्रलाइज़्ड होता है: कोई ग्लोबल घड़ी नहीं होती और अलग-अलग नोड एक ही समय पर अलग-अलग टाइमस्टैंप से डेटा को प्रोसेस कर सकते हैं. इससे पाइपलाइनिंग के ज़रिए प्रवाह की क्षमता बढ़ जाती है.
हालांकि, कई अनुभवों से जुड़े वर्कफ़्लो के लिए समय की जानकारी बहुत ज़रूरी होती है. कई इनपुट स्ट्रीम पाने वाले नोड को, आम तौर पर उन्हें किसी न किसी तरह से सिंक करना होता है. उदाहरण के लिए, ऑब्जेक्ट डिटेक्टर किसी फ़्रेम से, सीमा वाले रेक्टैंगल की सूची बना सकता है. इस जानकारी को रेंडरिंग नोड में डाला जा सकता है, जो इसे ओरिजनल फ़्रेम के साथ प्रोसेस कर सकता है.
इसलिए, MediaPipe फ़्रेमवर्क की मुख्य ज़िम्मेदारी नोड के लिए इनपुट सिंक करने की सुविधा उपलब्ध कराना है. फ़्रेमवर्क की प्रोसेस में, टाइमस्टैंप की मुख्य भूमिका सिंक्रोनाइज़ेशन कुंजी के तौर पर काम करती है.
इसके अलावा, MediaPipe को डिटर्मिनिस्टिक ऑपरेशन को सपोर्ट करने के लिए डिज़ाइन किया गया है, जो कई स्थितियों (टेस्टिंग, सिम्युलेशन, बैच प्रोसेसिंग वगैरह) में अहम है. वहीं, यह ग्राफ़ के लेखकों को रीयल-टाइम की सीमाओं को पूरा करने की ज़रूरत पड़ने पर, सारणिकता को कम करने में मदद करता है.
सिंक करने और सारणिकता के दो मकसदों के बीच कई डिज़ाइन
विकल्प होते हैं. खास तौर पर, किसी स्ट्रीम में पुश किए गए पैकेट में एक तरह से बढ़ते टाइमस्टैंप होने चाहिए: यह न सिर्फ़ कई नोड के लिए काम का अनुमान है, बल्कि सिंक करने के लॉजिक के हिसाब से भी इस पर भरोसा किया जाता है. हर स्ट्रीम के लिए, टाइमस्टैंप की एक सीमा होती है. यह स्ट्रीम पर किसी नए पैकेट के लिए इस्तेमाल किया जा सकने वाला सबसे कम टाइमस्टैंप होता है. जब T
टाइमस्टैंप वाला पैकेट मिलता है, तो बाउंड
अपने-आप T+1
हो जाता है. इससे, एक ही समय में मैसेज का समय तय करने वाली ज़रूरी शर्त का पता चलता है. इससे फ़्रेमवर्क को यह पक्का करने में मदद मिलती है कि T
से कम टाइमस्टैंप वाले पैकेट नहीं आएंगे.
इनपुट से जुड़ी नीतियां
सिंक करने की प्रोसेस, हर नोड पर स्थानीय तौर पर मैनेज की जाती है. इसके लिए, नोड पर बताई गई इनपुट नीति का इस्तेमाल किया जाता है.
DefaultInputStreamHandler
की तय की गई डिफ़ॉल्ट इनपुट नीति,
इनपुट को तय समय के हिसाब से सिंक करने की सुविधा देती है. साथ ही, इस तरह की गारंटी दी जाती है:
अगर कई इनपुट स्ट्रीम के लिए एक ही टाइमस्टैंप वाले पैकेट दिए गए हैं, तो उन्हें हमेशा एक साथ प्रोसेस किया जाएगा. भले ही, उनके पहुंचने का ऑर्डर रीयल-टाइम में हो.
इनपुट सेट, बढ़ते क्रम में टाइमस्टैंप के हिसाब से प्रोसेस किए जाते हैं.
कोई पैकेट नहीं छोड़ा जाता है और प्रोसेसिंग पूरी तरह से तय होती है.
ऊपर दी गई गारंटी को देखते हुए नोड, डेटा को जल्द से जल्द प्रोसेस करने के लिए तैयार हो जाता है.
इसके काम करने का तरीका बताने के लिए, हमें सेटल टाइमस्टैंप की परिभाषा बतानी होगी. हम कहते हैं कि स्ट्रीम का टाइमस्टैंप सेट किया जाता है, अगर यह टाइमस्टैंप की सीमा से कम होता है. दूसरे शब्दों में, किसी स्ट्रीम के लिए टाइमस्टैंप सेटल हो जाता है, जब उस टाइमस्टैंप पर इनपुट की स्थिति के बारे में साफ़ तौर पर पता चल जाता है: या तो एक पैकेट होता है या फिर यह पक्का होता है कि उस टाइमस्टैंप वाला पैकेट नहीं आएगा.
टाइमस्टैंप को कई स्ट्रीम में सेटल किया जाता है. ऐसा तब होता है, जब टाइमस्टैंप को हर स्ट्रीम में ही सेटल किया गया हो. इसके अलावा, अगर टाइमस्टैंप का सेटलमेंट है, तो इसका मतलब है कि पिछले सभी टाइमस्टैंप भी सेटलमेंट हैं. इस तरह से, सेटलमेंट के टाइमस्टैंप को तय किए गए बढ़ते क्रम में प्रोसेस किया जा सकता है.
इस परिभाषा के हिसाब से, डिफ़ॉल्ट इनपुट नीति वाला एक कैलकुलेटर तैयार होता है. ऐसा तब होता है, जब एक ऐसा टाइमस्टैंप हो जो सभी इनपुट स्ट्रीम में सेट हो और जिसमें कम से कम एक इनपुट स्ट्रीम में पैकेट मौजूद हो. इनपुट नीति में, सेट किए गए टाइमस्टैंप के लिए सभी उपलब्ध पैकेट होते हैं. इन्हें कैलकुलेटर के लिए, इनपुट सेट के तौर पर सेट किया जाता है.
इस व्यवहार का एक नतीजा यह होता है कि कई इनपुट स्ट्रीम वाले नोड के लिए, टाइमस्टैंप के सेट होने के लिए सैद्धांतिक रूप से अनबाउंड इंतज़ार किया जा सकता है. साथ ही, इस दौरान पैकेट की अनबाउंड संख्या वाले बफ़र को बफ़र किया जा सकता है. (दो इनपुट स्ट्रीम वाले नोड पर ध्यान दें, जिसमें से एक पैकेट भेजता रहता है, जबकि दूसरा कुछ नहीं भेजता है और सीमा को आगे नहीं बढ़ाता है.)
इसलिए, हम पसंद के मुताबिक इनपुट की नीतियां भी उपलब्ध कराते हैं: उदाहरण के लिए, SyncSetInputStreamHandler
के तय किए गए अलग-अलग सिंक में इनपुट को बांटें. इसके अलावा, सिंक करने से पूरी तरह बचें और ImmediateInputStreamHandler
से तय किए गए इनपुट को तुरंत प्रोसेस करें.
फ़्लो कंट्रोल
फ़्लो कंट्रोल के दो मुख्य तरीके हैं. जब किसी स्ट्रीम पर पैकेट बफ़र किए गए पैकेट CalculatorGraphConfig::max_queue_size
की तय की गई सीमा (कॉन्फ़िगर करने लायक) तक पहुंच जाते हैं, तब बैकप्रेशर तकनीक अपस्ट्रीम नोड के एक्ज़ीक्यूशन को थ्रॉटल करती है. यह तकनीक, तय किए गए व्यवहार को बनाए रखती है. साथ ही, इसमें डेडलॉक से बचने का सिस्टम होता है, जो ज़रूरत के समय कॉन्फ़िगर की गई सीमाओं में छूट देता है.
दूसरे सिस्टम में खास नोड शामिल किए जाते हैं, जो FlowLimiterCalculator
की बताई गई
रीयल-टाइम सीमाओं (आम तौर पर कस्टम इनपुट नीतियों का इस्तेमाल करके) के हिसाब से पैकेट ड्रॉप कर सकते हैं. उदाहरण के लिए, एक सामान्य पैटर्न, सबग्राफ़ के इनपुट पर फ़्लो-कंट्रोल नोड रखता है, जिसमें फ़ाइनल आउटपुट से फ़्लो-कंट्रोल नोड तक लूपबैक कनेक्शन होता है. फ़्लो-कंट्रोल नोड यह ट्रैक कर सकता है कि डाउनस्ट्रीम ग्राफ़ में कितने टाइमस्टैंप प्रोसेस किए जा रहे हैं. साथ ही, अगर यह संख्या (कॉन्फ़िगर करने लायक) सीमा तक पहुंच जाती है, तो पैकेट ड्रॉप किए जा सकते हैं. साथ ही, पैकेट को अपस्ट्रीम ड्रॉप किया जाता है, इसलिए हम इस तरह की बर्बादी से बचते हैं जो टाइमस्टैंप को कुछ हद तक प्रोसेस करता है और फिर बीच के लेवल के बीच पैकेट को ड्रॉप करता है.
कैलकुलेटर पर आधारित इस तरीके की मदद से, ग्राफ़ लेखक यह कंट्रोल कर सकता है कि पैकेट कहां छोड़े जा सकते हैं. साथ ही, संसाधन की कमी के हिसाब से ग्राफ़ के काम करने के तरीके को ज़रूरत के हिसाब से बदला जा सकता है.