डाइनैमिक तरीके से प्रोग्राम चलाना, 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 है, तो ऊपर देखें). हालांकि, इससे गड़बड़ियों की कुछ संभावित वजहें हट जाती हैं.