แจกแจงประสิทธิภาพของบิลด์

รายงานปัญหา ดูแหล่งที่มา

Bazel มีความซับซ้อนและทำสิ่งต่างๆ มากมายตลอดการสร้าง ซึ่งบางอย่างอาจส่งผลต่อประสิทธิภาพของบิลด์ หน้านี้พยายามจะเชื่อมโยงแนวคิดของ Bazel บางส่วนเหล่านี้เข้ากับผลกระทบที่มีต่อประสิทธิภาพในการสร้าง เราได้รวมตัวอย่างวิธีตรวจหาปัญหาด้านประสิทธิภาพผ่านการดึงข้อมูลเมตริกและวิธีแก้ไขไว้ แม้ว่าจะเป็นข้อมูลที่ไม่ครอบคลุมก็ตาม เราจึงหวังว่าคุณจะนำแนวคิดเหล่านี้ไปใช้เมื่อตรวจสอบประสิทธิภาพที่ถดถอยในการสร้าง

บิลด์ที่สะอาดเทียบกับบิลด์ที่เพิ่มขึ้น

บิลด์ที่ชัดเจนคือบิลด์ที่สร้างทุกอย่างใหม่ตั้งแต่ต้น ขณะที่งานสร้างเพิ่มเติมจะนำงานบางส่วนที่ทำเสร็จแล้วมาใช้ซ้ำ

เราขอแนะนำให้ดูบิลด์ที่เพิ่มขึ้นและชัดเจนแยกกัน โดยเฉพาะอย่างยิ่งเมื่อคุณกำลังรวบรวม / รวมเมตริกที่ขึ้นอยู่กับสถานะแคชของ Bazel (เช่น เมตริกขนาดคำขอสร้าง) และยังแสดงถึงประสบการณ์ของผู้ใช้ 2 อย่างที่แตกต่างกัน เมื่อเทียบกับการสร้างบิลด์ที่สะอาดตั้งแต่ต้น (ซึ่งใช้เวลานานกว่าเนื่องจากแคชเย็น) การสร้างส่วนเพิ่มจะเกิดขึ้นบ่อยมากขึ้นเมื่อนักพัฒนาซอฟต์แวร์ทำซ้ำโค้ด (โดยปกติจะเร็วกว่าเนื่องจากแคชมักจะอุ่นอยู่แล้ว)

คุณใช้ช่อง CumulativeMetrics.num_analyses ใน BEP เพื่อจำแนกประเภทได้ หากเป็น num_analyses <= 1 จะเป็นบิลด์ที่ชัดเจน มิเช่นนั้น เราสามารถจัดหมวดหมู่อย่างกว้างๆ ว่าน่าจะเป็นบิลด์แบบเพิ่มขึ้น ผู้ใช้อาจเปลี่ยนไปใช้ Flag อื่นหรือเป้าหมายอื่นที่ทำให้เกิดบิลด์ที่สะอาดตาได้อย่างมีประสิทธิภาพ คำนิยามที่เข้มงวดมากขึ้นของส่วนเพิ่มควรมาในรูปแบบของการเรียนรู้ เช่น การดูจำนวนแพ็กเกจที่โหลด (PackageMetrics.packages_loaded)

เมตริกบิลด์ที่กำหนดเป็นพร็อกซีสำหรับประสิทธิภาพของบิลด์

การวัดประสิทธิภาพบิลด์อาจทำได้ยากเนื่องจากเมตริกบางรายการไม่ได้เป็นตัวกำหนด (เช่น เวลา CPU ของ Bazel หรือเวลาในคิวบนคลัสเตอร์ระยะไกล) ดังนั้นการใช้เมตริกเชิงกำหนดเป็นตัวชี้วัดจำนวนงานที่ Bazel ทำได้จึงมีประโยชน์ ซึ่งจะส่งผลต่อประสิทธิภาพของเครื่องมือดังกล่าว

ขนาดของคำขอบิลด์อาจส่งผลกระทบอย่างมากต่อประสิทธิภาพของบิลด์ บิลด์ขนาดใหญ่อาจแสดงถึงงานที่มากกว่าในการวิเคราะห์และสร้างกราฟบิลด์ การเติบโตแบบออร์แกนิกของบิลด์จะมาพร้อมการพัฒนาอย่างเป็นธรรมชาติ เนื่องจากมีการเพิ่ม/สร้างทรัพยากร Dependency มากขึ้น ส่งผลให้มีความซับซ้อนและมีค่าใช้จ่ายสูงในการสร้าง

เราสามารถแบ่งปัญหานี้ออกเป็นระยะต่างๆ ของการสร้าง และใช้เมตริกต่อไปนี้เป็นเมตริกพร็อกซีสำหรับงานที่ทำในแต่ละระยะ

  1. PackageMetrics.packages_loaded: จำนวนแพ็กเกจที่โหลดสำเร็จ การถดถอยที่นี่แสดงถึงงานเพิ่มเติมที่ต้องทำในการอ่านและแยกวิเคราะห์ไฟล์ BUILD เพิ่มเติมแต่ละไฟล์ในช่วงการโหลด

    • กรณีนี้มักเกิดจากการเพิ่มทรัพยากร Dependency และต้องโหลดการปิดแบบสกรรม
    • ใช้ query / cquery เพื่อดูว่าระบบอาจเพิ่มทรัพยากร Dependency ใหม่ไว้ที่ใดบ้าง
  2. TargetMetrics.targets_configured: แสดงจำนวนเป้าหมายและ ด้านที่กำหนดค่าไว้ในบิลด์ การถดถอยแสดงถึงงานที่มากกว่าในการสร้างและข้ามผ่านกราฟเป้าหมายที่กำหนดค่าไว้

    • กรณีนี้มักเกิดจากการเพิ่มทรัพยากร Dependency และต้องมีการสร้างกราฟการปิดแบบทรานซิทีฟ
    • ใช้ cquery เพื่อดูว่าอาจมีการเพิ่มทรัพยากร Dependency ใหม่ไว้ที่ใดบ้าง
  3. ActionSummary.actions_created: แสดงการดำเนินการที่สร้างในบิลด์ และการถดถอยหมายถึงงานที่มากกว่าในการสร้างกราฟการดำเนินการ โปรดทราบว่ากรณีนี้รวมถึงการดำเนินการที่ไม่ได้ใช้ซึ่งอาจไม่ได้ดำเนินการด้วย

    • ใช้ aquery เพื่อแก้ไขข้อบกพร่องการเกิดปัญหาซ้ำ เราขอแนะนำให้เริ่มต้นด้วย --output=summary ก่อนที่จะเจาะลึกเกี่ยวกับ --skyframe_state
  4. ActionSummary.actions_executed: จำนวนการดำเนินการที่มีการดำเนินการ การถดถอยแสดงถึงงานที่มากขึ้นในการดำเนินการเหล่านี้โดยตรง

    • BEP จะเขียนสถิติการดำเนินการ ActionData ที่แสดงประเภทการดำเนินการที่มีการดำเนินการมากที่สุด โดยค่าเริ่มต้น ระบบจะรวบรวมการดำเนินการ 20 ประเภทแรก แต่คุณจะส่งใน --experimental_record_metrics_for_all_mnemonics เพื่อรวบรวมข้อมูลนี้สำหรับการดำเนินการทุกประเภทที่ดำเนินการไปแล้วได้
    • ซึ่งจะช่วยให้คุณทราบว่ามีการดำเนินการประเภทใดเกิดขึ้นบ้าง (เพิ่มเติม)
  5. BuildGraphSummary.outputArtifactCount: จำนวนอาร์ติแฟกต์ที่สร้างขึ้นโดยการดำเนินการ

    • หากจำนวนการดำเนินการที่ดำเนินการไม่เพิ่มขึ้น อาจเป็นไปได้ว่ามีการใช้กฎเปลี่ยนแปลงไป

เมตริกเหล่านี้ได้รับผลกระทบจากสถานะของแคชในเครื่อง คุณจึงต้องตรวจสอบว่าบิลด์ที่คุณดึงเมตริกเหล่านี้ออกมาเป็นบิลด์ที่สะอาด

เราสังเกตเห็นว่าการถดถอยในเมตริกเหล่านี้อาจมาพร้อมกับการถดถอยในช่วงเวลาจำกัด เวลา CPU และการใช้หน่วยความจำ

การใช้ทรัพยากรในเครื่อง

Bazel ใช้ทรัพยากรที่หลากหลายในเครื่องของคุณ (ทั้งเพื่อวิเคราะห์กราฟบิลด์และขับเคลื่อนการดำเนินการ รวมถึงการเรียกใช้การดำเนินการในพื้นที่) ซึ่งอาจส่งผลต่อประสิทธิภาพ / ความพร้อมใช้งานของเครื่องในขั้นตอนการสร้าง รวมถึงงานอื่นๆ ด้วย

เวลาที่ใช้

บางทีเมตริกที่เสี่ยงต่อสัญญาณรบกวนมากที่สุด (และอาจแตกต่างกันไปมากในแต่ละรุ่น) ก็คือเวลา โดยเฉพาะเวลาผนัง เวลา CPU และเวลาของระบบ คุณใช้ bazel-bench เพื่อดูการเปรียบเทียบสำหรับเมตริกเหล่านี้ได้ และหากมี --runs ที่เพียงพอ คุณก็จะเพิ่มนัยสำคัญทางสถิติในการวัดผลได้

  • Wall Time คือเวลาจริงที่ผ่านไป

    • หากเฉพาะเวลาจริงที่ถดถอย เราขอแนะนำให้รวบรวมโปรไฟล์การติดตาม JSON และหาความแตกต่าง มิฉะนั้น การตรวจสอบเมตริกอื่นๆ ที่ถดถอยก็น่าจะมีประสิทธิภาพมากกว่า เนื่องจากอาจมีผลกระทบต่อเวลามาตรฐาน
  • เวลา CPU คือเวลาที่ CPU เรียกใช้รหัสผู้ใช้

    • หากเวลา CPU ถดถอยในการคอมมิตโปรเจ็กต์ 2 รายการ เราขอแนะนำให้รวบรวมโปรไฟล์ CPU ของ Starlark นอกจากนี้ คุณน่าจะใช้ --nobuild เพื่อจำกัดบิลด์ให้อยู่ในขั้นการวิเคราะห์ เนื่องจากเป็นส่วนที่มีการใช้ CPU หนักๆ เป็นส่วนใหญ่
  • เวลาระบบคือเวลาที่ CPU ในเคอร์เนลใช้

    • หากเวลาของระบบถดถอย ก็มักจะสัมพันธ์กับ I/O เมื่อ 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()

  • BEP MemoryMetrics.peak_post_gc_heap_size: ขนาดของฮีป 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 Proto จาก BEP (ต้องมีการผ่าน --experimental_collect_system_network_usage)

นอกจากนี้ โปรไฟล์การติดตาม JSON ยังช่วยให้คุณดูการใช้งานเครือข่ายทั้งระบบได้ตลอดการสร้างโดยส่ง Flag --experimental_collect_system_network_usage (แบบใหม่ใน Bazel 6.0)

โปรไฟล์ที่มีการใช้งานเครือข่ายทั้งระบบ

รูปที่ 3 โปรไฟล์ที่มีการใช้งานเครือข่ายทั้งระบบ

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

อีกทางเลือกหนึ่งคือการกำหนดค่าดิสก์แคชในเครื่องเพื่อประหยัดแบนด์วิดท์การดาวน์โหลด