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

डाइनैमिक एक्सीक्यूशन 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's बेहतरीन ब्लॉग पोस्ट देखें

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

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

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

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