หน้านี้จะครอบคลุมวิธีใช้ผู้ปฏิบัติงานถาวร ประโยชน์ ข้อกำหนด และ วิธีที่คนทำงานส่งผลต่อการทำแซนด์บ็อกซ์
เวิร์กเกอร์แบบถาวรคือกระบวนการที่ทำงานต่อเนื่องซึ่งเซิร์ฟเวอร์ Bazel เริ่มต้นขึ้น ซึ่งทำหน้าที่เป็นตัวแฝงรอบเครื่องมือจริง (โดยทั่วไปคือคอมไพเลอร์) หรือเป็นเครื่องมือนั้นเอง เพื่อให้ได้รับประโยชน์จากผู้ปฏิบัติงานอย่างต่อเนื่อง เครื่องมือจะต้อง
รองรับการคอมไพล์ตามลำดับ และ Wrapper ต้องใช้การแปล
ระหว่าง API ของเครื่องมือกับรูปแบบคำขอ/คำตอบที่อธิบายไว้ด้านล่าง เรื่อง
ผู้ปฏิบัติงานอาจถูกเรียกโดยมีและไม่มีแฟล็ก --persistent_worker
ใน
บิลด์เดียวกัน และมีหน้าที่เริ่มต้นและพูดคุยกับ
รวมถึงการปิดระบบผู้ปฏิบัติงานเมื่อออกจากระบบ มีการกำหนดอินสแตนซ์ของผู้ปฏิบัติงานแต่ละรายการแล้ว
(แต่ไม่ได้ถูกแบ่งตาม) ไดเรกทอรีการทำงานที่แยกต่างหากภายใต้
<outputBase>/bazel-workers
การใช้เวิร์กเกอร์แบบถาวรเป็นกลยุทธ์การดําเนินการที่ช่วยลดค่าใช้จ่ายในการเริ่มต้น อนุญาตให้คอมไพล์ JIT ได้มากขึ้น และเปิดใช้การแคช เช่น ต้นไม้ไวยากรณ์นามธรรมในการดําเนินการ กลยุทธ์นี้ช่วยปรับปรุงประสิทธิภาพโดยการส่งคำขอหลายรายการไปยังกระบวนการที่ทำงานต่อเนื่อง
มีการใช้งานผู้ปฏิบัติงานถาวรสำหรับหลายภาษา รวมถึง Java สกาลา Kotlin และอื่นๆ
โปรแกรมที่ใช้รันไทม์ NodeJS สามารถใช้ @bazel/worker ไลบรารีตัวช่วยไปยัง ใช้โปรโตคอลผู้ปฏิบัติงาน
การใช้ผู้ปฏิบัติงานถาวร
Bazel 0.27 ขึ้นไปจะใช้เวิร์กเกอร์แบบถาวรโดยค่าเริ่มต้นเมื่อทำการบิลด์ แม้ว่าการดําเนินการจากระยะไกลจะมีความสำคัญมากกว่า สำหรับการดำเนินการที่ไม่รองรับเวิร์กเกอร์แบบถาวร Bazel จะเปลี่ยนไปใช้การเริ่มอินสแตนซ์เครื่องมือสําหรับการดำเนินการแต่ละรายการ คุณสามารถตั้งค่าบิลด์ให้ใช้ผู้ปฏิบัติงานแบบถาวรได้อย่างชัดเจนโดยการตั้งค่าworker
strategy สำหรับคําช่วยจําของเครื่องมือที่เกี่ยวข้อง แนวทางปฏิบัติแนะนำคือให้ระบุ local
เป็นกลยุทธ์สำรองสำหรับกลยุทธ์ worker
ดังตัวอย่างต่อไปนี้
bazel build //my:target --strategy=Javac=worker,local
การใช้กลยุทธ์สำหรับโหนดทำงานแทนกลยุทธ์ในเครื่องสามารถเพิ่มความเร็วในการคอมไพล์ได้อย่างมาก ทั้งนี้ขึ้นอยู่กับการใช้งาน สำหรับ Java บิลด์อาจเป็น 2-4 เร็วขึ้น บางครั้งก็เพิ่มขึ้นสำหรับการคอมไพล์แบบเพิ่มขึ้นเรื่อยๆ การคอมไพล์ Bazel จะเร็วขึ้นประมาณ 2.5 เท่าเมื่อมีโหนดทำงาน ดูรายละเอียดเพิ่มเติมได้ที่ส่วน "การเลือกจํานวนคนงาน"
หากมีสภาพแวดล้อมการสร้างระยะไกลที่ตรงกับสภาพแวดล้อมการสร้างในเครื่อง คุณสามารถใช้กลยุทธ์แบบไดนามิกเวอร์ชันทดลอง ซึ่งจะแข่งกันระหว่างการดําเนินการระยะไกลกับการดําเนินการของเวิร์กเกอร์ หากต้องการเปิดใช้โฆษณาแบบไดนามิก
ให้ส่งต่อ
--experimental_spawn_scheduler
แจ้ง กลยุทธ์นี้ช่วยให้พนักงานทำงานได้โดยอัตโนมัติ คุณจึงไม่จำเป็นต้อง
ระบุกลยุทธ์ worker
แต่คุณยังใช้ local
หรือ sandboxed
เป็น
วิดีโอสำรอง
การเลือกจํานวนผู้ปฏิบัติงาน
จำนวนอินสแตนซ์ผู้ปฏิบัติงานเริ่มต้นต่อคีย์สั้นคือ 4 แต่สามารถปรับได้ด้วยตัวเลือกworker_max_instances
แต่การใช้ CPU ที่มีและมาจากการใช้ CPU อย่างเหมาะสม
จำนวนการคอมไพล์ JIT และแคช Hit ที่คุณได้รับ ยิ่งพนักงานมากขึ้น ก็ยิ่งมีมากขึ้น
เป้าหมายจะจ่ายค่าใช้จ่ายในการเริ่มต้นในการเรียกใช้โค้ดที่ไม่ใช่ JITT และไม่พบปัญหาใดๆ
แคช หากมีเป้าหมายจำนวนไม่มากนัก การใช้ผู้ปฏิบัติงานเพียงรายเดียวอาจให้ผลลัพธ์ที่ดีที่สุดระหว่างความเร็วในการคอมไพล์กับการใช้ทรัพยากร (เช่น ดูปัญหา #8586)
Flag worker_max_instances
จะกำหนดจำนวนอินสแตนซ์ที่ทำงานสูงสุดต่อชุดคำสั่งสั้นๆ และ Flag (ดูด้านล่าง) ดังนั้นในระบบแบบผสม คุณอาจต้องใช้หน่วยความจำค่อนข้างมากหากใช้ค่าเริ่มต้น สำหรับบิลด์ที่เพิ่มขึ้นเรื่อยๆ ประโยชน์ของอินสแตนซ์ผู้ปฏิบัติงานหลายรายการจะยิ่งน้อยลง
กราฟนี้แสดงเวลาคอมไพล์จากรอยขีดข่วนสำหรับ Bazel (target
//src:bazel
) ในเวิร์กสเตชัน Intel Xeon 3.5 GHz Linux แบบไฮเปอร์เทรด 6 แกน
RAM ขนาด 64 GB สำหรับการกำหนดค่าผู้ปฏิบัติงานแต่ละรายการ จะมีการเรียกใช้บิลด์ที่สะอาด 5 รายการและ
คือค่าเฉลี่ยของ 4 ปัจจัยสุดท้าย
รูปที่ 1 กราฟการปรับปรุงประสิทธิภาพของบิลด์ที่สะอาด
สำหรับการกำหนดค่านี้ ผู้ปฏิบัติงาน 2 คนจะให้คอมไพล์ที่เร็วที่สุด แม้ว่าจะมีเพียง 14% เท่านั้น ที่ดีขึ้นเมื่อเทียบกับพนักงาน 1 คน ผู้ปฏิบัติงาน 1 คนเป็นตัวเลือกที่ดีหากคุณต้องการ ใช้หน่วยความจำน้อยลง
โดยทั่วไปแล้วการคอมไพล์แบบเพิ่มมักจะให้ประโยชน์มากกว่า บิลด์ที่สะอาดนั้นค่อนข้างเกิดขึ้นไม่บ่อยนัก แต่การเปลี่ยนแปลงไฟล์เดียวระหว่างการคอมไพล์นั้นเกิดขึ้นได้บ่อยครั้ง โดยเฉพาะในการพัฒนาแบบทดสอบเป็นแนวทาง ตัวอย่างข้างต้นยังมีการดำเนินการบางอย่างที่ไม่ใช่การแพ็กเกจ Java ซึ่งอาจบดบังเวลาคอมไพล์ที่เพิ่มขึ้น
การคอมไพล์ซอร์สโค้ด Java อีกครั้งเท่านั้น (//src/main/java/com/google/devtools/build/lib/bazel:BazelServer_deploy.jar
) หลังจากที่เปลี่ยนค่าคงที่สตริงภายในใน AbstractContainerizingSandboxedSpawn.java จะช่วยเพิ่มความเร็วขึ้น 3 เท่า (โดยเฉลี่ย 20 บิลด์แบบเพิ่มที่มี 1 บิลด์อุ่นเครื่องถูกทิ้ง)
รูปที่ 2 กราฟการปรับปรุงประสิทธิภาพของบิลด์ที่เพิ่มขึ้น
ความเร็วที่เพิ่มขึ้นจะขึ้นอยู่กับการเปลี่ยนแปลงที่เกิดขึ้น ความเร็วที่เพิ่มขึ้น 6 เท่าจะวัดได้ในสถานการณ์ข้างต้นเมื่อมีการเปลี่ยนแปลงค่าคงที่ที่ใช้กันโดยทั่วไป
การแก้ไขผู้ปฏิบัติงานแบบถาวร
คุณสามารถส่ง Flag --worker_extra_flag
เพื่อระบุ Flag เริ่มต้นให้กับ Worker โดยกำหนดคีย์เป็นคําช่วยจำ เช่น การใส่ --worker_extra_flag=javac=--debug
จะเปิดการแก้ไขข้อบกพร่องสําหรับ Javac เท่านั้น
คุณตั้งค่า Flag ของ Wrkr ได้เพียง 1 รายการต่อการใช้ Flag นี้ และสำหรับคําช่วยจํารายการเดียวเท่านั้น
ระบบไม่ได้สร้างผู้ปฏิบัติงานแยกกันสำหรับคําช่วยจําแต่ละรายการเท่านั้น แต่ยังสร้างสําหรับรูปแบบต่างๆ ของ Flag การเริ่มต้นด้วย ชุดค่าผสมของ mnemonic และ flag เริ่มต้นแต่ละชุดจะรวมกันเป็น WorkerKey
และระบบอาจสร้างผู้ปฏิบัติงานได้สูงสุด worker_max_instances
คนสำหรับ WorkerKey
แต่ละรายการ โปรดดูส่วนถัดไปเพื่อดูว่าการกำหนดค่าการดําเนินการระบุ Flag การตั้งค่าได้อย่างไร
คุณสามารถใช้ Flag --high_priority_workers
เพื่อระบุคําช่วยจําที่ควรเรียกใช้แทนคําช่วยจําที่มีลําดับความสําคัญเป็นปกติ วิธีนี้ช่วยจัดลำดับความสำคัญให้กับการดำเนินการที่สำคัญเสมอ
เส้นทาง หากมีผู้ปฏิบัติงานที่ต้องการส่งคำขอที่มีลำดับความสำคัญสูง 2 คนขึ้นไป
ป้องกันไม่ให้ผู้ปฏิบัติงานคนอื่นๆ ทำงาน คุณใช้ Flag นี้ซ้ำได้
การส่งผ่าน Flag --worker_sandboxing
จะทำให้คำขอของเวิร์กเกอร์แต่ละรายการใช้ไดเรกทอรีแซนด์บ็อกซ์แยกต่างหากสำหรับอินพุตทั้งหมด การตั้งค่า Sandbox จะใช้เวลาเพิ่มเติมอีกสักหน่อย โดยเฉพาะใน macOS แต่จะให้การรับประกันความถูกต้องที่ดีขึ้น
Flag --worker_quit_after_build
มีประโยชน์สําหรับการแก้ไขข้อบกพร่องและการสร้างโปรไฟล์เป็นหลัก Flag นี้จะบังคับให้เวิร์กเกอร์ทั้งหมดหยุดทำงานเมื่อบิวด์เสร็จแล้ว นอกจากนี้คุณยังสามารถ
--worker_verbose
ถึง
ได้รับผลลัพธ์เพิ่มเติมเกี่ยวกับสิ่งที่พนักงานกำลังทำอยู่ แฟล็กนี้จะแสดงในฟิลด์ verbosity
ใน WorkRequest
ซึ่งช่วยให้การติดตั้งใช้งาน Executor แสดงผลได้ละเอียดยิ่งขึ้นด้วย
ผู้ปฏิบัติงานจะจัดเก็บบันทึกไว้ในไดเรกทอรี <outputBase>/bazel-workers
เช่น /tmp/_bazel_larsrc/191013354bebe14fdddae77f2679c3ef/bazel-workers/worker-1-Javac.log
ชื่อไฟล์จะมีรหัสผู้ปฏิบัติงานและคําช่วยจํา เนื่องจากWorkerKey
อาจมีได้มากกว่า 1 รายการต่อคําช่วยจํา คุณจึงอาจเห็นไฟล์บันทึกมากกว่า worker_max_instances
ไฟล์สําหรับคําช่วยจําหนึ่งๆ
สำหรับบิลด์ Android โปรดดูรายละเอียดที่หน้าประสิทธิภาพบิลด์ Android
การใช้ผู้ปฏิบัติงานแบบถาวร
ดูข้อมูลเพิ่มเติมได้ที่หน้าการสร้างผู้ปฏิบัติงานถาวร ข้อมูลเกี่ยวกับวิธีสร้างพนักงาน
ตัวอย่างนี้แสดงการกำหนดค่า Starlark สำหรับเวิร์กเกอร์ที่ใช้ JSON
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) จากนั้นเวิร์กเกอร์จะดำเนินการและส่ง WorkResponse
ในรูปแบบ JSON ไปยัง Bazel บน stdout Bazel แล้ว
แยกวิเคราะห์คำตอบนี้และแปลงเป็น Pro ของ WorkResponse
ด้วยตนเอง ถึง
สื่อสารกับผู้ปฏิบัติงานที่เกี่ยวข้องโดยใช้ Protobuf ที่เข้ารหัสแบบไบนารีแทน
ระบบจะตั้งค่า JSON requires-worker-protocol
เป็น proto
ดังนี้
execution_requirements = {
"supports-workers" : "1" ,
"requires-worker-protocol" : "proto"
}
หากคุณไม่ได้รวม requires-worker-protocol
ไว้ในข้อกำหนดการเรียกใช้ ระบบของ Bazel จะกำหนดค่าเริ่มต้นให้การสื่อสารของเวิร์กเกอร์ใช้ protobuf
Bazel ดึง WorkerKey
มาจากเทคนิคการจำและแฟล็กที่แชร์ ดังนั้นถ้าสิ่งนี้
อนุญาตให้เปลี่ยนพารามิเตอร์ max_mem
ได้ พนักงานที่แยกต่างหากควร
สำหรับค่าแต่ละค่าที่ใช้ ซึ่งอาจทําให้หน่วยความจําถูกใช้มากเกินไปหากใช้ตัวแปรมากเกินไป
ปัจจุบันผู้ปฏิบัติงานแต่ละคนประมวลผลคำขอได้ทีละ 1 คำขอเท่านั้น การทดสอบ ฟีเจอร์ผู้ปฏิบัติงานมัลติเพล็กซ์อนุญาตให้ใช้ หากเครื่องมือที่สำคัญเป็นแบบมัลติเทรดและ Wrapper ได้รับการตั้งค่าเป็น เข้าใจเรื่องนี้
ในที่เก็บ GitHub นี้ คุณจะเห็นตัวอย่าง Wrapper ของ Worker ที่เขียนด้วย Java และ Python หากคุณ กำลังทำงานใน JavaScript หรือ TypeScript แพ็กเกจ@bazel/worker และ ตัวอย่างผู้ปฏิบัติงาน Nodejs อาจเป็นประโยชน์
ผู้ปฏิบัติงานส่งผลต่อแซนด์บ็อกซ์อย่างไร
การใช้กลยุทธ์ worker
โดยค่าเริ่มต้นจะไม่เรียกใช้การดําเนินการในแซนด์บ็อกซ์ ซึ่งคล้ายกับกลยุทธ์ local
คุณสามารถตั้งค่า Flag --worker_sandboxing
เพื่อเรียกใช้เวิร์กเกอร์ทั้งหมดภายในแซนด์บ็อกซ์ เพื่อให้แน่ใจว่าการเรียกใช้เครื่องมือแต่ละครั้งจะเห็นเฉพาะไฟล์อินพุตที่ควรจะเห็น เครื่องมือ
จึงอาจยังทำให้ข้อมูลระหว่างคำขอรั่วไหลภายใน เช่น ผ่าน
แคช กำลังใช้กลยุทธ์dynamic
กำหนดให้ผู้ปฏิบัติงานต้องถูกแซนด์บ็อกซ์
ระบบจะส่งข้อมูลสรุปไปพร้อมกับไฟล์อินพุตแต่ละไฟล์เพื่อให้ใช้แคชคอมไพเลอร์กับเวิร์กเกอร์ได้อย่างถูกต้อง ดังนั้นคอมไพเลอร์หรือรัปเปอร์จะตรวจสอบได้ว่าอินพุตยังคงใช้งานได้หรือไม่โดยไม่ต้องอ่านไฟล์
แม้ในขณะที่ใช้ไดเจสต์อินพุตเพื่อป้องกันการแคชที่ไม่พึงประสงค์ โดยมีการทำแซนด์บ็อกซ์ มีการตั้งค่าแซนด์บ็อกซ์ที่เข้มงวดน้อยกว่าแซนด์บ็อกซ์ทั้งหมด เนื่องจากเครื่องมืออาจ เก็บสถานะภายในอื่นๆ ที่ได้รับผลกระทบจากคำขอก่อนหน้าไว้
ผู้ปฏิบัติงาน Multiplex จะใช้แซนด์บ็อกซ์ได้ในกรณีที่ผู้ปฏิบัติงานรองรับเท่านั้น
และแซนด์บ็อกซ์นี้ต้องเปิดใช้
แยกต่างหากด้วย
แฟล็ก --experimental_worker_multiplex_sandboxing
ดูรายละเอียดเพิ่มเติมใน
เอกสารการออกแบบ)
อ่านเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับผู้ปฏิบัติงานแบบถาวรได้ที่