Bazel มีความซับซ้อนและทำสิ่งต่างๆ มากมายในระหว่างการบิลด์ ซึ่งบางอย่างอาจส่งผลต่อประสิทธิภาพการบิลด์ หน้านี้พยายามเชื่อมโยงแนวคิดบางอย่างของ Bazel กับผลกระทบต่อประสิทธิภาพการบิลด์ แม้จะไม่ครอบคลุมทั้งหมด แต่เราได้รวมตัวอย่างวิธีตรวจหาปัญหาด้านประสิทธิภาพการบิลด์ผ่านการแยกเมตริกและสิ่งที่คุณทำได้เพื่อแก้ไขปัญหา เราหวังว่าคุณจะนำแนวคิดเหล่านี้ไปใช้ได้เมื่อตรวจสอบการถดถอยของประสิทธิภาพการบิลด์
การบิลด์แบบ Clean กับแบบ Incremental
การบิลด์แบบ Clean คือการบิลด์ทุกอย่างตั้งแต่ต้น ส่วนการบิลด์แบบ Incremental จะใช้ซ้ำบางส่วนที่ทำเสร็จแล้ว
เราขอแนะนำให้ดูการบิลด์แบบ Clean และแบบ Incremental แยกกัน โดยเฉพาะอย่างยิ่งเมื่อ คุณรวบรวม / รวมเมตริกที่ขึ้นอยู่กับสถานะของ แคชของ Bazel (เช่น เมตริกขนาดคำขอบิลด์ ) นอกจากนี้ การบิลด์ทั้ง 2 แบบยังแสดงถึงประสบการณ์ของผู้ใช้ที่แตกต่างกันด้วย การบิลด์แบบ Incremental เกิดขึ้นบ่อยกว่ามากเมื่อนักพัฒนาซอฟต์แวร์ทำการวนซ้ำในโค้ด (โดยปกติจะเร็วกว่าเนื่องจากแคชพร้อมใช้งานอยู่แล้ว) เมื่อเทียบกับการเริ่มต้นการบิลด์แบบ Clean ตั้งแต่ต้น (ซึ่งใช้เวลานานกว่าเนื่องจากแคชยังไม่พร้อมใช้งาน)
คุณสามารถใช้ฟิลด์ CumulativeMetrics.num_analyses ใน BEP เพื่อจัดประเภทการบิลด์ หาก num_analyses <= 1 จะเป็นการบิลด์แบบ Clean แต่หากไม่เป็นเช่นนั้น เราจะจัดประเภทการบิลด์นั้นเป็นแบบ Incremental โดยทั่วไป
ซึ่งผู้ใช้อาจเปลี่ยนไปใช้แฟล็กหรือเป้าหมายอื่นที่ทำให้เกิดการบิลด์แบบ Clean
คำจำกัดความที่เข้มงวดมากขึ้นของการบิลด์แบบ Incremental น่าจะต้องอยู่ในรูปแบบของฮิวริสติก เช่น การดูจำนวนแพ็กเกจที่โหลด (PackageMetrics.packages_loaded)
เมตริกการบิลด์เชิงกำหนดเป็นตัวแทนของประสิทธิภาพการบิลด์
การวัดประสิทธิภาพการบิลด์อาจทำได้ยากเนื่องจากลักษณะที่ไม่แน่นอนของเมตริกบางอย่าง (เช่น เวลา CPU ของ Bazel หรือเวลาคิวในคลัสเตอร์ระยะไกล) ดังนั้น การใช้เมตริกแบบเชิงกำหนดเป็นตัวแทนของปริมาณงานที่ Bazel ทำจึงอาจเป็นประโยชน์ ซึ่งจะส่งผลต่อประสิทธิภาพของ Bazel
ขนาดของคำขอบิลด์อาจส่งผลอย่างมากต่อประสิทธิภาพการบิลด์ การบิลด์ขนาดใหญ่อาจหมายถึงงานที่มากขึ้นในการวิเคราะห์และสร้างกราฟการบิลด์ การเติบโตแบบออร์แกนิกของการบิลด์เกิดขึ้นตามธรรมชาติพร้อมกับการพัฒนา เนื่องจากมีการเพิ่ม/สร้างการขึ้นต่อกันมากขึ้น จึงมีความซับซ้อนมากขึ้นและใช้ทรัพยากรมากขึ้นในการบิลด์
เราสามารถแบ่งปัญหานี้ออกเป็นระยะต่างๆ ของการบิลด์ และใช้เมตริกต่อไปนี้เป็นเมตริกตัวแทนสำหรับงานที่ทำในแต่ละระยะ
PackageMetrics.packages_loaded: จำนวนแพ็กเกจที่โหลดสำเร็จ การถดถอยในที่นี้แสดงถึงงานที่ต้องทำมากขึ้นในการอ่านและแยกวิเคราะห์ไฟล์ BUILD เพิ่มเติมแต่ละไฟล์ในระยะการโหลดTargetMetrics.targets_configured: แสดงจำนวนเป้าหมายและแง่มุมที่กำหนดค่าในการบิลด์ การเกิดปัญหาซ้ำแสดงถึงงานที่มากขึ้นในการสร้างและข้ามผ่านกราฟเป้าหมายที่กำหนดค่า- ซึ่งมักเกิดจากการเพิ่มการขึ้นต่อกันและต้องสร้างกราฟของการปิดทรานซิทีฟ
- ใช้ 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: จำนวนอาร์ติแฟกต์ที่สร้างขึ้นโดยการดำเนินการที่ดำเนินการ- หากจำนวนการดำเนินการที่ดำเนินการไม่เพิ่มขึ้น แสดงว่ามีการเปลี่ยนแปลงการใช้งานกฎ
เมตริกเหล่านี้ทั้งหมดได้รับผลกระทบจากสถานะของแคชในเครื่อง ดังนั้นคุณจึงต้องตรวจสอบว่าการบิลด์ที่คุณแยกเมตริกเหล่านี้ออกมาเป็นการบิลด์แบบ Clean
เราสังเกตว่าการถดถอยในเมตริกเหล่านี้อาจมาพร้อมกับการถดถอยในเวลาจริง เวลา CPU และการใช้งานหน่วยความจำ
การใช้ทรัพยากรในเครื่อง
Bazel ใช้ทรัพยากรต่างๆ ในเครื่องของคุณ (ทั้งสำหรับการวิเคราะห์กราฟการบิลด์และการขับเคลื่อนการดำเนินการ รวมถึงสำหรับการดำเนินการในเครื่อง) ซึ่งอาจส่งผลต่อประสิทธิภาพ / ความพร้อมใช้งานของเครื่องในการบิลด์และงานอื่นๆ
เวลาที่ใช้
เมตริกที่อาจได้รับผลกระทบจากสัญญาณรบกวนมากที่สุด (และอาจแตกต่างกันอย่างมากจากการบิลด์หนึ่งไปยังอีกการบิลด์หนึ่ง) คือเวลา โดยเฉพาะเวลาจริง เวลา CPU และเวลาของระบบ คุณสามารถ ใช้ bazel-bench เพื่อรับ การเปรียบเทียบสำหรับเมตริกเหล่านี้ และใช้ --runs จำนวนมากพอเพื่อเพิ่มนัยสำคัญทางสถิติของการวัด
เวลาจริง คือเวลาที่ผ่านไปในโลกจริง
- หากเวลาจริง เท่านั้น ที่ถดถอย เราขอแนะนำให้รวบรวม โปรไฟล์การติดตาม JSON และมองหา ความแตกต่าง มิฉะนั้น การตรวจสอบเมตริกอื่นๆ ที่ถดถอยน่าจะมีประสิทธิภาพมากกว่า เนื่องจากเมตริกเหล่านั้นอาจส่งผลต่อเวลาจริง
เวลา CPU คือเวลาที่ CPU ใช้ในการดำเนินการโค้ดของผู้ใช้
- หากเวลา CPU ถดถอยในการคอมมิตโปรเจ็กต์ 2 รายการ เราขอแนะนำให้รวบรวมโปรไฟล์ CPU ของ Starlark นอกจากนี้ คุณควรใช้
--nobuildเพื่อจำกัดการบิลด์ไว้ที่ระยะการวิเคราะห์ เนื่องจากเป็นระยะที่มีการทำงานที่ใช้ CPU มากที่สุด
- หากเวลา CPU ถดถอยในการคอมมิตโปรเจ็กต์ 2 รายการ เราขอแนะนำให้รวบรวมโปรไฟล์ CPU ของ Starlark นอกจากนี้ คุณควรใช้
เวลาของระบบคือเวลาที่ CPU ใช้ในเคอร์เนล
- หากเวลาของระบบถดถอย มักจะสัมพันธ์กับการรับ/ส่งข้อมูลเมื่อ Bazel อ่านไฟล์จากระบบไฟล์
การสร้างโปรไฟล์การโหลดทั้งระบบ
โปรไฟล์การติดตาม JSON จะรวบรวมค่าเฉลี่ยการโหลดของระบบระหว่างการเรียกใช้โดยใช้แฟล็ก
--experimental_collect_load_average_in_profiler
ที่เปิดตัวใน Bazel 6.0

