डाइनैमिक एक्ज़ीक्यूशन

किसी समस्या की शिकायत करें सोर्स देखें Nightly · 8.2 · 8.1 · 8.0 · 7.6 · 7.5

डाइनैमिक तरीके से प्रोग्राम चलाना, Bazel की एक सुविधा है. यह सुविधा 0.21 वर्शन से उपलब्ध है. इसमें एक ही ऐक्शन को एक साथ लोकल और रिमोट, दोनों जगह पर चलाया जाता है. इसके लिए, पहले पूरी होने वाली शाखा के आउटपुट का इस्तेमाल किया जाता है और दूसरी शाखा को रद्द कर दिया जाता है. यह रिमोट बिल्ड सिस्टम की, प्रोग्राम को लागू करने की क्षमता और/या बड़े शेयर किए गए कैश मेमोरी को, स्थानीय तौर पर प्रोग्राम को लागू करने में लगने वाले कम समय के साथ जोड़ता है. इससे क्लीन और इंक्रीमेंटल, दोनों तरह के बिल्ड के लिए, दोनों ही तरीकों का फ़ायदा मिलता है.

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

क्या डाइनैमिक तरीके से प्रोग्राम चलाने की सुविधा चालू करनी है?

डाइनैमिक एक्सीक्यूशन मॉड्यूल, Bazel का हिस्सा है. हालांकि, डाइनैमिक एक्सीक्यूशन का इस्तेमाल करने के लिए, आपके पास एक ही Bazel सेटअप से, लोकल और रिमोट, दोनों तरह से कंपाइल करने की सुविधा होनी चाहिए.

डाइनैमिक तरीके से प्रोग्राम चलाने वाले मॉड्यूल को चालू करने के लिए, Bazel को --internal_spawn_scheduler फ़्लैग पास करें. इससे, dynamic नाम की एक नई रणनीति जोड़ी जाती है. अब आपके पास, उन मेमोनेमिक के लिए अपनी रणनीति के तौर पर इसका इस्तेमाल करने का विकल्प है जिन्हें डाइनैमिक तौर पर चलाना है, जैसे कि --strategy=Javac=dynamic. डायनैमिक तरीके से लागू करने के लिए, कौनसे मेमोनिक चुनने हैं, यह जानने के लिए अगला सेक्शन देखें.

डाइनैमिक रणनीति का इस्तेमाल करने वाले किसी भी मेनिमोन के लिए, रिमोट तौर पर लागू होने वाली रणनीतियां --dynamic_remote_strategy फ़्लैग से ली जाती हैं और स्थानीय रणनीतियां --dynamic_local_strategy फ़्लैग से ली जाती हैं. --dynamic_local_strategy=worker,sandboxed को पास करने पर, डाइनैमिक तरीके से लागू करने की स्थानीय शाखा के लिए डिफ़ॉल्ट सेटिंग सेट हो जाती है. इससे, उस क्रम में वर्कर्स या सैंडबॉक्स किए गए तरीके से लागू करने की कोशिश की जा सकती है. --dynamic_local_strategy=Javac=worker पास करने पर, सिर्फ़ Javac के लिए डिफ़ॉल्ट सेटिंग बदल जाती है. रिमोट वर्शन भी इसी तरह काम करता है. दोनों फ़्लैग को एक से ज़्यादा बार इस्तेमाल किया जा सकता है. अगर कोई कार्रवाई स्थानीय तौर पर नहीं की जा सकती, तो उसे सामान्य तौर पर रिमोट से किया जाता है. इसके उलट, अगर कोई कार्रवाई रिमोट से नहीं की जा सकती, तो उसे स्थानीय तौर पर किया जाता है.

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

डाइनैमिक तरीके से प्रोसेस करने की सुविधा का इस्तेमाल, लोकल सैंडबॉक्स की रणनीति के साथ-साथ पर्सिस्टेंट वर्कर्स के साथ भी किया जा सकता है. डाइनैमिक तरीके से लागू करने के साथ इस्तेमाल किए जाने पर, हमेशा चालू रहने वाले वर्कर्स अपने-आप सैंडबॉक्सिंग के साथ चलेंगे. साथ ही, मल्टीप्लेक्स वर्कर्स का इस्तेमाल नहीं किया जा सकता. Darwin और Windows सिस्टम पर, सैंडबॉक्स की रणनीति धीमी हो सकती है. इन सिस्टम पर सैंडबॉक्स बनाने के ओवरहेड को कम करने के लिए, --reuse_sandbox_directories पास किया जा सकता है.

