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

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

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

บิลด์แบบสะอาดกับบิลด์แบบเพิ่ม

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

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

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

เมตริกการสร้างแบบกำหนดได้เพื่อใช้แทนประสิทธิภาพการสร้าง

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

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

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

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

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

    • ซึ่งมักเกิดจากการเพิ่มการพึ่งพาและต้องสร้างกราฟของ Closure แบบทรานซิทีฟ
    • ใช้ cquery เพื่อค้นหาตำแหน่งที่อาจมีการเพิ่มข้อกำหนดใหม่
  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 เพียงพอ คุณจะเพิ่มความสําคัญทางสถิติของการวัดได้

  • เวลาจริงคือเวลาที่ผ่านไปในชีวิตจริง

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

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

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