Cách tăng tốc độ biên dịch Firefox lên 17% bằng cách cache WebIDL

13 tháng 4, 2026·5 phút đọc

Bài viết khám phá việc sử dụng hệ thống plugin Lua của buildcache để lưu trữ cache cho quá trình tạo mã WebIDL trong Firefox. Cải tiến này giúp rút ngắn đáng kể thời gian biên dịch, đặc biệt là trong các lần build lại (warm builds).

Trong bài đăng trước, tôi đã đề cập rằng buildcache sở hữu một số tính năng độc đáo so với ccache và sccache. Một trong số đó là hệ thống plugin Lua, cho phép viết các wrapper tùy chỉnh cho các chương trình không phải là trình biên dịch theo nghĩa truyền thống. Với việc hợp nhất Bug 2027655, chúng ta giờ đây có thể sử dụng tính năng này để cache quá trình tạo mã binding WebIDL của Firefox.

Bước WebIDL là gì?

Khi bạn biên dịch Firefox, một trong các bước đầu tiên sẽ chạy lệnh python3 -m mozbuild.action.webidl để tạo mã binding C++ từ hàng trăm tệp .webidl. Quá trình này tạo ra hàng ngàn tệp đầu ra: các tệp header, tệp cpp, khai báo forward, triển khai sự kiện, v.v.

Bước này không quá chậm trên thực tế, nhưng nó chạy trong mọi lần build sạch (clobber build), và đầu ra hoàn toàn xác định (deterministic) dựa trên cùng một đầu vào. Điều này biến nó thành một ứng cử viên hoàn hảo để cache.

Vấn đề là bộ nhớ đệm của trình biên dịch (compiler cache) chưa bao giờ được truyền vào bước này. Buildcache chỉ bao bọc (wrap) các lời gọi trình biên dịch thực sự, chứ không phải codegen bằng Python.

Thay đổi được thực hiện

Bản sửa lỗi trong Bug 2027655 khá nhỏ gọn. Trong dom/bindings/Makefile.in, chúng ta giờ đây có điều kiện truyền $(CCACHE) như một command wrapper cho lời gọi py_action:

WEBIDL_CCACHE=
ifdef MOZ_USING_BUILDCACHE
WEBIDL_CCACHE=$(CCACHE)
endif

webidl.stub: $(codegen_dependencies)
    $(call py_action,webidl $(relativesrcdir),$(srcdir),,$(WEBIDL_CCACHE))
    @$(TOUCH) $@

Macro py_action trong config/makefiles/functions.mk là thứ chạy các hành động build bằng Python. Khả năng truyền một command wrapper làm đối số thứ tư cũng được giới thiệu trong bug này. Khi buildcache được cấu hình làm bộ nhớ đệm trình biên dịch, điều này có nghĩa là hành động webidl được gọi dưới dạng buildcache python3 -m mozbuild.action.webidl ... thay vì chỉ là python3 -m mozbuild.action.webidl .... Đó là tất cả những gì buildcache cần để chặn nó.

Hãy lưu ý đến guard ifdef MOZ_USING_BUILDCACHE. Điều này dành riêng cho buildcache vì ccache và sccache không có cơ chế để cache các lệnh tùy ý. Buildcache thì có, thông qua các wrapper Lua của nó.

Wrapper Lua

Hệ thống plugin Lua của buildcache cho phép bạn viết một tập lệnh hướng dẫn cách xử lý một chương trình mà nó không hiểu theo mặc định. Wrapper cho codegen WebIDL, webidl.lua, cần trả lời một vài câu hỏi cho buildcache:

  • Tôi có thể xử lý lệnh này không? Khớp chuỗi mozbuild.action.webidl trong danh sách đối số.
  • Đầu vào là gì? Tất cả các tệp nguồn .webidl, cộng với các tập lệnh codegen Python. Chúng đến từ file-lists.json (do mach tạo) và codegen.json (theo dõi các phụ thuộc Python từ lần chạy trước).
  • Đầu ra là gì? Tất cả các header binding, tệp cpp, tệp sự kiện và tệp trạng thái codegen đã tạo. Một lần nữa, chúng được lấy từ file-lists.json.

