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

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

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

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

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

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

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

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

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

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

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

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

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

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

    • ใช้ aquery เพื่อดูการถดถอย
  4. ActionSummary.actions_executed: จํานวนการดําเนินการที่เกิดขึ้น การเกิดปัญหาซ้ําแสดงถึงการดําเนินการเพิ่มเติมโดยตรงในการดําเนินการเหล่านี้

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

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

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

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

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

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

เวลาที่ใช้

บางทีเมตริกที่มีแนวโน้มที่จะเป็นสัญญาณรบกวนมากที่สุด (และอาจต่างกันไปมากตั้งแต่สร้างไปจนถึงสร้าง) คือเวลา โดยเฉพาะเวลาติดผนัง เวลา CPU และเวลาของระบบ คุณใช้ bazel-carousel เพื่อดูการเปรียบเทียบของเมตริกเหล่านี้ได้ และด้วยจํานวน --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 carousel มีการเปรียบเทียบสําหรับเมตริกนี้ด้วย
    • นอกจากนี้ยังมี peak-heap-size, max-heap-size, used-heap-size และ committed-heap-size (ดูเอกสารประกอบ) แต่มีความเกี่ยวข้องน้อย
  • BEP MemoryMetrics.peak_post_gc_heap_size: ขนาดของฮีป 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 จาก BEP (ต้องผ่าน --experimental_collect_system_network_usage)

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

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

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

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

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