बिल्ड परफ़ॉर्मेंस का विश्लेषण

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

क्लीन बिल्ड बनाम इंक्रीमेंटल बिल्ड

क्लीन बिल्ड में, सब कुछ नए सिरे से बनाया जाता है. वहीं, इंक्रीमेंटल बिल्ड में, पहले से पूरा किए गए कुछ काम का फिर से इस्तेमाल किया जाता है.

हमारा सुझाव है कि क्लीन और इंक्रीमेंटल बिल्ड को अलग-अलग देखें. खास तौर पर, तब जब आपको ऐसी मेट्रिक इकट्ठा / एग्रीगेट करनी हों जो Bazel की कैश मेमोरी की स्थिति पर निर्भर करती हैं. उदाहरण के लिए, बिल्ड अनुरोध के साइज़ की मेट्रिक. ये दोनों, उपयोगकर्ता अनुभव को भी अलग-अलग तरीके से दिखाते हैं. शुरुआत से एक नया बिल्ड बनाने में ज़्यादा समय लगता है, क्योंकि इसमें कोल्ड कैश का इस्तेमाल होता है. इसके मुकाबले, इंक्रीमेंटल बिल्ड ज़्यादा बार होते हैं, क्योंकि डेवलपर कोड को बार-बार दोहराते हैं. आम तौर पर, इंक्रीमेंटल बिल्ड तेज़ी से होते हैं, क्योंकि कैश पहले से ही वार्म होता है.

BEP में CumulativeMetrics.num_analyses फ़ील्ड का इस्तेमाल करके, बिल्ड को कैटगरी में बांटा जा सकता है. अगर num_analyses <= 1, तो यह एक क्लीन बिल्ड है. अगर ऐसा नहीं है, तो हम इसे इंक्रीमेंटल बिल्ड के तौर पर कैटगरी में रख सकते हैं. ऐसा इसलिए, क्योंकि उपयोगकर्ता अलग-अलग फ़्लैग या अलग-अलग टारगेट पर स्विच कर सकता है. इससे क्लीन बिल्ड मिल सकता है. इंक्रीमेंटैलिटी की ज़्यादा सटीक परिभाषा, अनुमान के तौर पर दी जा सकती है. उदाहरण के लिए, लोड किए गए पैकेज की संख्या (PackageMetrics.packages_loaded) देखना.

बिल्ड की परफ़ॉर्मेंस के लिए प्रॉक्सी के तौर पर, डिटरमिनिस्टिक बिल्ड मेट्रिक

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

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

इस समस्या को अलग-अलग चरणों में बांटा जा सकता है. साथ ही, हर चरण में किए गए काम के लिए, यहां दी गई मेट्रिक का इस्तेमाल प्रॉक्सी मेट्रिक के तौर पर किया जा सकता है:

  1. PackageMetrics.packages_loaded: लोड किए गए पैकेज की संख्या. यहां रिग्रेशन का मतलब है कि लोडिंग फ़ेज़ में हर अतिरिक्त BUILD फ़ाइल को पढ़ने और पार्स करने के लिए, ज़्यादा काम करना होगा.

    • ऐसा अक्सर डिपेंडेंसी जोड़ने और उनके ट्रांज़िटिव क्लोज़र को लोड करने की वजह से होता है.
    • query / cquery का इस्तेमाल करके, यह पता लगाएं कि नई डिपेंडेंसी कहां जोड़ी गई हैं.
  2. TargetMetrics.targets_configured: यह बिल्ड में कॉन्फ़िगर किए गए टारगेट और पहलुओं की संख्या दिखाता है. रिग्रेशन का मतलब है कि कॉन्फ़िगर किए गए टारगेट ग्राफ़ को बनाने और उसे पार करने में ज़्यादा काम करना होगा.

    • ऐसा अक्सर डिपेंडेंसी जोड़ने और उनके ट्रांज़िटिव क्लोज़र का ग्राफ़ बनाने की वजह से होता है.
    • cquery का इस्तेमाल करके, यह पता लगाएं कि नई डिपेंडेंसी कहां जोड़ी गई हैं.
  3. ActionSummary.actions_created: यह बिल्ड में बनाए गए ऐक्शन को दिखाता है. साथ ही, रिग्रेशन, ऐक्शन ग्राफ़ बनाने में ज़्यादा काम को दिखाता है. ध्यान दें कि इसमें ऐसी कार्रवाइयां भी शामिल हैं जिनका इस्तेमाल नहीं किया गया है और जिन्हें शायद लागू नहीं किया गया है.

    • रिग्रेशन को डीबग करने के लिए, aquery का इस्तेमाल करें; हमारा सुझाव है कि --skyframe_state का इस्तेमाल करने से पहले, --output=summary का इस्तेमाल करें.
  4. ActionSummary.actions_executed: इससे, कार्रवाइयों की संख्या का पता चलता है. रिग्रेशन से पता चलता है कि इन कार्रवाइयों को पूरा करने में ज़्यादा समय लगा.

    • बीईपी, कार्रवाई के आंकड़ों के बारे में बताता है ActionData. इससे पता चलता है कि किस तरह की कार्रवाई सबसे ज़्यादा की गई. डिफ़ॉल्ट रूप से, यह सबसे ज़्यादा बार किए गए 20 तरह के ऐक्शन का डेटा इकट्ठा करता है. हालांकि, --experimental_record_metrics_for_all_mnemonics को पास करके, सभी तरह के ऐक्शन का डेटा इकट्ठा किया जा सकता है.
    • इससे आपको यह पता लगाने में मदद मिलेगी कि कौनसी कार्रवाइयां (इसके अलावा) की गई थीं.
  5. BuildGraphSummary.outputArtifactCount: एक्ज़ीक्यूट की गई कार्रवाइयों से बनाए गए आर्टफ़ैक्ट की संख्या.

    • अगर लागू की गई कार्रवाइयों की संख्या नहीं बढ़ी है, तो हो सकता है कि नियम लागू करने के तरीके में बदलाव किया गया हो.

