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

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)

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

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

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

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

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

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

    • ซึ่งมักเกิดจากการเพิ่มการขึ้นต่อกันและต้องสร้างกราฟการปิดทรานซิทีฟของการขึ้นต่อกันเหล่านั้น
    • ใช้ 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: จำนวนอาร์ติแฟกต์ที่สร้างขึ้นโดยการดำเนินการที่ดำเนินการ

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

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

เราสังเกตว่าการถดถอยในเมตริกเหล่านี้อาจมาพร้อมกับการถดถอยในเวลาจริง เวลา 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

คุณสามารถดูการใช้งานหน่วยความจำของ 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 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)

โปรไฟล์ที่มีการใช้งานหน่วยความจำของ Worker

รูปที่ 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 ซึ่งจะช่วยเร่งการบิลด์โดยหลีกเลี่ยงการดาวน์โหลดอาร์ติแฟกต์ระดับกลางที่ไม่จำเป็น

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