รูปที่ 1 โปรไฟล์ที่มีค่าเฉลี่ยการโหลดของระบบ
การโหลดสูงระหว่างการเรียกใช้ Bazel อาจบ่งชี้ว่า Bazel กำหนดเวลาการดำเนินการในเครื่องมากเกินไปพร้อมกันสำหรับเครื่องของคุณ คุณอาจต้องพิจารณา
ปรับ
--local_cpu_resources
และ --local_ram_resources,
โดยเฉพาะอย่างยิ่งในสภาพแวดล้อมคอนเทนเนอร์ (อย่างน้อยจนกว่า
#16512 จะมีการผสาน)
การตรวจสอบการใช้งานหน่วยความจำของ Bazel
คุณสามารถดูการใช้งานหน่วยความจำของ Bazel ได้จากแหล่งที่มาหลัก 2 แหล่ง ได้แก่ 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(ดู เอกสารประกอบ) แต่มีความเกี่ยวข้องน้อยกว่า
BEP’s
MemoryMetrics.peak_post_gc_heap_sizeของ BEP: ขนาดฮีป JVM สูงสุดเป็น ไบต์หลัง GC (ต้องตั้งค่า--memory_profileที่พยายามบังคับให้ทำ GC แบบเต็ม)
การถดถอยในการใช้งานหน่วยความจำมักเกิดจากการถดถอยใน เมตริกขนาดคำขอบิลด์, ซึ่งมักเกิดจากการเพิ่มทรัพยากร Dependency หรือการเปลี่ยนแปลงการใช้งานกฎ
หากต้องการวิเคราะห์หน่วยความจำที่ใช้ของ Bazel ในระดับที่ละเอียดยิ่งขึ้น เราขอแนะนำให้ใช้ โปรไฟล์หน่วยความจำในตัว สำหรับกฎ
การสร้างโปรไฟล์หน่วยความจำของผู้ปฏิบัติงานแบบถาวร
แม้ว่า ผู้ปฏิบัติงานแบบถาวร จะช่วยเร่งการบิลด์ได้อย่างมาก (โดยเฉพาะอย่างยิ่งสำหรับภาษาที่แปลโดยอินเทอร์พรีเตอร์) แต่หน่วยความจำที่ใช้อาจเป็นปัญหาได้ Bazel จะรวบรวมเมตริกเกี่ยวกับผู้ปฏิบัติงาน โดยเฉพาะอย่างยิ่ง ฟิลด์ WorkerMetrics.WorkerStats.worker_memory_in_kb จะบอกปริมาณหน่วยความจำที่ผู้ปฏิบัติงานใช้ (ตามคำช่วยจำ)
โปรไฟล์การติดตาม JSON ยัง
รวบรวมการใช้งานหน่วยความจำของผู้ปฏิบัติงานแบบถาวรระหว่างการเรียกใช้โดยส่งแฟล็ก
--experimental_collect_system_network_usage (ใหม่ใน Bazel 6.0)

รูปที่ 2 โปรไฟล์ที่มีการใช้งานหน่วยความจำของผู้ปฏิบัติงาน
การลดค่า
--worker_max_instances
(ค่าเริ่มต้นคือ 4) อาจช่วยลด
ปริมาณหน่วยความจำที่ผู้ปฏิบัติงานแบบถาวรใช้ เรากำลังดำเนินการอย่างจริงจังเพื่อทำให้ตัวจัดการทรัพยากรและตัวกำหนดเวลาของ Bazel ฉลาดขึ้น เพื่อให้คุณไม่ต้องปรับแต่งอย่างละเอียดเช่นนี้บ่อยนักในอนาคต
การตรวจสอบการจราจรของข้อมูลในเครือข่ายสำหรับการบิลด์ระยะไกล
ในการดำเนินการระยะไกล Bazel จะดาวน์โหลดอาร์ติแฟกต์ที่สร้างขึ้นจากการดำเนินการ ดังนั้น แบนด์วิดท์ของเครือข่ายจึงอาจส่งผลต่อประสิทธิภาพการบิลด์
หากคุณใช้การดำเนินการระยะไกลสำหรับการบิลด์ คุณอาจต้องพิจารณาตรวจสอบการจราจรของข้อมูลในเครือข่ายระหว่างการเรียกใช้โดยใช้NetworkMetrics.SystemNetworkStats โปรโตจาก BEP(ต้องส่ง --experimental_collect_system_network_usage)
นอกจากนี้ โปรไฟล์การติดตาม JSON ยังช่วยให้คุณดูการใช้งานเครือข่ายทั้งระบบตลอดระยะเวลาการบิลด์ได้ โดยส่งแฟล็ก --experimental_collect_system_network_usage (ใหม่ใน Bazel 6.0)

รูปที่ 3 โปรไฟล์ที่มีการใช้งานเครือข่ายทั้งระบบ
การใช้งานเครือข่ายสูงแต่ค่อนข้างคงที่เมื่อใช้การดำเนินการระยะไกลอาจบ่งชี้ว่าเครือข่ายเป็นจุดคอขวดในการบิลด์ หากยังไม่ได้ใช้ ให้ลองเปิดใช้การบิลด์โดยไม่ใช้ไบต์โดยส่ง--remote_download_minimal
ซึ่งจะช่วยเร่งการบิลด์โดยหลีกเลี่ยงการดาวน์โหลดอาร์ติแฟกต์ระดับกลางที่ไม่จำเป็น
อีกตัวเลือกหนึ่งคือการกำหนดค่าแคชดิสก์ในเครื่อง เพื่อประหยัดแบนด์วิดท์ในการดาวน์โหลด