परमानेंट वर्कर

यह पेज बताता है कि स्थायी कर्मचारियों का इस्तेमाल कैसे किया जाए, इसके फ़ायदे, ज़रूरी शर्तें क्या हों, और सैंडबॉक्स सैंडबॉक्स को कैसे प्रभावित करते हैं.

स्थायी वर्कर, लंबे समय से चल रही बेज़ेल सर्वर की प्रोसेस है, जो असल में टूल (आम तौर पर कंपाइलर) के आस-पास रैपर के तौर पर काम करती है या खुद टूल होती है. स्थायी कर्मचारियों से फ़ायदा पाने के लिए, टूल को कंपाइलेशन का क्रम तय करना होगा. साथ ही, रैपर को टूल's एपीआई और नीचे दिए गए अनुरोध/रिस्पॉन्स फ़ॉर्मैट के बीच अनुवाद करना होगा. एक ही बिल्ड में एक ही वर्कर को --persistent_worker फ़्लैग के साथ और उसके बिना भी कॉल किया जा सकता है. साथ ही, टूल को शुरू करने और उससे बात करने के साथ-साथ घर से बाहर निकलने पर वर्कर को बंद करने की ज़िम्मेदारी भी इसी की होती है. हर वर्कर इंस्टेंस को असाइन किया जाता है (लेकिन, <outputBase>/bazel-workers में काम करने के लिए एक अलग डायरेक्ट्री बनाई जाती है.

लगातार काम करने वाले वर्कर का इस्तेमाल करना, एक्ज़ीक्यूशन की रणनीति होती है जो स्टार्ट-अप का काम कम कर देती है. इससे, JIT कंपाइलेशन ज़्यादा होता है. साथ ही, कार्रवाई के दौरान ऐब्स्ट्रैक्ट सिंटैक्स ट्री के लिए कैश मेमोरी को चालू किया जा सकता है. यह रणनीति लंबे समय तक चलने वाली प्रक्रिया में, एक से ज़्यादा अनुरोध भेजकर इन सुधारों को पूरा करती है.

लगातार चलने वाली वर्कर, कई भाषाओं के लिए लागू की जाती हैं. इनमें Java, Scala, Kotlin वगैरह शामिल हैं.

NodeJS रनटाइम का इस्तेमाल करने वाले प्रोग्राम, वर्कर प्रोटोकॉल को लागू करने के लिए, @bazer/worker हेल्पर लाइब्रेरी का इस्तेमाल कर सकते हैं.

स्थायी वर्कर का इस्तेमाल करना

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

bazel build //my:target --strategy=Javac=worker,local

स्थानीय रणनीति के बजाय वर्कर के लिए रणनीति का इस्तेमाल करने से, कंपाइलेशन की स्पीड बढ़ सकती है. यह इस बात पर निर्भर करता है कि इसे कैसे लागू किया गया है. Java के लिए, बिल्ड 2-4 गुना तेज़ी से हो सकते हैं, कभी-कभी ज़्यादा कंपाइलेशन के लिए ज़्यादा. बैज़ेल को कंपाइल करने में करीब 2.5 गुना तेज़ी आती है. ज़्यादा जानकारी के लिए, &&tt;कर्मियों की संख्या चुनना" सेक्शन देखें.

अगर आपके पास ऐसा रिमोट बिल्ड एनवायरमेंट भी है जो आपके बिल्ड एनवायरमेंट से मेल खाता है, तो आप प्रयोग के तौर पर डाइनैमिक रणनीति का इस्तेमाल कर सकते हैं. यह रणनीति रिमोट तरीके से काम करती है और वर्कर को एक्ज़ीक्यूशन देती है. डाइनैमिक रणनीति चालू करने के लिए, --प्रयोगल_spawn_शेड्यूलर फ़्लैग करें फ़्लैग करें. यह रणनीति अपने-आप वर्कर को चालू करती है, इसलिए worker रणनीति को तय करने की कोई ज़रूरत नहीं है. हालांकि, आप local या sandboxed को फ़ॉलबैक के तौर पर इस्तेमाल कर सकते हैं.

कामकाजी लोगों की संख्या चुनना

हर वर्ण के हिसाब से वर्कर वर्कर इंस्टेंस की संख्या 4 है, लेकिन इसमें worker_max_instances फ़्लैग किया जा सकता है. उपलब्ध सीपीयू का इस्तेमाल करने और आपको मिलने वाले JIT कंपाइलेशन और कैश हिट के इस्तेमाल में भारी अंतर है. अगर आप ज़्यादा वर्कर काम करते हैं, तो ज़्यादा टारगेट जो गैर-Jitit कोड चलाने और शुरुआत की कैश मेमोरी को प्रोसेस करने के लिए, शुरू होने वाले खर्च के पैसे चुकाएगा. अगर आपके पास बनाने के लिए बहुत कम टारगेट हैं, तो कोई एक वर्कर कंपाइलेशन की स्पीड और रिसॉर्स के इस्तेमाल के बीच, सबसे अच्छा संतुलन बना सकता है. उदाहरण के लिए, समस्या #8586 देखें. worker_max_instances फ़्लैग, हर मेमोरियल और फ़्लैग सेट के लिए वर्कर इंस्टेंस की ज़्यादा से ज़्यादा संख्या सेट करता है (नीचे देखें), इसलिए अगर आप डिफ़ॉल्ट मान रखते हैं, तो मिले-जुले सिस्टम में बहुत ज़्यादा मेमोरी का इस्तेमाल कर सकते हैं. इंक्रीमेंटल बिल्ड के लिए कई वर्कर इंस्टेंस के फ़ायदे और भी छोटे हैं.