Với thông tin đó, buildcache có thể băm (hash) các đầu vào, kiểm tra cache, và hoặc là phát lại các đầu ra đã cache hoặc chạy lệnh thực sự và lưu trữ kết quả.

Wrapper sử dụng khả năng direct_mode của buildcache, nghĩa là nó băm các tệp đầu vào trực tiếp thay vì dựa vào đầu ra đã được tiền xử lý. Đây là cách tiếp cận đúng đắn ở đây vì chúng ta không đang xử lý một bộ tiền xử lý C mà là một tập lệnh Python đọc các tệp .webidl.

Con số biết nói

Dưới đây là thời gian build cho lệnh ./mach build trên Linux, so sánh các công cụ cache trình biên dịch. Mỗi hàng hiển thị một lần build sạch với cache trống (cold), theo sau là một lần build sạch với cache đã đầy (warm):

Công cụCold (Cache trống)Warm (Cache đầy)Với Plugin
Không có5m35sn/an/a
ccache5m42s3m21sn/a
sccache9m38s2m49sn/a
buildcache5m43s1m27s1m12s

Cột "Với Plugin" là buildcache với wrapper webidl.lua được kích hoạt. Nó cắt giảm thêm 15 giây, đưa tổng thời gian xuống còn 1m12s. Mặc dù không phải là một bước đột phá cách mạng, nhưng nó chứng minh cơ chế hoạt động. Bước WebIDL chỉ là hành động Python đầu tiên được áp dụng cách xử lý này; còn có các bước codegen khác trong quá trình build có thể hưởng lợi từ cách tiếp cận tương tự.

Một cách rộng hơn, các con số này cho thấy buildcache đang vượt xa hẳn ở các lần build warm. Việc giảm từ 5m35s cho một build sạch xuống còn 1m12s cho một lần build lại có cache là một sự cải tiến khá tốt cho chu trình chỉnh sửa - biên dịch - kiểm tra (edit-compile-test).

Đây là các lần chạy đơn trên một máy, không phải là điểm chuẩn khắt khe, nhưng xu hướng đã đủ rõ ràng.

Cách cài đặt

Nếu bạn đã sử dụng buildcache với mach, thay đổi Makefile đã có sẵn khi cập nhật lên central mới nhất. Để kích hoạt wrapper Lua, hãy clone repo buildcache-wrappers và trỏ buildcache đến nó thông qua lua_paths trong ~/.buildcache/config.json:

{
  "lua_paths": ["/path/to/buildcache-wrappers/mozilla"],
  "max_cache_size": 10737418240,
  "max_local_entry_size": 2684354560
}

Ngoài ra, bạn có thể đặt biến môi trường BUILDCACHE_LUA_PATH. Một nơi thuận tiện để làm điều đó là trong mozconfig của bạn:

mk_add_options "export BUILDCACHE_LUA_PATH=/path/to/buildcache-wrappers/mozilla/"

Giá trị max_local_entry_size lớn (2.5 GB) là cần thiết vì một số thùng chứa (crate) Rust tạo ra các mục cache rất lớn.

Tiếp theo là gì?

Hệ thống plugin Lua là phần thú vị ở đây. Wrapper WebIDL là một khái niệm chứng minh (proof of concept), nhưng cùng một kỹ thuật này có thể áp dụng cho bất kỳ bước build xác định nào có đầu vào đã biết và tạo ra đầu ra đã biết. Có các hành động codegen khác trong quá trình build Firefox có thể nhận được sự xử lý tương tự, và tôi dự định sẽ khám phá những cái đó tiếp theo.

Lưu thích: 1. Cho một lần build sạch với cache ấm. 2. Trên máy của tôi.

Bài viết được tổng hợp và biên soạn bằng AI từ các nguồn tin tức công nghệ. Nội dung mang tính tham khảo. Xem bài gốc ↗