Khung tranh tường

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

Mô hình đánh giá song song và mức độ gia tăng của Bazel.

Mô hình dữ liệu

Mô hình dữ liệu bao gồm các mục sau:

  • SkyValue. Còn được gọi là nút. SkyValues là các đối tượng bất biến chứa tất cả dữ liệu được xây dựng trong quá trình tạo và dữ liệu đầu vào của bản dựng. Ví dụ: tệp đầu vào, tệp đầu ra, mục tiêu và mục tiêu đã định cấu hình.
  • SkyKey. Tên ngắn không thể thay đổi để tham chiếu đến SkyValue, ví dụ: FILECONTENTS:/tmp/foo hoặc PACKAGE://foo.
  • SkyFunction. Tạo nút dựa trên các khoá và nút phụ thuộc.
  • Biểu đồ nút. Cấu trúc dữ liệu chứa mối quan hệ phụ thuộc giữa các nút.
  • Skyframe. Tên mã cho khung đánh giá gia tăng Bazel dựa trên.

Đánh giá

Tạo bản dựng bằng cách đánh giá nút đại diện cho yêu cầu bản dựng.

Trước tiên, Bazel tìm thấy SkyFunction tương ứng với khoá của SkyKey cấp cao nhất. Sau đó, hàm này yêu cầu đánh giá các nút cần đánh giá nút cấp cao nhất, từ đó dẫn đến các lệnh gọi SkyFunction khác, cho đến khi đạt đến các nút lá. Nút lá thường là những nút đại diện cho các tệp đầu vào trong hệ thống tệp. Cuối cùng, Bazel sẽ cuối cùng là giá trị của SkyValue cấp cao nhất, một số hiệu ứng phụ (chẳng hạn như tệp đầu ra trong hệ thống tệp) và biểu đồ tuần hoàn có định hướng của các phần phụ thuộc giữa các nút có liên quan đến bản dựng.

Một SkyFunction có thể yêu cầu SkyKeys trong nhiều lần truyền nếu không thể báo trước tất cả các nút mà nó cần để thực hiện công việc. Một ví dụ đơn giản là đánh giá một nút tệp đầu vào hoá ra là một đường liên kết tượng trưng: hàm này cố gắng đọc tệp, nhận ra đó là một đường liên kết tượng trưng và do đó tìm nạp nút hệ thống tệp đại diện cho mục tiêu của đường liên kết tượng trưng. Tuy nhiên, bản thân đó cũng có thể là một đường liên kết tượng trưng, trong trường hợp này, hàm ban đầu cũng sẽ cần tìm nạp mục tiêu.

Các hàm được biểu thị trong mã bằng giao diện SkyFunction và các dịch vụ được cung cấp cho hàm đó bằng một giao diện có tên là SkyFunction.Environment. Sau đây là những việc mà các hàm có thể làm:

  • Yêu cầu đánh giá một nút khác bằng cách gọi env.getValue. Nếu nút có sẵn, giá trị của nút đó sẽ được trả về, nếu không, null sẽ được trả về và chính hàm đó dự kiến sẽ trả về null. Trong trường hợp sau, nút phụ thuộc được đánh giá, sau đó trình tạo nút ban đầu được gọi lại, nhưng lần này lệnh gọi env.getValue đó sẽ trả về một giá trị không phải null.
  • Yêu cầu đánh giá nhiều nút khác bằng cách gọi env.getValues(). Về cơ bản, việc này giống như vậy, ngoại trừ việc các nút phụ thuộc được đánh giá song song.
  • Thực hiện tính toán trong khi gọi
  • Có tác dụng phụ, chẳng hạn như ghi tệp vào hệ thống tệp. Bạn cần lưu ý rằng 2 hàm khác nhau tránh dẫm lên ngón chân nhau. Nhìn chung, việc ghi hiệu ứng phụ (trong đó dữ liệu truyền ra từ Bazel) khá ổn, đọc các hiệu ứng phụ (trong đó dữ liệu truyền vào Bazel mà không có phần phụ thuộc đã đăng ký) thì không, vì đó là phần phụ thuộc chưa được đăng ký và do đó, có thể gây ra các bản dựng gia tăng không chính xác.

Việc triển khai SkyFunction đúng cách sẽ tránh truy cập vào dữ liệu theo bất kỳ cách nào khác ngoài việc yêu cầu các phần phụ thuộc (chẳng hạn như bằng cách đọc trực tiếp hệ thống tệp), vì điều đó dẫn đến việc Bazel không đăng ký phần phụ thuộc dữ liệu trên tệp đã được đọc, dẫn đến việc tạo các bản dựng gia tăng không chính xác.

Sau khi một hàm có đủ dữ liệu để thực hiện công việc, hàm đó sẽ trả về một giá trị không phải null cho biết đã hoàn thành.

Chiến lược đánh giá này có một số lợi ích sau:

  • Độ mạnh. Nếu các hàm chỉ yêu cầu dữ liệu đầu vào theo cách phụ thuộc vào các nút khác, thì Bazel có thể đảm bảo rằng nếu trạng thái đầu vào giống nhau, thì cùng một dữ liệu sẽ được trả về. Nếu tất cả các hàm sky đều là tất định, thì toàn bộ bản dựng cũng sẽ mang tính quyết định.
  • Gia tăng chính xác và hoàn hảo. Nếu mọi dữ liệu đầu vào của mọi hàm đều được ghi lại, thì Bazel chỉ có thể vô hiệu hoá chính tập hợp các nút cần vô hiệu hoá khi dữ liệu đầu vào thay đổi.
  • Tính song song. Vì các hàm chỉ có thể tương tác với nhau bằng cách yêu cầu phần phụ thuộc, nên các hàm không phụ thuộc vào nhau có thể được chạy song song và Bazel có thể đảm bảo rằng kết quả giống như khi các hàm đó chạy tuần tự.

Mức độ gia tăng

Vì các hàm chỉ có thể truy cập dữ liệu đầu vào bằng cách phụ thuộc vào các nút khác, Bazel có thể tạo một biểu đồ luồng dữ liệu hoàn chỉnh từ các tệp đầu vào đến các tệp đầu ra và sử dụng thông tin này để chỉ tạo lại các nút thực sự cần được tạo lại: trạng thái đóng bắc cầu ngược của tập hợp các tệp đầu vào đã thay đổi.

Cụ thể, có thể có 2 chiến lược về mức độ gia tăng: chiến lược từ dưới lên và chiến lược từ trên xuống. Mục nào là tối ưu tuỳ thuộc vào giao diện của biểu đồ phần phụ thuộc.

  • Trong quá trình vô hiệu hoá từ dưới lên, sau khi tạo biểu đồ và xác định được một tập hợp đầu vào đã thay đổi, tất cả các nút đều bị vô hiệu hoá mà phụ thuộc bắc cầu vào các tệp được thay đổi. Đây là cách tối ưu nếu cùng một nút cấp cao nhất sẽ được tạo lại. Lưu ý rằng việc vô hiệu hoá từ dưới lên đòi hỏi bạn phải chạy stat() trên tất cả các tệp đầu vào của bản dựng trước để xác định xem các tệp đó có thay đổi hay không. Bạn có thể cải thiện điều này bằng cách sử dụng inotify hoặc một cơ chế tương tự để tìm hiểu về các tệp đã thay đổi.

  • Trong quá trình vô hiệu hoá từ trên xuống, trạng thái đóng bắc cầu của nút cấp cao nhất sẽ được kiểm tra và chỉ những nút đó mới được giữ lại có trạng thái đóng bắc cầu không bị ảnh hưởng. Điều này sẽ tốt hơn nếu biểu đồ nút lớn, nhưng bản dựng tiếp theo chỉ cần một tập hợp con nhỏ: việc vô hiệu hoá từ dưới lên sẽ làm vô hiệu hoá biểu đồ lớn hơn của bản dựng đầu tiên, không giống như vô hiệu hoá từ trên xuống, chỉ xem xét đồ thị nhỏ của bản dựng thứ hai.

Bazel chỉ vô hiệu hoá từ dưới lên.

Để gia tăng hiệu quả hơn nữa, Bazel sử dụng phương pháp cắt bớt thay đổi: nếu một nút không hợp lệ, nhưng sau khi xây dựng lại, hệ thống phát hiện ra rằng giá trị mới của nút đó giống với giá trị cũ, các nút đã bị vô hiệu hoá do thay đổi trong nút này sẽ được "hồi sinh".

Cách này hữu ích, chẳng hạn như nếu một người thay đổi một nhận xét trong tệp C++: thì tệp .o được tạo từ tệp đó sẽ giữ nguyên. Do đó, bạn không cần gọi lại trình liên kết.

Liên kết / biên dịch gia tăng

Hạn chế chính của mô hình này là việc vô hiệu hoá một nút là vấn đề tất cả hoặc không có gì xảy ra: khi một phần phụ thuộc thay đổi, nút phụ thuộc luôn được tạo lại từ đầu, ngay cả khi có thuật toán tốt hơn sẽ làm thay đổi giá trị cũ của nút dựa trên các thay đổi. Dưới đây là một số ví dụ mà tính năng này sẽ hữu ích:

  • Liên kết gia tăng
  • Khi một tệp lớp đơn lẻ thay đổi trong tệp JAR, bạn có thể sửa đổi tệp JAR tại chỗ thay vì tạo lại tệp từ đầu.

Bazel không hỗ trợ những việc này theo nguyên tắc gấp 2 lần:

  • Mức tăng hiệu suất có giới hạn.
  • Khó xác thực rằng kết quả của đột biến giống với kết quả của việc xây dựng lại hoàn toàn và các giá trị của Google có thể lặp lại theo kiểu bit-cho-bit.

Cho đến nay, bạn có thể đạt được hiệu suất đủ tốt bằng cách phân tích một bước xây dựng tốn kém và đánh giá lại một phần theo cách đó. Ví dụ: trong ứng dụng Android, bạn có thể chia tất cả các lớp thành nhiều nhóm và tạo tệp dex riêng biệt. Bằng cách này, nếu các lớp trong một nhóm không thay đổi, thì bạn không cần phải thực hiện lại thao tác tạo tệp dex.

Ánh xạ đến các khái niệm của Bazel

Đây là bản tóm tắt cấp cao về các cách triển khai khoá SkyFunctionSkyValue mà Bazel sử dụng để tạo bản dựng:

  • FileStateValue. Kết quả của một lstat(). Đối với các tệp tồn tại, hàm này cũng tính toán thông tin bổ sung để phát hiện các thay đổi đối với tệp đó. Đây là nút cấp thấp nhất trong biểu đồ Skyframe và không có phần phụ thuộc.
  • FileValue (Giá trị tệp). Dùng cho bất kỳ mục nào quan tâm đến nội dung thực tế hoặc đường dẫn đã phân giải của một tệp. Phụ thuộc vào FileStateValue tương ứng và bất kỳ đường liên kết tượng trưng nào cần được phân giải (chẳng hạn như FileValue cho a/b cần đường dẫn được phân giải của a và đường dẫn được phân giải của a/b). Sự khác biệt giữa FileValueFileStateValue là rất quan trọng vì bạn có thể dùng đường liên kết thứ hai trong trường hợp nội dung của tệp không thực sự cần thiết. Ví dụ: nội dung tệp không liên quan khi đánh giá toàn bộ hệ thống tệp (chẳng hạn như srcs=glob(["*/*.java"])).
  • DirectoryListingStateValue. Kết quả của readdir(). Giống như FileStateValue, đây là nút cấp thấp nhất và không có phần phụ thuộc.
  • DirectoryListingValue. Được dùng bởi bất kỳ mục nào quan tâm đến các mục nhập của thư mục. Phụ thuộc vào DirectoryListingStateValue tương ứng, cũng như FileValue liên kết của thư mục.
  • PackageValue (Giá trị gói). Đại diện cho phiên bản được phân tích cú pháp của tệp BUILD. Phụ thuộc vào FileValue của tệp BUILD được liên kết, đồng thời phụ thuộc vào bất kỳ DirectoryListingValue nào được dùng để phân giải các khối toàn cầu trong gói (cấu trúc dữ liệu biểu thị nội dung của tệp BUILD trong nội bộ).
  • ConfiguredTargetValue (Định cấu hình giá trị mục tiêu). Đại diện cho một mục tiêu đã định cấu hình, là một bộ gồm tập hợp các thao tác được tạo trong quá trình phân tích mục tiêu và thông tin được cung cấp cho các mục tiêu được định cấu hình phụ thuộc. Phụ thuộc vào PackageValue của mục tiêu tương ứng, ConfiguredTargetValues của các phần phụ thuộc trực tiếp và một nút đặc biệt đại diện cho cấu hình bản dựng.
  • ArtifactValue. Đại diện cho một tệp trong bản dựng, có thể là nguồn hoặc cấu phần phần mềm đầu ra. Cấu phần phần mềm gần như tương đương với tệp và được dùng để tham chiếu đến tệp trong quá trình thực thi các bước tạo bản dựng thực tế. Các tệp nguồn phụ thuộc vào FileValue của nút được liên kết, còn các cấu phần phần mềm đầu ra phụ thuộc vào ActionExecutionValue của bất kỳ hành động nào tạo ra cấu phần phần mềm.
  • ActionExecutionValue. Biểu thị quá trình thực thi một hành động. Phụ thuộc vào ArtifactValues của các tệp đầu vào. Thao tác mà nó thực thi nằm trong SkyKey, trái ngược với khái niệm rằng SkyKeys phải có kích thước nhỏ. Xin lưu ý rằng ActionExecutionValueArtifactValue sẽ không được dùng nếu giai đoạn thực thi không chạy.

Để hỗ trợ trực quan, sơ đồ này cho thấy mối quan hệ giữa các phương thức triển khai SkyFunction sau khi tạo bản dựng của chính Bazel:

Biểu đồ về các mối quan hệ triển khai SkyFunction