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