यह ग्राफ़, 6-कोर हाइपर-थ्रेड वाले Intel Xeon 3.5 गीगाहर्ट्ज़ Linux वर्कस्टेशन पर 64 जीबी रैम के साथ, बैजल (टारगेट //src:bazel) के लिए स्क्रैच करने के समय को दिखाता है. हर वर्कर कॉन्फ़िगरेशन के लिए, पांच क्लीन बिल्ड चलाए जाते हैं और पिछले चार बिल्ड का औसत लिया जाता है.

क्लीन बिल्ड के परफ़ॉर्मेंस सुधार का ग्राफ़

पहला डायग्राम. साफ़ बिल्ड की परफ़ॉर्मेंस में सुधार करने का ग्राफ़.

इस कॉन्फ़िगरेशन के लिए, दो वर्कर सबसे तेज़ कंपाइल देते हैं, जबकि एक वर्कर के मुकाबले इसमें सिर्फ़ 14% का सुधार हुआ है. अगर आप कम मेमोरी का इस्तेमाल करना चाहते हैं, तो एक वर्कर एक अच्छा विकल्प है.

आम तौर पर, इंक्रीमेंटल (बढ़ने वाले) कंपाइलेशन की मदद से और भी ज़्यादा फ़ायदा मिलता है. साफ़ बिल्ड बहुत ही कम होते हैं, लेकिन कंपाइल के बीच एक फ़ाइल में बदलाव करना आम बात है, खास तौर पर टेस्ट के आधार पर डेवलपमेंट में. ऊपर दिए गए उदाहरण में Java की कुछ ऐसी पैकेजिंग भी हैं जो Java की कंपाइल होने में लगने वाले समय को बढ़ा सकती हैं.

सिर्फ़ Java सोर्स को फिर से कंपाइल करना (//src/main/java/com/google/devtools/build/lib/bazel:BazelServer_deploy.jar) AbstractContainerizedSpawn.java में इंटरनल स्ट्रिंग कॉन्सटेंट बदलने के बाद, इससे तीन गुना स्पीड मिलती है (एक वॉर्मअप बिल्ड के साथ 20 इंक्रीमेंटल बिल्ड औसत होते हैं):

इंक्रीमेंटल बिल्ड में परफ़ॉर्मेंस का सुधार दिखाने वाला ग्राफ़

दूसरा डायग्राम. इंक्रिमेंटल बिल्ड की परफ़ॉर्मेंस में सुधार करने का ग्राफ़.

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

लगातार काम करने वाले लोगों में बदलाव करना

आप कर्मचारियों को स्टार्ट-अप फ़्लैग के बारे में बताने के लिए --worker_extra_flag फ़्लैग कर सकते हैं. इसके अलावा, फ़्लैग रंग का इस्तेमाल करके भी सब कुछ सही तरीके से किया जा सकता है. उदाहरण के लिए, --worker_extra_flag=javac=--debug को पास करने से, सिर्फ़ Javac के लिए डीबग करना चालू हो जाता है. इस फ़्लैग का इस्तेमाल करने पर, हर वर्कर के लिए सिर्फ़ एक फ़्लैग सेट किया जा सकता है. साथ ही, सिर्फ़ एक स्मारक के लिए, ऐसा किया जा सकता है. कर्मचारियों को सिर्फ़ हर याद के लिए अलग से नहीं बनाया जाता, बल्कि उनके शुरुआती झंडे के लिए भी अलग-अलग लेबल बनाए जाते हैं. मेमोरी और स्टार्ट-अप फ़्लैग के हर कॉम्बिनेशन को WorkerKey में जोड़ा जाता है. साथ ही, हर WorkerKey के लिए worker_max_instances तक वर्कर बनाए जा सकते हैं. कार्रवाई कॉन्फ़िगरेशन, सेट-अप फ़्लैग को कैसे तय कर सकता है, इसके लिए अगला सेक्शन देखें.

आप --high_priority_workers फ़्लैग का इस्तेमाल करके, उस प्राथमिकता की पहचान कर सकते हैं जिसे सामान्य प्राथमिकता वाले इनबॉक्स में प्राथमिकता दी जानी चाहिए. इससे उन कार्रवाइयों को प्राथमिकता देने में मदद मिल सकती है जो हमेशा अहम पाथ में ही होती हैं. अगर दो या उससे ज़्यादा प्राथमिकता वाले वर्कर, अनुरोधों को लागू कर रहे हैं, तो सभी दूसरे वर्कर चलने से रोक दिए जाते हैं. इस फ़्लैग का इस्तेमाल कई बार किया जा सकता है.

--worker_sandboxing फ़्लैग फ़्लैग को पास करने से हर वर्कर अनुरोध को उसके सभी इनपुट के लिए एक अलग सैंडबॉक्स डायरेक्ट्री का इस्तेमाल करने देता है. सैंडबॉक्स को सेट अप करने में कुछ और समय लगता है, खास तौर पर 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 को कंपाइल करने का अनुरोध इस तरह दिखेगा:

arguments: [ "-g", "-source", "1.5", "Foo.java" ]
inputs: [
  {path: "symlinkfarm/input1" digest: "d49a..." },
  {path: "symlinkfarm/input2", digest: "093d..."},
]

वर्कर को यह stdin पर नई-डीलिमिटेड JSON फ़ॉर्मैट में मिलता है, क्योंकि requires-worker-protocol को JSON पर सेट किया गया है. इसके बाद वर्कर काम करता है, और JSON का फ़ॉर्मैट WorkResponse को उसके soutout पर बैज़ेल के लिए भेजता है. फिर बेज़ल इस जवाब को पार्स करता है और मैन्युअल तरीके से इसे WorkResponse प्रोटो में बदल देता है. JSON के बजाय बाइनरी-एन्कोड किए गए प्रोटोबफ़ का इस्तेमाल करके, जुड़े वर्कर से संपर्क करने के लिए, requires-worker-protocol को proto पर सेट किया जाएगा, जैसे:

  execution_requirements = {
    "supports-workers" : "1" ,
    "requires-worker-protocol" : "proto"
  }

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

बेजल को WorkerKey प्रचलित नाम और शेयर किए गए फ़्लैग से लिया गया है. इसलिए, अगर इस कॉन्फ़िगरेशन में max_mem पैरामीटर को बदलने की अनुमति दी गई है, तो इस्तेमाल की गई हर वैल्यू के लिए एक अलग वर्कर काम करेगा. इससे, बहुत ज़्यादा स्टोरेज का इस्तेमाल होने पर बहुत ज़्यादा मेमोरी का इस्तेमाल हो सकता है.

फ़िलहाल, हर वर्कर एक बार में सिर्फ़ एक अनुरोध प्रोसेस कर सकता है. अगर टेस्ट करने के लिए मल्टीप्लेक्स वर्कर सुविधा कई थ्रेड का इस्तेमाल करती है, तो बुनियादी टूल एक से ज़्यादा थ्रेड में होगा और रैपर इसे समझने के लिए सेट अप होगा.

इस GitHub repo में, आप Java में और Python में लिखे गए वर्कर रैपर देख सकते हैं. अगर आप JavaScript या TypeScript में काम कर रहे हैं, तो @bazer/worker पैकेज और nodejs वर्कर उदाहरण से मदद मिल सकती है.

कर्मचारियों का सैंडबॉक्स पर क्या असर पड़ता है?

डिफ़ॉल्ट तौर पर, worker रणनीति का इस्तेमाल करने से, localसैंडबॉक्स की तरह ही, सैंडबॉक्स में कार्रवाई नहीं होती. सैंडबॉक्स में सभी वर्कर को चलाने के लिए, --worker_sandboxing फ़्लैग सेट किया जा सकता है. इससे, यह पक्का होता है कि टूल के हर एक ऐक्शन को सिर्फ़ वे इनपुट फ़ाइलें दिखती हैं जो #39 के हिसाब से होनी चाहिए. टूल, आंतरिक रूप से अनुरोधों के बीच जानकारी को अब भी कैश के ज़रिए लीक कर सकता है. dynamic रणनीति का इस्तेमाल करने पर कर्मियों को सैंडबॉक्स होना ज़रूरी है.

वर्कर के साथ कंपाइलर कैश मेमोरी का सही इस्तेमाल करने के लिए, हर इनपुट फ़ाइल के साथ डाइजेस्ट पास किया जाता है. इस तरह कंपाइलर या रैपर यह जांच कर सकता है कि इनपुट फ़ाइल को पढ़े बिना भी मान्य है या नहीं.

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

मल्टीप्लेक्स वर्कर को सिर्फ़ तब सैंडबॉक्स किया जा सकता है, जब वर्कर लागू करने की सुविधा के साथ काम करता हो और इस सैंडबॉक्स को --experimental_worker_multiplex_sandboxing फ़्लैग से अलग से चालू किया जाना चाहिए. (डिज़ाइन दस्तावेज़ में ज़्यादा जानकारी देखें).

इसके बारे में और पढ़ें

लगातार काम करने वाले कर्मचारियों के बारे में ज़्यादा जानकारी के लिए, ये देखें: