Phân tích hiệu suất bản dựng

Báo cáo sự cố Xem nguồn

Bazel phức tạp và thực hiện rất nhiều việc trong suốt quá trình tạo bản dựng, một số việc có thể ảnh hưởng đến hiệu suất của bản dựng. Trang này cố gắng liên kết một số khái niệm Bazel này với ý nghĩa của chúng đối với hiệu suất bản dựng. Mặc dù không đầy đủ, nhưng chúng tôi đã đính kèm một số ví dụ về cách phát hiện vấn đề về hiệu suất bản dựng thông qua trích xuất các chỉ số và những việc bạn có thể làm để khắc phục các vấn đề đó. Bằng cách này, chúng tôi hy vọng bạn có thể áp dụng các khái niệm này khi nghiên cứu hồi quy hiệu suất bản dựng.

Bản dựng sạch so với Bản dựng tăng dần

Bản dựng sạch là bản dựng có thể tạo mọi thứ từ đầu, trong khi bản dựng tăng dần sẽ sử dụng lại một số bản dựng đã hoàn thành.

Bạn nên xem xét riêng các bản dựng sạch và tăng dần, đặc biệt là khi thu thập / tổng hợp các chỉ số phụ thuộc vào trạng thái bộ nhớ đệm của Bazel (ví dụ: chỉ số về kích thước yêu cầu bản dựng). Các bản dựng này cũng đại diện cho hai trải nghiệm người dùng khác nhau. So với việc bắt đầu một bản dựng sạch từ đầu (mất nhiều thời gian hơn do bộ nhớ đệm nguội), các bản dựng gia tăng xảy ra thường xuyên hơn nhiều khi nhà phát triển lặp lại mã (thường nhanh hơn vì bộ nhớ đệm thường đã ấm).

Bạn có thể sử dụng trường CumulativeMetrics.num_analyses trong BEP để phân loại bản dựng. Nếu num_analyses <= 1, đó là một bản dựng sạch; nếu không, chúng tôi có thể phân loại rộng rãi đó có thể là một bản dựng tăng dần – người dùng có thể đã chuyển sang các cờ khác nhau hoặc các mục tiêu khác nhau, từ đó dẫn đến một bản dựng sạch hiệu quả. Mọi định nghĩa nghiêm ngặt hơn về mức độ gia tăng có thể phải ở dạng suy đoán, chẳng hạn như xem số lượng gói được tải (PackageMetrics.packages_loaded).

Chỉ số bản dựng mang tính quyết định dùng làm proxy cho hiệu suất của bản dựng

Việc đo lường hiệu suất của bản dựng có thể khó khăn do tính chất không xác định của một số chỉ số (ví dụ: thời gian CPU hoặc thời gian chờ của Bazel trên một cụm từ xa). Do đó, bạn nên sử dụng các chỉ số xác định làm proxy cho lượng công việc mà Bazel thực hiện. Điều này sẽ ảnh hưởng đến hiệu suất của Bazel.

Kích thước của yêu cầu bản dựng có thể ảnh hưởng đáng kể đến hiệu suất của bản dựng. Bản dựng càng lớn thì càng có nhiều hoạt động phân tích và tạo biểu đồ bản dựng. Quá trình phát triển tự nhiên sẽ diễn ra theo hướng tự nhiên, vì có nhiều phần phụ thuộc được thêm vào/tạo hơn, từ đó trở nên phức tạp hơn và tốn kém chi phí xây dựng hơn.

Chúng ta có thể chia vấn đề này thành nhiều giai đoạn xây dựng và sử dụng các chỉ số sau đây làm chỉ số proxy cho công việc ở mỗi giai đoạn:

  1. PackageMetrics.packages_loaded: số lượng gói đã tải thành công. Sự hồi quy ở đây cho thấy bạn cần phải làm nhiều việc hơn để đọc và phân tích cú pháp từng tệp XÂY DỰNG bổ sung trong giai đoạn tải.

    • Điều này thường là do việc thêm các phần phụ thuộc và phải tải hệ thống đóng bắc cầu của các phần phụ thuộc đó.
    • Sử dụng query / cquery để tìm vị trí có thể thêm các phần phụ thuộc mới.
  2. TargetMetrics.targets_configured: đại diện cho số lượng mục tiêu và khung hình được định cấu hình trong bản dựng. Hồi quy cho thấy bạn phải làm việc nhiều hơn trong việc xây dựng và truyền tải biểu đồ mục tiêu đã định cấu hình.

    • Điều này thường là do việc thêm các phần phụ thuộc và phải dựng đồ thị đóng cửa bắc cầu của các phần phụ thuộc đó.
    • Sử dụng cquery để tìm vị trí các phần phụ thuộc mới có thể được thêm vào.
  3. ActionSummary.actions_created: đại diện cho các hành động được tạo trong bản dựng, còn hồi quy cho thấy việc tạo biểu đồ hành động tốn nhiều công sức hơn. Xin lưu ý rằng điều này cũng bao gồm các hành động không dùng đến có thể chưa được thực thi.

  4. ActionSummary.actions_executed: số lượng thao tác được thực thi, hồi quy trực tiếp thể hiện lượng công việc cần làm trong việc thực thi các thao tác này.

    • BEP ghi lại số liệu thống kê về hành động ActionData cho biết các loại hành động được thực thi nhiều nhất. Theo mặc định, công cụ này sẽ thu thập 20 loại hành động hàng đầu, nhưng bạn có thể chuyển vào --experimental_record_metrics_for_all_mnemonics để thu thập dữ liệu này cho tất cả các loại hành động đã được thực thi.
    • Điều này sẽ giúp bạn xác định những loại hành động đã được thực thi (bổ sung).
  5. BuildGraphSummary.outputArtifactCount: số lượng cấu phần phần mềm do các hành động được thực thi tạo ra.

    • Nếu số lượng thao tác được thực thi không tăng, thì có thể phương thức triển khai quy tắc đã được thay đổi.

Các chỉ số này đều chịu ảnh hưởng của trạng thái của bộ nhớ đệm cục bộ. Do đó, bạn cần đảm bảo rằng các bản dựng mà bạn trích xuất các chỉ số này là bản dựng sạch.

Chúng tôi nhận thấy rằng sự hồi quy của bất kỳ chỉ số nào trong số này có thể đi kèm với sự hồi quy về thời gian hiển thị, thời gian CPU và mức sử dụng bộ nhớ.

Sử dụng tài nguyên cục bộ

Bazel sử dụng nhiều tài nguyên trên máy cục bộ của bạn (cả việc phân tích biểu đồ bản dựng và thúc đẩy quá trình thực thi, và để chạy các hành động cục bộ), điều này có thể ảnh hưởng đến hiệu suất / khả năng sử dụng của máy khi thực hiện bản dựng cũng như các tác vụ khác.

Thời gian trên đường mòn

Có thể các chỉ số dễ bị nhiễu nhất (và có thể thay đổi đáng kể giữa các bản dựng) là thời gian; cụ thể là thời gian xử lý, thời gian CPU và thời gian hệ thống. Bạn có thể sử dụng bazel-bench để lấy điểm chuẩn cho các chỉ số này. Với số lượng --runs đủ, bạn có thể tăng ý nghĩa thống kê của hoạt động đo lường.

  • Giờ tường là thời gian thực đã trôi qua.

    • Nếu chỉ hồi quy thời gian chờ, bạn nên thu thập hồ sơ theo dõi JSON và tìm sự khác biệt. Nếu không, bạn nên điều tra các chỉ số hồi quy khác vì chúng có thể đã ảnh hưởng đến thời gian chờ.
  • Thời gian của CPU là thời gian để CPU thực thi mã người dùng.

    • Nếu thời gian CPU hồi quy qua 2 lần cam kết dự án, bạn nên thu thập hồ sơ CPU Starlark. Bạn cũng nên sử dụng --nobuild để hạn chế bản dựng trong giai đoạn phân tích vì đó là nơi thực hiện hầu hết công việc nặng về CPU.
  • Thời gian hệ thống là thời gian mà CPU sử dụng trong nhân.

    • Nếu thời gian hệ thống hồi quy, thời gian này chủ yếu tương quan với I/O khi Bazel đọc các tệp từ hệ thống tệp của bạn.

Lập hồ sơ tải trên toàn hệ thống

Bằng cách sử dụng cờ --experimental_collect_load_average_in_profiler được giới thiệu trong Bazel 6.0, Trình phân tích dấu vết JSON sẽ thu thập mức tải trung bình của hệ thống trong lệnh gọi.

Hồ sơ bao gồm mức trung bình tải của hệ thống

Hình 1. Hồ sơ bao gồm mức trung bình tải của hệ thống.

Mức tải cao trong lệnh gọi Bazel có thể là dấu hiệu cho thấy Bazel lên lịch song song quá nhiều thao tác cục bộ cho máy của bạn. Bạn nên xem xét việc điều chỉnh --local_cpu_resources--local_ram_resources, đặc biệt là trong môi trường vùng chứa (ít nhất cho đến khi #16512 được hợp nhất).

Giám sát mức sử dụng bộ nhớ Bazel

Có hai nguồn chính để tải mức sử dụng bộ nhớ của Bazel là Bazel infoBEP.

  • bazel info used-heap-size-after-gc: Dung lượng bộ nhớ đã sử dụng tính bằng byte sau khi gọi System.gc().

    • Bazel bench cũng cung cấp các điểm chuẩn cho chỉ số này.
    • Ngoài ra, còn có peak-heap-size, max-heap-size, used-heap-sizecommitted-heap-size (xem tài liệu), nhưng ít liên quan hơn.
  • MemoryMetrics.peak_post_gc_heap_size của BEP: Kích thước của kích thước vùng nhớ khối xếp JVM cao nhất tính bằng byte sau GC (yêu cầu cài đặt --memory_profile cố gắng buộc GC đầy đủ).

Việc hồi quy mức sử dụng bộ nhớ thường là kết quả của sự hồi quy trong chỉ số kích thước yêu cầu bản dựng, thường là do việc thêm các phần phụ thuộc hoặc thay đổi trong quá trình triển khai quy tắc.

Để phân tích mức sử dụng bộ nhớ của Bazel ở mức độ chi tiết hơn, bạn nên sử dụng trình phân tích bộ nhớ tích hợp sẵn cho các quy tắc.

Phân tích bộ nhớ của worker liên tục

Mặc dù trình thực thi liên tục có thể giúp tăng tốc đáng kể các bản dựng (đặc biệt là đối với ngôn ngữ thông dịch), nhưng mức sử dụng bộ nhớ của chúng có thể gây ra vấn đề. Bazel thu thập các chỉ số trên các worker của mình, cụ thể là trường WorkerMetrics.WorkerStats.worker_memory_in_kb cho biết lượng bộ nhớ mà trình thực thi sử dụng (theo trình ghi nhớ).

Trình phân tích dấu vết JSON cũng thu thập mức sử dụng bộ nhớ của worker liên tục trong lệnh gọi bằng cách truyền cờ --experimental_collect_system_network_usage (mới trong Bazel 6.0).

Hồ sơ bao gồm mức sử dụng bộ nhớ của worker

Hình 2. Hồ sơ bao gồm mức sử dụng bộ nhớ của worker.

Việc giảm giá trị --worker_max_instances (mặc định là 4) có thể giúp giảm dung lượng bộ nhớ mà các worker liên tục sử dụng. Chúng tôi đang tích cực tìm cách cải tiến trình quản lý tài nguyên và trình lập lịch biểu của Bazel để ít phải điều chỉnh hơn trong tương lai.

Giám sát lưu lượng truy cập mạng cho các bản dựng từ xa

Trong quá trình thực thi từ xa, Bazel sẽ tải các cấu phần phần mềm được tạo ra nhờ các thao tác thực thi. Do đó, băng thông mạng có thể ảnh hưởng đến hiệu suất của bản dựng.

Nếu đang sử dụng phương thức thực thi từ xa cho các bản dựng của mình, bạn nên cân nhắc giám sát lưu lượng truy cập mạng trong quá trình gọi bằng cách sử dụng proto NetworkMetrics.SystemNetworkStats từ BEP (yêu cầu truyền --experimental_collect_system_network_usage).

Hơn nữa, hồ sơ theo dõi JSON cho phép bạn xem mức sử dụng mạng trên toàn hệ thống trong suốt quá trình xây dựng bằng cách truyền cờ --experimental_collect_system_network_usage (mới trong Bazel 6.0).

Cấu hình bao gồm việc sử dụng mạng trên toàn hệ thống

Hình 3. Cấu hình bao gồm việc sử dụng mạng trên toàn hệ thống.

Mức sử dụng mạng cao nhưng khá phẳng khi sử dụng quá trình thực thi từ xa có thể cho biết rằng mạng là điểm tắc nghẽn trong bản dựng của bạn; nếu bạn chưa sử dụng mạng này, hãy cân nhắc bật Bản dựng không có Byte bằng cách truyền --remote_download_minimal. Việc này sẽ tăng tốc các bản dựng bằng cách tránh tải các cấu phần phần mềm trung gian không cần thiết xuống.

Một lựa chọn khác là định cấu hình bộ nhớ đệm của ổ đĩa cục bộ để tiết kiệm băng thông tải xuống.