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

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)

เมตริกการบิลด์แบบ 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 ใช้ในเคอร์เนล

    • หากเวลาของระบบถดถอย มักจะสัมพันธ์กับการรับ/ส่งข้อมูลเมื่อ 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’s MemoryMetrics.peak_post_gc_heap_size: ขนาดฮีป JVM สูงสุดใน หน่วยไบต์หลัง GC (ต้องตั้งค่า --memory_profile ที่พยายามบังคับให้ทำ GC แบบเต็ม)

การถดถอยในการใช้งานหน่วยความจำมักเกิดจากการถดถอยใน เมตริกขนาดคำขอการบิลด์, ซึ่งมักเกิดจากการเพิ่มทรัพยากร Dependency หรือการเปลี่ยนแปลงการใช้งานกฎ

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

การสร้างโปรไฟล์หน่วยความจำของ Worker แบบถาวร

แม้ว่า Worker แบบถาวร จะช่วยเร่งการบิลด์ ได้อย่างมาก (โดยเฉพาะสำหรับภาษาที่ตีความ) แต่หน่วยความจำที่ใช้ของ Worker อาจ เป็นปัญหาได้ Bazel จะรวบรวมเมตริกเกี่ยวกับ Worker โดยเฉพาะฟิลด์ WorkerMetrics.WorkerStats.worker_memory_in_kb จะบอกปริมาณหน่วยความจำที่ Worker ใช้ (ตามคำช่วยจำ)

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

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

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

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

การตรวจสอบการจราจรของข้อมูลในเครือข่ายสำหรับการบิลด์ระยะไกล

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

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

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

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

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

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

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