ये सभी मेट्रिक, लोकल कैश मेमोरी की स्थिति से प्रभावित होती हैं. इसलिए, आपको यह पक्का करना होगा कि जिन बिल्ड से ये मेट्रिक निकाली गई हैं वे क्लीन बिल्ड हों.

हमने देखा है कि इनमें से किसी भी मेट्रिक में गिरावट आने पर, वॉल टाइम, सीपीयू टाइम, और मेमोरी के इस्तेमाल में भी गिरावट आ सकती है.

स्थानीय संसाधनों का इस्तेमाल

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

बिताया गया समय

शायद समय ऐसी मेट्रिक है जिस पर नॉइज़ का सबसे ज़्यादा असर पड़ता है. साथ ही, यह एक बिल्ड से दूसरे बिल्ड में काफ़ी अलग हो सकती है. खास तौर पर - वॉल टाइम, सीपीयू टाइम, और सिस्टम टाइम. इन मेट्रिक के लिए मानदंड पाने के लिए, bazel-bench का इस्तेमाल किया जा सकता है. साथ ही, --runs की ज़रूरी संख्या के साथ, मेज़रमेंट के आंकड़ों के महत्व को बढ़ाया जा सकता है.

  • वॉल टाइम, असल में बीता हुआ समय होता है.

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

    • अगर दो प्रोजेक्ट कमिट के बीच सीपीयू टाइम कम हो जाता है, तो हमारा सुझाव है कि आप Starlark सीपीयू प्रोफ़ाइल इकट्ठा करें. आपको शायद --nobuild का इस्तेमाल भी करना चाहिए, ताकि बिल्ड को विश्लेषण के चरण तक सीमित किया जा सके. ऐसा इसलिए, क्योंकि सीपीयू का ज़्यादातर काम इसी चरण में होता है.
  • सिस्टम टाइम, सीपीयू का वह समय होता है जो कर्नल में बिताया जाता है.

    • अगर सिस्टम का समय कम हो जाता है, तो यह ज़्यादातर I/O से जुड़ा होता है. ऐसा तब होता है, जब Bazel आपके फ़ाइल सिस्टम से फ़ाइलें पढ़ता है.

सिस्टम-वाइड लोड प्रोफ़ाइलिंग

Bazel 6.0 में पेश किए गए --experimental_collect_load_average_in_profiler फ़्लैग का इस्तेमाल करके, JSON ट्रेस प्रोफ़ाइलर, इनवोकेशन के दौरान सिस्टम के लोड का औसत इकट्ठा करता है.

ऐसी प्रोफ़ाइल जिसमें सिस्टम लोड का औसत शामिल हो

पहली इमेज. ऐसी प्रोफ़ाइल जिसमें सिस्टम लोड का औसत शामिल हो.

