इस पेज में बताया गया है कि स्थायी वर्कर का इस्तेमाल कैसे किया जाता है, उनके फ़ायदे और ज़रूरी शर्तें क्या हैं. साथ ही, सैंडबॉक्सिंग पर इसके असर के बारे में भी जानकारी दी गई है.
स्थायी वर्कर, एक लंबे समय तक चलने वाली प्रोसेस है. यह Bazel सर्वर शुरू करता है. यह वास्तविक टूल (आम तौर पर, कंपाइलर) के चारों ओर रैपर की तरह काम करता है या खुद टूल भी है. लगातार काम करने वाले कर्मचारियों से फ़ायदा पाने के लिए यह ज़रूरी है कि
टूल, कंपाइलेशन के क्रम में काम करे. साथ ही, रैपर को टूल के एपीआई और नीचे दिए गए अनुरोध/रिस्पॉन्स फ़ॉर्मैट के बीच अनुवाद
करना चाहिए. हो सकता है कि एक ही कर्मचारी को, उसी बिल्ड में --persistent_worker
फ़्लैग के साथ और उसके बिना कॉल किया जाए. साथ ही, वह सही तरीके से टूल शुरू करने, उससे बात करने, और बाहर निकलने पर कर्मचारियों को बंद करने के लिए ज़िम्मेदार होगा. हर वर्कर इंस्टेंस को <outputBase>/bazel-workers
के तहत एक अलग डायरेक्ट्री असाइन की जाती है, लेकिन इसका इस्तेमाल नहीं किया जाता.
स्थायी वर्कर का इस्तेमाल करना एक एक्ज़ीक्यूशन रणनीति है. यह स्टार्ट-अप ओवरहेड को कम करती है, ज़्यादा जेआईटी कंपाइलेशन की अनुमति देती है, और कार्रवाई के एक्ज़ीक्यूशन में ऐब्सट्रैक्ट सिंटैक्स ट्री के लिए कैश मेमोरी को चालू करती है. यह रणनीति लंबे समय तक चलने वाली प्रोसेस में कई अनुरोध भेजकर इन सुधारों को हासिल करती है.
स्थायी वर्कर का इस्तेमाल कई भाषाओं के लिए किया जाता है. इनमें Java, Scala, Kotlin वगैरह शामिल हैं.
NodeJS रनटाइम का इस्तेमाल करने वाले प्रोग्राम, वर्कर प्रोटोकॉल को लागू करने के लिए, @bazel/worker हेल्पर लाइब्रेरी का इस्तेमाल कर सकते हैं.
स्थायी कर्मचारियों का इस्तेमाल करना
Bazel 0.27 और उसके बाद वाले वर्शन
बिल्ड को एक्ज़ीक्यूट करते समय, डिफ़ॉल्ट रूप से स्थायी वर्कर का इस्तेमाल करते हैं. हालांकि, रिमोट
एक्ज़ीक्यूशन को प्राथमिकता दी जाती है. जो कार्रवाइयां लगातार काम करने वाले कर्मचारियों की मदद नहीं करतीं, उनके लिए
बेज़ेल हर कार्रवाई के लिए टूल इंस्टेंस शुरू करना शुरू कर देता है. लागू टूल के लिए worker
रणनीति सेट करके, स्थायी कर्मचारियों का इस्तेमाल करने के लिए, साफ़ तौर पर बिल्ड सेट किया जा सकता है. सबसे सही तरीका यह है कि इस उदाहरण में, worker
रणनीति के लिए local
को फ़ॉलबैक के तौर पर तय करना शामिल है:
bazel build //my:target --strategy=Javac=worker,local
स्थानीय रणनीति के बजाय वर्कर रणनीति का इस्तेमाल करने से, कंपाइलेशन की रफ़्तार बढ़ सकती है. Java के लिए, बिल्ड 2–4 गुना तेज़ हो सकते हैं, कभी-कभी इंक्रीमेंटल कंपाइलेशन के लिए भी ऐसा किया जा सकता है. Bazel को कंपाइल करने में कर्मचारियों की तुलना में करीब 2.5 गुना तेज़ी आती है. ज़्यादा जानकारी के लिए, "कर्मचारियों की संख्या चुनना" सेक्शन देखें.
अगर आपके पास ऐसा रिमोट बिल्ड एनवायरमेंट है जो आपके लोकल बिल्ड एनवायरमेंट से मेल खाता है, तो एक्सपेरिमेंट के तौर पर उपलब्ध डाइनैमिक रणनीति का इस्तेमाल किया जा सकता है. यह रणनीति, रिमोट तौर पर एक्ज़ीक्यूशन और वर्कर को एक्ज़ीक्यूट करने के लिए प्रेरित करती है. डाइनैमिक रणनीति को चालू करने के लिए, --experimental_spawn_scheduler
फ़्लैग को पास करें. यह रणनीति अपने-आप कर्मचारियों को चालू करती है. इसलिए, worker
रणनीति बताने की ज़रूरत नहीं है. हालांकि, आप अब भी local
या sandboxed
को फ़ॉलबैक के तौर पर इस्तेमाल कर सकते हैं.
कर्मचारियों की संख्या चुनी जा रही है
हर मेनेमोनिक के लिए वर्कर इंस्टेंस की डिफ़ॉल्ट संख्या 4 है, लेकिन इसे
worker_max_instances
फ़्लैग के साथ बदला जा सकता है. उपलब्ध सीपीयू का अच्छी तरह इस्तेमाल किया जा सकता है. हालांकि, आपको मिलने वाली JIT और कैश मेमोरी हिट के बीच संतुलन बनाना ज़रूरी है. जितने ज़्यादा वर्कर होंगे, ज़्यादा टारगेट ऑडियंस को नॉन-जितेड कोड चलाने और कोल्ड कैश मेमोरी का इस्तेमाल करने से शुरू होने वाले
खर्चों का फ़ायदा मिलेगा. अगर आपके पास बनाने के लिए कम टारगेट हैं, तो कोई एक वर्कर, कंपाइलेशन की स्पीड और रिसॉर्स के इस्तेमाल के बीच बेहतर संतुलन बना सकता है (उदाहरण के लिए, समस्या #8586 देखें.
worker_max_instances
फ़्लैग, हर स्नेमोनिक और फ़्लैग सेट के लिए वर्कर इंस्टेंस की ज़्यादा से ज़्यादा संख्या सेट करता है (नीचे देखें). इसलिए, डिफ़ॉल्ट वैल्यू को बनाए रखने पर मिले-जुले सिस्टम में आप बहुत ज़्यादा मेमोरी का इस्तेमाल कर सकते हैं. इंंक्रीमेंटल बिल्ड के लिए,
एक से ज़्यादा वर्कर इंस्टेंस का फ़ायदा और भी कम होता है.
यह ग्राफ़, 64 जीबी रैम वाले, 6-कोर हाइपर-थ्रेड Intel Xeon 3.5 GHz Linux वर्कस्टेशन
पर Bazel (टारगेट
//src:bazel
) के लिए, स्क्रैच के समय का कंपाइलेशन दिखाता है. हर वर्कर कॉन्फ़िगरेशन के लिए, पांच क्लीन बिल्ड चलाए जाते हैं और आखिरी चार में से औसतन चार बिल्ड लिए जाते हैं.
पहला डायग्राम. क्लीन बिल्ड की परफ़ॉर्मेंस में सुधार करने का ग्राफ़.
इस कॉन्फ़िगरेशन के लिए, दो वर्कर सबसे तेज़ कंपाइल करते हैं. हालांकि, एक वर्कर की तुलना में सिर्फ़ 14% सुधार मिलता है. अगर आपको कम मेमोरी का इस्तेमाल करना है, तो एक वर्कर बढ़िया विकल्प है.
आम तौर पर, वीडियो के बढ़ते हुए कंपाइलेशन से ज़्यादा फ़ायदा मिलता है. क्लीन बिल्ड आम तौर पर बहुत कम होता है, लेकिन कंपाइलेशन के बीच किसी एक फ़ाइल को बदलना आम बात है. खास तौर पर, टेस्ट की मदद से किए जाने वाले डेवलपमेंट में. ऊपर दिए गए उदाहरण में कुछ नॉन-Java पैकेजिंग कार्रवाइयां भी शामिल हैं जो इंक्रीमेंटल कंपाइलेशन टाइम को छिपा सकती हैं.
सिर्फ़ जावा सोर्स को फिर से कंपाइल करने से
(//src/main/java/com/google/devtools/build/lib/bazel:BazelServer_deploy.jar
)
AbstractContainerizingSandboxedSpawn.java में
इंटरनल स्ट्रिंग कॉन्सटेंट को बदलने के बाद
तीन गुना स्पीड-अप मिलता है (एक वॉर्मअप बिल्ड के साथ औसतन 20 इंक्रीमेंटल बिल्ड
अस्वीकार किए जाते हैं):
दूसरा डायग्राम. इंक्रीमेंटल बिल्ड की परफ़ॉर्मेंस में सुधार करने का ग्राफ़.
स्पीड में होने वाला बदलाव, किए जा रहे बदलाव के हिसाब से तय होगा. फ़ैक्टर 6 की स्पीड के बढ़ने को ऊपर की स्थिति में तब मापा जाता है, जब आम तौर पर इस्तेमाल होने वाले कॉन्सटेंट को बदला जाता है.
स्थायी वर्कर में बदलाव करना
कर्मचारियों को स्टार्ट-अप फ़्लैग तय करने के लिए, --worker_extra_flag
फ़्लैग पास किया जा सकता है. यह मिनिमनिक की मदद से होता है. उदाहरण के लिए,
--worker_extra_flag=javac=--debug
को पास करने से, सिर्फ़ Javac के लिए डीबग करने की सुविधा चालू होती है.
इस फ़्लैग का इस्तेमाल करने पर सिर्फ़ एक वर्कर फ़्लैग सेट किया जा सकता है.
काम करने वाले लोगों को न सिर्फ़ हर स्मोनिक के लिए अलग से बनाया जाता है, बल्कि उनके स्टार्ट-अप फ़्लैग के अलग-अलग तरह के वर्शन के लिए भी बनाया जाता है. मेनेमोनिक और स्टार्ट-अप फ़्लैग के हर कॉम्बिनेशन को WorkerKey
में मिलाया जाता है. साथ ही, हर WorkerKey
के लिए, ज़्यादा से ज़्यादा worker_max_instances
वर्कर बनाए जा सकते हैं. ऐक्शन कॉन्फ़िगरेशन, सेट-अप फ़्लैग के बारे में कैसे बता सकता है, यह जानने के लिए अगला सेक्शन देखें.
--high_priority_workers
फ़्लैग का इस्तेमाल करके, यह तय किया जा सकता है कि सामान्य प्राथमिकता वाले मेनेमॉनिक को प्राथमिकता दी जाए या नहीं. इससे उन कार्रवाइयों को प्राथमिकता देने में मदद मिल सकती है जो हमेशा अहम पाथ में रहती हैं. अगर दो या उससे ज़्यादा ज़्यादा प्राथमिकता वाले वर्कर, अनुरोधों को पूरा करते हैं, तो दूसरे सभी वर्कर को चलाने से रोक दिया जाता है. इस फ़्लैग का इस्तेमाल एक से ज़्यादा बार किया जा सकता है.
--worker_sandboxing
फ़्लैग पास करने पर, हर वर्कर के अनुरोध को उसके सभी इनपुट के लिए, एक अलग सैंडबॉक्स डायरेक्ट्री का इस्तेमाल करने में मदद मिलती है. sandbox को सेट अप करने में थोड़ा ज़्यादा समय लगता है, खास तौर पर
macOS पर. हालांकि, इससे बेहतर काम करने की गारंटी मिलती है.
--worker_quit_after_build
फ़्लैग मुख्य रूप से, डीबग करने और प्रोफ़ाइल बनाने के लिए इस्तेमाल होता है. यह फ़्लैग एक बिल्ड पूरा हो जाने के बाद
सभी वर्कर को बंद होने के लिए मजबूर करता है. कर्मचारी क्या काम कर रहे हैं, इस बारे में ज़्यादा आउटपुट पाने के लिए
--worker_verbose
भी पास किया जा सकता है. यह फ़्लैग WorkRequest
के verbosity
फ़ील्ड में दिखता है. इससे वर्कर को लागू करने की जानकारी ज़्यादा शब्दों में दी जाती है.
वर्कर, अपने लॉग को <outputBase>/bazel-workers
डायरेक्ट्री में सेव करते हैं, जैसे कि /tmp/_bazel_larsrc/191013354bebe14fdddae77f2679c3ef/bazel-workers/worker-1-Javac.log
.
फ़ाइल के नाम में वर्कर आईडी और मेनेमोनिक शामिल हैं. हर महीने के लिए एक WorkerKey
से ज़्यादा लॉग फ़ाइलें हो सकती हैं. इसलिए, आपको दिए गए मिनेमोनिक के लिए worker_max_instances
से ज़्यादा लॉग फ़ाइलें दिख सकती हैं.
Android बिल्ड के लिए, Android बिल्ड परफ़ॉर्मेंस पेज पर जानकारी देखें.
स्थायी कर्मचारी लागू करना
कर्मचारी बनाने के तरीकों के बारे में ज़्यादा जानकारी के लिए, स्थायी कर्मचारी बनाना पेज देखें.
यह उदाहरण JSON का इस्तेमाल करने वाले वर्कर के लिए Starlark कॉन्फ़िगरेशन दिखाता है:
args_file = ctx.actions.declare_file(ctx.label.name + "_args_file")
ctx.actions.write(
output = args_file,
content = "\n".join(["-g", "-source", "1.5"] + ctx.files.srcs),
)
ctx.actions.run(
mnemonic = "SomeCompiler",
executable = "bin/some_compiler_wrapper",
inputs = inputs,
outputs = outputs,
arguments = [ "-max_mem=4G", "@%s" % args_file.path],
execution_requirements = {
"supports-workers" : "1", "requires-worker-protocol" : "json" }
)
इस परिभाषा के साथ, इस कार्रवाई का पहला इस्तेमाल,
कमांड लाइन /bin/some_compiler -max_mem=4G --persistent_worker
को लागू करने से होगा. Foo.java
को कंपाइल करने का अनुरोध
ऐसा दिखेगा:
ध्यान दें: प्रोटोकॉल बफ़र स्पेसिफ़िकेशन में "स्नेक केस" (request_id
) का इस्तेमाल किया जाता है. हालांकि, JSON प्रोटोकॉल में "कैमल केस" (requestId
) का इस्तेमाल किया जाता है. इस दस्तावेज़ में, हम JSON के उदाहरणों में ऊंट केस का इस्तेमाल करेंगे, लेकिन प्रोटोकॉल पर ध्यान दिए बिना फ़ील्ड के बारे में बात करते समय, हम स्नेक केस का इस्तेमाल करेंगे.
{
"arguments": [ "-g", "-source", "1.5", "Foo.java" ]
"inputs": [
{ "path": "symlinkfarm/input1", "digest": "d49a..." },
{ "path": "symlinkfarm/input2", "digest": "093d..." },
],
}
वर्कर को stdin
पर यह न्यूलाइन-डीलिमिटेड JSON फ़ॉर्मैट में मिलता है (क्योंकि
requires-worker-protocol
को JSON पर सेट किया गया है). इसके बाद, वर्कर कार्रवाई करता है और
अपने स्टडआउट पर Bazel को JSON फ़ॉर्मैट वाला WorkResponse
भेजता है. इसके बाद, Bazel
इस रिस्पॉन्स को पार्स करता है और मैन्युअल तरीके से WorkResponse
प्रोटो में बदल देता है. JSON के बजाय बाइनरी कोड में बदले गए प्रोटोबफ़ का इस्तेमाल करके,
असोसिएट किए गए वर्कर से संपर्क करने के लिए,
requires-worker-protocol
को proto
पर सेट किया जाएगा, जैसे:
execution_requirements = {
"supports-workers" : "1" ,
"requires-worker-protocol" : "proto"
}
अगर लागू करने की शर्तों में requires-worker-protocol
को शामिल नहीं किया जाता है, तो
Bazel, प्रोटोबफ़ का इस्तेमाल करने के लिए, वर्कर कम्यूनिकेशन को डिफ़ॉल्ट रूप से सेट करेगा.
Bazel, WorkerKey
को स्मेनिक और शेयर किए गए फ़्लैग से लेता है. इसलिए, अगर इस कॉन्फ़िगरेशन में max_mem
पैरामीटर को बदलने की अनुमति है, तो इस्तेमाल की गई हर वैल्यू के लिए एक अलग वर्कर बनाया जाएगा. बहुत ज़्यादा वैरिएशन इस्तेमाल करने पर,
मेमोरी का बहुत ज़्यादा इस्तेमाल हो सकता है.
फ़िलहाल, हर वर्कर एक बार में सिर्फ़ एक अनुरोध को प्रोसेस कर सकता है. अगर प्रयोग के तौर पर इस्तेमाल किया जाने वाला मल्टिप्लेक्स वर्कर सुविधा, एक से ज़्यादा थ्रेड का इस्तेमाल करने की अनुमति देती है, तो ऐसा तब होता है, जब मुख्य टूल मल्टीथ्रेड किया गया हो और रैपर को सेट अप किया गया हो.
GitHub के इस रेपो में, आप Java और Python में लिखे गए वर्कर रैपर के उदाहरण देख सकते हैं. अगर JavaScript या टाइपस्क्रिप्ट में काम किया जा रहा है, तो @bazel/worker पैकेज और nodejs वर्कर का उदाहरण आपके लिए मददगार हो सकता है.
वर्कर, सैंडबॉक्सिंग पर कैसे असर डालते हैं?
डिफ़ॉल्ट रूप से worker
रणनीति का इस्तेमाल करने पर, कार्रवाई local
रणनीति की तरह sandbox में नहीं चलती. आपके पास सैंडबॉक्स में सभी कर्मचारियों को चलाने के लिए, --worker_sandboxing
फ़्लैग सेट करने का विकल्प होता है. इससे यह पक्का हो जाता है कि टूल का हर एक्ज़ीक्यूशन सिर्फ़ वही इनपुट फ़ाइलें देखता है जिनमें उसकी ज़रूरत थी. यह टूल अब भी अंदरूनी अनुरोधों के बीच जानकारी लीक कर सकता है, उदाहरण के लिए कैश मेमोरी के ज़रिए. dynamic
रणनीति का इस्तेमाल करने के लिए,
कर्मचारियों को सैंडबॉक्स करना ज़रूरी है.
वर्कर के साथ कंपाइलर कैश का सही इस्तेमाल करने के लिए, हर इनपुट फ़ाइल के साथ एक डाइजेस्ट पास किया जाता है. इसलिए, कंपाइलर या रैपर यह जांच कर सकता है कि फ़ाइल को पढ़े बिना, इनपुट अब भी मान्य है या नहीं.
यहां तक कि जब अनचाहे कैशिंग से बचने के लिए इनपुट डाइजेस्ट का इस्तेमाल किया जाता है, तब भी सैंडबॉक्स किए गए कर्मचारी, शुद्ध सैंडबॉक्स की तुलना में कम सख्त सैंडबॉक्सिंग देते हैं, क्योंकि टूल एक ऐसी अंदरूनी स्थिति को बनाए रख सकता है जो पिछले अनुरोधों से प्रभावित हुई हो.
मल्टीप्लेक्स वर्कर सिर्फ़ तब सैंडबॉक्स किए जा सकते हैं, जब वर्कर को लागू करने पर यह काम करे. यह सैंडबॉक्सिंग, --experimental_worker_multiplex_sandboxing
फ़्लैग के साथ अलग से चालू होनी चाहिए. ज़्यादा जानकारी के लिए डिज़ाइन दस्तावेज़ देखें).
इसके बारे में और पढ़ें
लगातार काम करने वाले कर्मचारियों के बारे में ज़्यादा जानकारी के लिए, देखें:
- लंबे समय तक काम करने वाले मूल कर्मचारी ब्लॉग पोस्ट
- हस्केल लागू करने से जुड़ी जानकारी
- माइक मोरर्टी की ब्लॉग पोस्ट
- Bzel के साथ फ़्रंट एंड डेवलपमेंट: Asana के साथ काम करने वाले एंगुलर/टाइपस्क्रिप्ट और परसिस्टेंट वर्कर
- Bazel की रणनीतियों के बारे में जानकारी
- बैज़ेल-डिस्कस ईमेल पाने वाले लोगों की सूची में शामिल कर्मचारियों की रणनीति के बारे में जानकारी देने वाली चर्चा