डाइनैमिक तरीके से प्रोग्राम चलाना, Bazel की एक सुविधा है. इसमें एक ही कार्रवाई को एक साथ लोकल और रिमोट तरीके से चलाया जाता है. इसके लिए, पहले पूरी होने वाली शाखा के आउटपुट का इस्तेमाल किया जाता है और दूसरी शाखा को रद्द कर दिया जाता है. यह रिमोट बिल्ड सिस्टम की बेहतर परफ़ॉर्मेंस और/या बड़े शेयर किए गए कैश मेमोरी को, स्थानीय तौर पर बिल्ड करने में लगने वाले कम समय के साथ जोड़ता है. इससे क्लीन और इंक्रीमेंटल, दोनों तरह के बिल्ड के लिए बेहतर परफ़ॉर्मेंस मिलती है.
इस पेज पर, डाइनैमिक तरीके से प्रोग्राम चलाने की सुविधा को चालू करने, उसे ट्यून करने, और डीबग करने का तरीका बताया गया है. अगर आपने लोकल और रिमोट, दोनों तरह के प्रोग्राम को सेट अप किया है और बेहतर परफ़ॉर्मेंस के लिए 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 के लिए डिफ़ॉल्ट सेटिंग बदल जाती है. रिमोट वर्शन भी इसी तरह काम करता है. दोनों फ़्लैग को एक से ज़्यादा बार इस्तेमाल किया जा सकता है. अगर कोई कार्रवाई स्थानीय तौर पर नहीं की जा सकती, तो उसे सामान्य तौर पर रिमोट से किया जाता है. इसके उलट, अगर कोई कार्रवाई रिमोट से नहीं की जा सकती, तो उसे स्थानीय तौर पर किया जाता है.
अगर आपके रिमोट सिस्टम में कैश मेमोरी है, तो --dynamic_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
को डिफ़ॉल्ट रणनीति के तौर पर सेट करता है. इसकी वजह से, अक्सर इस तरह की समस्याएं आती हैं.
परफ़ॉर्मेंस
डाइनैमिक तरीके से लागू करने का यह तरीका, स्थानीय और रिमोट तौर पर उपलब्ध संसाधनों के आधार पर तय किया जाता है. इससे, पूरी परफ़ॉर्मेंस को बेहतर बनाने के लिए, कुछ अतिरिक्त संसाधनों को खर्च करना सही होता है. हालांकि, ज़्यादा संसाधनों का इस्तेमाल करने से, Bazel या उस पर काम करने वाली मशीन धीमी हो सकती है. इसके अलावा, किसी रिमोट सिस्टम पर अचानक ज़्यादा दबाव पड़ सकता है. डाइनैमिक तरीके से लागू होने की प्रोसेस में बदलाव करने के लिए, कई विकल्प उपलब्ध हैं:
--dynamic_local_execution_delay
, रिमोट शाखा के शुरू होने के बाद, किसी स्थानीय शाखा को शुरू होने में कुछ मिलीसेकंड की देरी करता है. हालांकि, ऐसा सिर्फ़ तब होता है, जब मौजूदा बिल्ड के दौरान रिमोट कैश मेमोरी में हिट हुआ हो. इससे, रिमोट कैश मेमोरी का फ़ायदा पाने वाले बिल्ड, स्थानीय संसाधनों को बर्बाद नहीं करते. ऐसा तब होता है, जब ज़्यादातर आउटपुट कैश मेमोरी में मिल सकते हैं. कैश मेमोरी की क्वालिटी के आधार पर, इसे कम करने से बिल्ड की स्पीड बेहतर हो सकती है. हालांकि, इसके लिए ज़्यादा लोकल रिसॉर्स का इस्तेमाल करना पड़ सकता है.
--experimental_dynamic_local_load_factor
, संसाधन मैनेज करने का एक बेहतर विकल्प है. फ़िलहाल, इसे एक्सपेरिमेंट के तौर पर आज़माया जा रहा है. इसकी वैल्यू 0 से 1 के बीच होती है. 0 का मतलब है कि यह सुविधा बंद है.
अगर इसे 0 से ज़्यादा वैल्यू पर सेट किया जाता है, तो जब कई ऐक्शन शेड्यूल किए जाने के लिए इंतज़ार कर रहे हों, तब Bazel स्थानीय तौर पर शेड्यूल की गई ऐक्शन की संख्या में बदलाव करता है. इसे 1 पर सेट करने से, --local_cpu_resources
के मुताबिक उपलब्ध सीपीयू के हिसाब से उतनी कार्रवाइयां शेड्यूल की जा सकती हैं. कम वैल्यू से, शेड्यूल की गई कार्रवाइयों की संख्या कम हो जाती है, क्योंकि ज़्यादा कार्रवाइयां चलाने के लिए उपलब्ध होती हैं. यह सुनने में अटपटा लग सकता है, लेकिन जब कई ऐक्शन चलाए जा रहे हों, तो अच्छे रिमोट सिस्टम के साथ, लोकल ऐक्शन से ज़्यादा फ़ायदा नहीं मिलता. साथ ही, लोकल सीपीयू का इस्तेमाल रिमोट ऐक्शन को मैनेज करने में किया जाता है.
--experimental_dynamic_slow_remote_time
, लोकल शाखाओं को शुरू करने को प्राथमिकता देता है,
जब रिमोट शाखा कम से कम इस अवधि से चल रही हो. आम तौर पर, सबसे हाल ही में शेड्यूल की गई कार्रवाई को प्राथमिकता दी जाती है, क्योंकि इसकी रेस जीतने की संभावना सबसे ज़्यादा होती है. हालांकि, अगर रिमोट सिस्टम कभी-कभी हैंग हो जाता है या ज़्यादा समय लेता है, तो इससे किसी बिल्ड को आगे बढ़ने में मदद मिल सकती है. यह सुविधा डिफ़ॉल्ट रूप से चालू नहीं होती, क्योंकि इससे रिमोट सिस्टम से जुड़ी समस्याएं छिप सकती हैं. इन समस्याओं को ठीक करना ज़रूरी है. अगर यह विकल्प चालू किया जाता है, तो अपने रिमोट सिस्टम की परफ़ॉर्मेंस को मॉनिटर करना न भूलें.
--experimental_dynamic_ignore_local_signals
का इस्तेमाल, किसी सिग्नल की वजह से स्थानीय स्पैन के बाहर निकलने पर, रिमोट शाखा को कंट्रोल करने के लिए किया जा सकता है. यह मुख्य रूप से, वर्कर्स के लिए संसाधनों की सीमाओं के साथ काम आता है (--experimental_worker_memory_limit_mb
,
--experimental_worker_sandbox_hardening
, और
--experimental_sandbox_memory_limit_mb
). इन सीमाओं के तहत, वर्कर्स की प्रोसेस को तब बंद किया जा सकता है, जब वे बहुत ज़्यादा संसाधनों का इस्तेमाल करें.
JSON ट्रेस प्रोफ़ाइल में, परफ़ॉर्मेंस से जुड़े कई ग्राफ़ होते हैं. इनसे, परफ़ॉर्मेंस और संसाधन के इस्तेमाल के बीच के समझौते को बेहतर बनाने के तरीकों का पता चलता है.
समस्या का हल
डाइनैमिक तरीके से लागू करने से जुड़ी समस्याएं, आसानी से नहीं दिखती हैं और उन्हें डीबग करना मुश्किल होता है. ऐसा इसलिए, क्योंकि ये समस्याएं सिर्फ़ स्थानीय और रिमोट तरीके से लागू करने के कुछ खास कॉम्बिनेशन में दिखती हैं.
--debug_spawn_scheduler
, डाइनैमिक एक्सीक्यूशन सिस्टम से अतिरिक्त आउटपुट जोड़ता है, जिससे इन समस्याओं को डीबग करने में मदद मिल सकती है. --dynamic_local_execution_delay
फ़्लैग और रिमोट बनाम लोकल जॉब की संख्या में भी बदलाव किया जा सकता है, ताकि समस्याओं को दोहराना आसान हो.
अगर आपको standalone
रणनीति का इस्तेमाल करके, डाइनैमिक तरीके से कार्रवाइयां करने में समस्याएं आ रही हैं, तो --experimental_local_lockfree_output
के बिना कार्रवाइयां करने की कोशिश करें या अपने लोकल ऐक्शन को सैंडबॉक्स में चलाएं. इससे आपके बिल्ड में थोड़ी देरी हो सकती है (अगर आपके पास Mac या Windows है, तो ऊपर देखें). हालांकि, इससे गड़बड़ियों की कुछ संभावित वजहें हट जाती हैं.