Bazel มีความซับซ้อนและทําสิ่งต่างๆ มากมายในระหว่างการบิลด์ ซึ่งบางอย่างอาจส่งผลต่อประสิทธิภาพการบิลด์ หน้านี้จะพยายามจับคู่แนวคิดบางอย่างของ Bazel กับผลกระทบต่อประสิทธิภาพการสร้าง แม้ว่าจะไม่ได้ครอบคลุมมากนัก แต่เราก็ได้รวมตัวอย่างวิธีตรวจหาปัญหาด้านประสิทธิภาพของบิลด์ผ่านการดึงข้อมูลเมตริก และสิ่งที่คุณสามารถทําเพื่อแก้ไขปัญหา เราหวังว่าคุณจะนําแนวคิดเหล่านี้ไปใช้ได้เมื่อตรวจสอบการถดถอยของประสิทธิภาพบิลด์
บิลด์แบบสะอาดกับบิลด์แบบเพิ่ม
บิลด์ที่สะอาดคือบิลด์ที่สร้างทุกอย่างตั้งแต่ต้น ส่วนบิลด์แบบเพิ่มจะนํางานที่ทำเสร็จแล้วบางส่วนมาใช้ซ้ำ
เราขอแนะนำให้ดูบิลด์ที่สะอาดและบิลด์ที่เพิ่มเข้ามาแยกกัน โดยเฉพาะเมื่อคุณรวบรวม / รวบรวมเมตริกที่ขึ้นอยู่กับสถานะของแคชของ Bazel (เช่น เมตริกขนาดคำขอบิลด์) นอกจากนี้ บิลด์เหล่านี้ยังแสดงถึงประสบการณ์การใช้งานที่แตกต่างกัน 2 แบบ เมื่อเทียบกับการเริ่มสร้างใหม่ตั้งแต่ต้น (ซึ่งใช้เวลานานกว่าเนื่องจากแคชเย็น) การสร้างแบบเพิ่มจะทําได้บ่อยกว่ามากเมื่อนักพัฒนาซอฟต์แวร์แก้ไขโค้ดซ้ำๆ (โดยปกติจะเร็วกว่าเนื่องจากแคชมักจะอุ่นอยู่แล้ว)
คุณสามารถใช้ช่อง CumulativeMetrics.num_analyses
ใน BEP เพื่อจัดประเภทบิลด์ได้ หากเป็น num_analyses <= 1
แสดงว่าเป็นบิลด์ที่สะอาด หากไม่ใช่ เราอาจจัดหมวดหมู่อย่างคร่าวๆ ว่าน่าจะเป็นบิลด์ที่เพิ่มเข้ามา เนื่องจากผู้ใช้อาจเปลี่ยนไปใช้ Flag หรือเป้าหมายอื่นซึ่งทำให้บิลด์สะอาด คำจำกัดความที่เข้มงวดยิ่งขึ้นของการเพิ่มจำนวนมีแนวโน้มที่จะอยู่ในรูปแบบของการเฮิวริสติก เช่น การดูจำนวนแพ็กเกจที่โหลด (PackageMetrics.packages_loaded
)
เมตริกการสร้างแบบกำหนดได้เพื่อใช้แทนประสิทธิภาพการสร้าง
การวัดประสิทธิภาพการสร้างอาจเป็นเรื่องยากเนื่องจากเมตริกบางรายการมีลักษณะที่ไม่แน่นอน (เช่น เวลา CPU ของ Bazel หรือเวลาคิวในคลัสเตอร์ระยะไกล) ดังนั้นจึงอาจเป็นประโยชน์ในการใช้เมตริกแบบกำหนดได้เพื่อประเมินปริมาณงานที่ Bazel ดำเนินการ ซึ่งจะส่งผลต่อประสิทธิภาพของ Bazel
ขนาดของคําขอสร้างอาจส่งผลต่อประสิทธิภาพการสร้างอย่างมาก บิลด์ขนาดใหญ่อาจแสดงถึงงานวิเคราะห์และการสร้างกราฟบิลด์มากขึ้น การเติบโตของบิลด์แบบทั่วไปเกิดขึ้นตามปกติเมื่อมีการพัฒนาการ เนื่องจากมีการเพิ่ม/สร้างการพึ่งพามากขึ้น ซึ่งทำให้เกิดความซับซ้อนมากขึ้นและทำให้การสร้างบิลด์มีราคาแพงขึ้น
เราแบ่งปัญหานี้ออกเป็นระยะต่างๆ ของการสร้างได้ และใช้เมตริกต่อไปนี้เป็นเมตริกพร็อกซีสําหรับงานที่ทําในแต่ละระยะ
PackageMetrics.packages_loaded
: จํานวนแพ็กเกจที่โหลดสําเร็จ การถดถอยที่นี่แสดงถึงงานเพิ่มเติมที่ต้องทำเพื่ออ่านและแยกวิเคราะห์ไฟล์ BUILD แต่ละไฟล์เพิ่มเติมในระยะการโหลดTargetMetrics.targets_configured
: แสดงจํานวนเป้าหมายและด้านต่างๆ ที่กําหนดค่าไว้ในบิลด์ การถดถอยแสดงถึงงานเพิ่มเติมในการสร้างและเรียกดูกราฟเป้าหมายที่กําหนดค่าไว้- ซึ่งมักเกิดจากการเพิ่มการพึ่งพาและต้องสร้างกราฟของ Closure แบบทรานซิทีฟ
- ใช้ cquery เพื่อค้นหาตำแหน่งที่อาจมีการเพิ่มข้อกำหนดใหม่
ActionSummary.actions_created
: แสดงการดำเนินการที่สร้างขึ้นในบิลด์ และการแสดงภาพเป็นภาพกราฟแสดงการทำงานเพิ่มเติมในการสร้างกราฟการดำเนินการ โปรดทราบว่าการดำเนินการนี้ยังรวมถึงการดำเนินการที่ไม่ได้ใช้ซึ่งอาจไม่ได้ดำเนินการด้วย- ใช้ aquery สำหรับการแก้ไขข้อบกพร่องแบบย้อนกลับ เราขอแนะนำให้เริ่มต้นด้วย
--output=summary
ก่อนเจาะลึกด้วย--skyframe_state
- ใช้ aquery สำหรับการแก้ไขข้อบกพร่องแบบย้อนกลับ เราขอแนะนำให้เริ่มต้นด้วย
ActionSummary.actions_executed
: จํานวนการดําเนินการ ภาวะถดถอยแสดงถึงงานเพิ่มเติมในการดําเนินการเหล่านี้โดยตรง- BEP จะเขียนสถิติการดําเนินการ
ActionData
ออกมา ซึ่งแสดงประเภทการดําเนินการที่ดําเนินการมากที่สุด โดยค่าเริ่มต้น ระบบจะรวบรวมประเภทการดําเนินการ 20 อันดับแรก แต่คุณสามารถส่ง--experimental_record_metrics_for_all_mnemonics
เพื่อรวบรวมข้อมูลนี้สําหรับประเภทการดําเนินการทั้งหมดที่ดำเนินการ - ซึ่งจะช่วยให้คุณทราบว่ามีการดำเนินการประเภทใดบ้าง (เพิ่มเติม)
- BEP จะเขียนสถิติการดําเนินการ
BuildGraphSummary.outputArtifactCount
: จํานวนรายการต่างๆ ที่สร้างขึ้นจากการดำเนินการที่ดำเนินการ- หากจํานวนการดําเนินการที่ดำเนินการไม่ได้เพิ่มขึ้น แสดงว่าอาจมีการเปลี่ยนแปลงการใช้งานกฎ
เมตริกเหล่านี้ทั้งหมดได้รับผลกระทบจากสถานะของแคชในเครื่อง คุณจึงต้องตรวจสอบว่าบิลด์ที่คุณดึงข้อมูลเมตริกเหล่านี้ออกมาเป็นบิลด์ที่สะอาด
เราพบว่าการถดถอยของเมตริกใดเมตริกหนึ่งเหล่านี้อาจมาพร้อมกับการถดถอยของเวลาจริง เวลาที่ใช้ CPU และการใช้หน่วยความจำ
การใช้ทรัพยากรในเครื่อง
Bazel ใช้ทรัพยากรต่างๆ ในเครื่องของคุณ (ทั้งสําหรับการวิเคราะห์กราฟการบิลด์และขับเคลื่อนการดําเนินการ รวมถึงสําหรับการดําเนินการในเครื่อง) ซึ่งอาจส่งผลต่อประสิทธิภาพ / ความพร้อมใช้งานของเครื่องในการบิลด์ รวมถึงงานอื่นๆ
เวลาที่ใช้
เมตริกที่อาจไวต่อสัญญาณรบกวนมากที่สุด (และอาจแตกต่างกันอย่างมากจากบิลด์หนึ่งๆ ไปอีกบิลด์หนึ่ง) ก็คือเวลา โดยเฉพาะเวลาจริง เวลา CPU และเวลาของระบบ คุณสามารถใช้ bazel-bench เพื่อดูการเปรียบเทียบสำหรับเมตริกเหล่านี้ และหากมี --runs
เพียงพอ คุณจะเพิ่มความสําคัญทางสถิติของการวัดได้
เวลาจริงคือเวลาที่ผ่านไปในชีวิตจริง
- หากเฉพาะเวลาจริงที่ลดลง เราขอแนะนำให้รวบรวมโปรไฟล์การติดตาม JSON และมองหาความแตกต่าง หรืออาจมีประสิทธิภาพมากกว่าหากตรวจสอบเมตริกอื่นๆ ที่ลดลง เนื่องจากอาจส่งผลต่อเวลาในการรับชม
เวลา CPU คือเวลาที่ CPU ใช้ในการเรียกใช้โค้ดของผู้ใช้
- หากเวลา CPU ลดลงในคอมมิต 2 รายการของโปรเจ็กต์ เราขอแนะนำให้รวบรวมโปรไฟล์ CPU ของ Starlark คุณควรใช้
--nobuild
เพื่อจำกัดการสร้างให้อยู่ในระยะการวิเคราะห์ด้วย เนื่องจากเป็นระยะที่ CPU ทำงานหนักมากที่สุด
- หากเวลา CPU ลดลงในคอมมิต 2 รายการของโปรเจ็กต์ เราขอแนะนำให้รวบรวมโปรไฟล์ CPU ของ Starlark คุณควรใช้
เวลาของระบบคือเวลาที่ CPU ใช้ในเคอร์เนล
- หากเวลาของระบบลดลง แสดงว่าเวลาดังกล่าวส่วนใหญ่เกี่ยวข้องกับ I/O เมื่อ Bazel อ่านไฟล์จากระบบไฟล์
โปรไฟล์การโหลดทั่วทั้งระบบ
เมื่อใช้ตัวเลือก --experimental_collect_load_average_in_profiler
ที่เปิดตัวใน Bazel 6.0 เครื่องมือวิเคราะห์ร่องรอย JSON จะรวบรวมค่าเฉลี่ยภาระของระบบระหว่างการเรียกใช้
รูปที่ 1 โปรไฟล์ที่มีค่าเฉลี่ยของการโหลดระบบ
ภาระงานสูงระหว่างการเรียกใช้ Bazel อาจบ่งชี้ว่า Bazel กำหนดเวลาการดำเนินการในเครื่องพร้อมกันมากเกินไป คุณอาจต้องพิจารณาเกี่ยวกับการปรับ --local_cpu_resources
และ --local_ram_resources
โดยเฉพาะอย่างยิ่งในสภาพแวดล้อมคอนเทนเนอร์ (อย่างน้อยจนกว่าจะมีการผสาน #16512)
การตรวจสอบการใช้หน่วยความจำของ Bazel
แหล่งที่มาหลักๆ 2 แห่งสําหรับดูการใช้งานหน่วยความจําของ Bazel คือ Bazel info
และ BEP
bazel info used-heap-size-after-gc
: ปริมาณหน่วยความจําที่ใช้เป็นไบต์หลังจากการเรียกใช้System.gc()
- Bazel bench มีข้อมูลเปรียบเทียบสำหรับเมตริกนี้ด้วย
- นอกจากนี้ ยังมี
peak-heap-size
,max-heap-size
,used-heap-size
และcommitted-heap-size
(ดูเอกสารประกอบ) แต่มีความเกี่ยวข้องน้อยกว่า
MemoryMetrics.peak_post_gc_heap_size
ของ BEP: ขนาดของฮีป JVM สูงสุดเป็นไบต์หลัง GC (กำหนดการตั้งค่า--memory_profile
ที่พยายามบังคับให้ GC แบบสมบูรณ์)
การใช้งานหน่วยความจําที่ลดลงมักจะเป็นผลมาจากเมตริกขนาดคําขอบิลด์ที่ลดลง ซึ่งมักเกิดจากการเพิ่มข้อกําหนดหรือการเปลี่ยนแปลงการใช้งานกฎ
หากต้องการวิเคราะห์พื้นที่หน่วยความจำของ Bazel ในรายละเอียดยิ่งขึ้น เราขอแนะนำให้ใช้เครื่องมือวิเคราะห์หน่วยความจำในตัวสำหรับกฎ
การสร้างโปรไฟล์หน่วยความจําของผู้ปฏิบัติงานแบบถาวร
แม้ว่าเวิร์กเกอร์แบบถาวรจะช่วยเร่งความเร็วการสร้างได้อย่างมาก (โดยเฉพาะสำหรับภาษาที่แปล) แต่การใช้หน่วยความจำก็อาจทำให้เกิดปัญหาได้ Bazel จะรวบรวมเมตริกเกี่ยวกับผู้ปฏิบัติงาน โดยเฉพาะอย่างยิ่ง ช่อง WorkerMetrics.WorkerStats.worker_memory_in_kb
จะบอกปริมาณหน่วยความจำที่ผู้ปฏิบัติงานใช้ (ตามคําช่วยจํา)
นอกจากนี้ เครื่องมือวิเคราะห์ร่องรอย JSON ยังรวบรวมการใช้งานหน่วยความจำของผู้ปฏิบัติงานแบบถาวรในระหว่างการเรียกใช้ด้วยการส่ง Flag --experimental_collect_system_network_usage
(ใหม่ใน Bazel 6.0)
รูปที่ 2 โปรไฟล์ที่มีการใช้งานหน่วยความจำของผู้ปฏิบัติงาน
การลดค่าของ --worker_max_instances
(ค่าเริ่มต้น 4) อาจช่วยในการลดปริมาณหน่วยความจําที่ผู้ปฏิบัติงานแบบถาวรใช้ เรากําลังทํางานอย่างเต็มที่เพื่อทำให้เครื่องมือจัดการทรัพยากรและเครื่องมือจัดตารางเวลาของ Bazel ฉลาดขึ้น เพื่อที่จะได้ไม่ต้องปรับแต่งแบบละเอียดบ่อยนักในอนาคต
การตรวจสอบการรับส่งข้อมูลในเครือข่ายสําหรับบิลด์ระยะไกล
ในการดําเนินการจากระยะไกล Bazel จะดาวน์โหลดอาร์ติแฟกต์ที่สร้างขึ้นจากการดำเนินการ แบนด์วิดท์ของเครือข่ายจึงอาจส่งผลต่อประสิทธิภาพของบิลด์
หากใช้การเรียกใช้จากระยะไกลสําหรับบิลด์ คุณอาจต้องพิจารณาตรวจสอบการเข้าชมเครือข่ายระหว่างการเรียกใช้โดยใช้ NetworkMetrics.SystemNetworkStats
proto จาก BEP (ต้องส่ง --experimental_collect_system_network_usage
)
นอกจากนี้ โปรไฟล์การติดตาม JSON ยังช่วยให้คุณดูการใช้งานเครือข่ายทั้งระบบตลอดกระบวนการสร้างได้ด้วยการส่ง Flag --experimental_collect_system_network_usage
(ใหม่ใน Bazel 6.0)
รูปที่ 3 โปรไฟล์ที่มีการใช้งานเครือข่ายทั้งระบบ
การใช้เครือข่ายที่สูงแต่ค่อนข้างคงที่เมื่อใช้การดําเนินการจากระยะไกลอาจบ่งชี้ว่าเครือข่ายเป็นคอขวดในการสร้าง หากยังไม่ได้ใช้ ให้ลองเปิด "สร้างโดยไม่ใช้ Bytes" โดยส่ง --remote_download_minimal
ซึ่งจะช่วยเร่งความเร็วการสร้างโดยหลีกเลี่ยงการดาวน์โหลดอาร์ติแฟกต์ระดับกลางที่ไม่จำเป็น
อีกตัวเลือกหนึ่งคือการกำหนดค่าแคชดิสก์ในเครื่องเพื่อประหยัดแบนด์วิดท์การดาวน์โหลด