Bazel को शुरू करने के दौरान ज़्यादा लोड होने का मतलब यह हो सकता है कि Bazel, आपकी मशीन के लिए एक साथ कई लोकल ऐक्शन शेड्यूल करता है. आपको --local_cpu_resources और --local_ram_resources को अडजस्ट करने के बारे में सोचना चाहिए. खास तौर पर, कंटेनर एनवायरमेंट में (कम से कम तब तक, जब तक #16512 मर्ज नहीं हो जाता).

Bazel की मेमोरी के इस्तेमाल को मॉनिटर करना

Bazel की मेमोरी के इस्तेमाल की जानकारी पाने के दो मुख्य सोर्स हैं: Bazel info और बीईपी.

  • bazel info used-heap-size-after-gc: System.gc() को कॉल करने के बाद, इस्तेमाल की गई मेमोरी की मात्रा बाइट में.

    • Bazel bench इस मेट्रिक के लिए भी बेंचमार्क उपलब्ध कराता है.
    • इसके अलावा, peak-heap-size, max-heap-size, used-heap-size, और committed-heap-size (दस्तावेज़ देखें) भी उपलब्ध हैं, लेकिन ये कम काम के हैं.
  • बीईपी’s MemoryMetrics.peak_post_gc_heap_size: GC के बाद बाइट में पीक JVM हीप साइज़ (इसके लिए, --memory_profile सेट करना ज़रूरी है, जो फ़ुल GC को लागू करने की कोशिश करता है).

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

हमारा सुझाव है कि नियमों के लिए, बिल्ट-इन मेमोरी प्रोफ़ाइलर का इस्तेमाल करें. इससे Bazel की मेमोरी के इस्तेमाल का ज़्यादा बारीकी से विश्लेषण किया जा सकता है.

पर्सिस्टेंट वर्कर्स के स्टोरेज के इस्तेमाल की प्रोफ़ाइल बनाना

स्थायी वर्कर, बिल्ड को तेज़ी से पूरा करने में मदद कर सकते हैं. खास तौर पर, इंटरप्रेट की गई भाषाओं के लिए. हालांकि, इनकी मेमोरी फ़ुटप्रिंट की वजह से समस्या हो सकती है. Bazel, अपने वर्कर के बारे में मेट्रिक इकट्ठा करता है. खास तौर पर, WorkerMetrics.WorkerStats.worker_memory_in_kb फ़ील्ड से पता चलता है कि वर्कर, नेमोनिक के हिसाब से कितनी मेमोरी का इस्तेमाल करते हैं.

JSON ट्रेस प्रोफ़ाइलर, इनवोकेशन के दौरान पर्सिस्टेंट वर्कर की मेमोरी के इस्तेमाल की जानकारी भी इकट्ठा करता है. इसके लिए, वह --experimental_collect_system_network_usage फ़्लैग (Bazel 6.0 में नया) पास करता है.

ऐसी प्रोफ़ाइल जिसमें वर्कर के मेमोरी इस्तेमाल करने की जानकारी शामिल हो

दूसरी इमेज. ऐसी प्रोफ़ाइल जिसमें वर्कर के मेमोरी इस्तेमाल करने की जानकारी शामिल हो.

--worker_max_instances (डिफ़ॉल्ट रूप से 4) की वैल्यू कम करने से, परसिस्टेंट वर्कर के इस्तेमाल की जाने वाली मेमोरी को कम करने में मदद मिल सकती है. हम Bazel के रिसॉर्स मैनेजर और शेड्यूलर को बेहतर बनाने के लिए लगातार काम कर रहे हैं, ताकि आने वाले समय में इस तरह के फ़ाइन ट्यूनिंग की ज़रूरत कम पड़े.

रिमोट बिल्ड के लिए नेटवर्क ट्रैफ़िक को मॉनिटर करना

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

अगर बिल्ड के लिए रिमोट एक्ज़ीक्यूशन का इस्तेमाल किया जा रहा है, तो हो सकता है कि आपको इनवॉकेशन के दौरान नेटवर्क ट्रैफ़िक की निगरानी करनी पड़े. इसके लिए, बीईपी से NetworkMetrics.SystemNetworkStats प्रोटो का इस्तेमाल करें. इसके लिए, --experimental_collect_system_network_usage पास करना ज़रूरी है.

इसके अलावा, JSON ट्रेस प्रोफ़ाइल की मदद से, पूरे सिस्टम में नेटवर्क के इस्तेमाल की जानकारी देखी जा सकती है. इसके लिए, आपको --experimental_collect_system_network_usage फ़्लैग पास करना होगा. यह फ़्लैग, Bazel 6.0 में नया है.

ऐसी प्रोफ़ाइल जिसमें पूरे सिस्टम में नेटवर्क के इस्तेमाल की जानकारी शामिल हो

तीसरी इमेज. ऐसी प्रोफ़ाइल जिसमें पूरे सिस्टम में नेटवर्क के इस्तेमाल की जानकारी शामिल हो.

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

डाउनलोड बैंडविथ को बचाने के लिए, लोकल डिस्क कैश मेमोरी को कॉन्फ़िगर किया जा सकता है.