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

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

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

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

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

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

คุณใช้ช่อง 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 เพื่อหาจุดที่อาจมีการเพิ่มการขึ้นต่อกันใหม่
  3. ActionSummary.actions_created: แสดงการดำเนินการที่สร้างในบิลด์ ส่วนการถดถอยแสดงถึงงานอื่นๆ ในการสร้างกราฟการดำเนินการ โปรดทราบว่ายังรวมถึงการดำเนินการที่ไม่ได้ใช้งานซึ่งอาจไม่ได้ดำเนินการด้วย

    • ใช้คำค้นหาเพื่อแก้ไขข้อบกพร่องของปัญหาการเกิดปัญหาซ้ำ เราขอแนะนำให้เริ่มต้นด้วย --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 Flag ที่เปิดตัวใน Bazel 6.0 เครื่องมือสร้างโปรไฟล์การติดตาม JSON จะรวบรวมค่าเฉลี่ยการโหลดของระบบระหว่างการเรียกใช้

โปรไฟล์ที่มีค่าเฉลี่ยการโหลดของระบบ

รูปที่ 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 จะดาวน์โหลดอาร์ติแฟกต์ที่สร้างขึ้นอันเป็นผลมาจากการดำเนินการต่างๆ ดังนั้นแบนด์วิดท์เครือข่ายอาจส่งผลต่อประสิทธิภาพการทำงานของบิลด์

หากใช้การดำเนินการระยะไกลสำหรับบิลด์ คุณอาจต้องตรวจสอบการจราจรของข้อมูลในเครือข่ายระหว่างการเรียกใช้โดยใช้ Proto NetworkMetrics.SystemNetworkStats จาก BEP (ต้องระบุ --experimental_collect_system_network_usage)

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

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

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

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

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