डाइनैमिक तरीके से लागू होने की सुविधा, standalone रणनीति के साथ भी काम कर सकती है. हालांकि, standalone रणनीति को लागू होने के दौरान आउटपुट लॉक लेना होता है. इससे, रिमोट रणनीति को पहले लागू होने से रोका जा सकता है. --experimental_local_lockfree_output फ़्लैग की मदद से, इस समस्या को हल किया जा सकता है. इसके लिए, स्थानीय तौर पर एक्ज़ीक्यूट होने वाले फ़ंक्शन को सीधे आउटपुट में लिखने की अनुमति दी जाती है. हालांकि, अगर रिमोट तौर पर एक्ज़ीक्यूट होने वाला फ़ंक्शन पहले पूरा हो जाता है, तो स्थानीय तौर पर एक्ज़ीक्यूट होने वाले फ़ंक्शन को रोक दिया जाता है.

अगर डाइनैमिक तरीके से लागू होने की कोई शाखा पहले खत्म हो जाती है, लेकिन वह काम नहीं करती है, तो पूरी कार्रवाई पूरी नहीं होती. यह जान-बूझकर किया गया विकल्प है, ताकि लोकल और रिमोट रनटाइम के बीच के अंतर को अनदेखा न किया जा सके.

डाइनैमिक तरीके से लागू होने और लॉक होने की प्रोसेस के बारे में ज़्यादा जानने के लिए, Julio Merino की बेहतरीन ब्लॉग पोस्ट देखें

मुझे डाइनैमिक तरीके से लागू करने का विकल्प कब चुनना चाहिए?

डाइनैमिक तरीके से एक्ज़ीक्यूशन करने के लिए, रिमोट एक्ज़ीक्यूशन सिस्टम की ज़रूरत होती है. फ़िलहाल, सिर्फ़ कैश मेमोरी वाले रिमोट सिस्टम का इस्तेमाल नहीं किया जा सकता. ऐसा इसलिए, क्योंकि कैश मेमोरी में डेटा न मिलने पर, कार्रवाई पूरी न होने के तौर पर मार्क कर दिया जाएगा.

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

रिलीज़ 5.0.0-pre.20210708.4 के बाद, परफ़ॉर्मेंस प्रोफ़ाइलिंग में वर्कर्स के एक्सीक्यूशन का डेटा शामिल होता है. इसमें, डाइनैमिक एक्सीक्यूशन रेस में हारने के बाद, किसी वर्क रिक्वेस्ट को पूरा करने में लगने वाला समय भी शामिल होता है. अगर आपको डाइनैमिक रनटाइम वर्क थ्रेड, संसाधन हासिल करने में ज़्यादा समय बिताते हुए दिखते हैं या async-worker-finish में ज़्यादा समय बिताते हैं, तो हो सकता है कि कुछ धीमी स्थानीय कार्रवाइयों की वजह से वर्क थ्रेड में देरी हो रही हो.

डाइनैमिक तरीके से लागू करने की खराब परफ़ॉर्मेंस वाले डेटा की प्रोफ़ाइल बनाना

ऊपर दी गई प्रोफ़ाइल में, आठ Javac वर्कर्स का इस्तेमाल किया गया है. इसमें हमने देखा कि कई Javac वर्कर्स, रेस में हार गए और async-worker-finish थ्रेड पर अपना काम पूरा किया. ऐसा इसलिए हुआ, क्योंकि नॉन-वर्कर मेमोनेम को ज़रूरत के मुताबिक संसाधन मिल रहे थे, जिसकी वजह से वर्कर्स को देरी हो रही थी.

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

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

पहले सुझाया गया --experimental_spawn_scheduler फ़्लैग अब काम नहीं करता. यह डाइनैमिक तरीके से लागू होने की सुविधा चालू करता है और सभी स्मृति सहायकों के लिए dynamic को डिफ़ॉल्ट रणनीति के तौर पर सेट करता है. इसकी वजह से, अक्सर इस तरह की समस्याएं आती हैं.

समस्या का हल

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

अगर आपको standalone रणनीति का इस्तेमाल करके, डाइनैमिक तरीके से कार्रवाइयां करने में समस्याएं आ रही हैं, तो --experimental_local_lockfree_output के बिना कार्रवाइयां चलाकर देखें. इसके अलावा, अपने लोकल ऐक्शन को सैंडबॉक्स में चलाकर भी देखें. इससे आपके बिल्ड में थोड़ी देरी हो सकती है (अगर आपके पास Mac या Windows है, तो ऊपर देखें). हालांकि, इससे गड़बड़ियों की कुछ संभावित वजहें हट जाती हैं.