Ngày phát hành: 29 tháng 1 năm 2025
Thu gom rác WebAssembly (WasmGC)
Có hai loại ngôn ngữ lập trình: ngôn ngữ lập trình thu gom rác và ngôn ngữ lập trình yêu cầu quản lý bộ nhớ theo cách thủ công. Ví dụ về ngôn ngữ lập trình hướng đối tượng có Kotlin, PHP hoặc Java. Ví dụ về ngôn ngữ lập trình này là C, C++ hoặc Rust. Theo nguyên tắc chung, các ngôn ngữ lập trình cấp cao hơn có nhiều khả năng có tính năng thu gom rác làm tính năng tiêu chuẩn.
Nói một cách đơn giản, thu gom rác là nỗ lực lấy lại bộ nhớ do chương trình phân bổ nhưng không còn được tham chiếu nữa. Bộ nhớ như vậy được gọi là rác. Có nhiều chiến lược để triển khai tính năng thu gom rác. Một trong những phương pháp dễ hiểu nhất là đếm tham chiếu, trong đó mục tiêu là đếm số lượng tham chiếu đến các đối tượng trong bộ nhớ.
Có vẻ như đây là một khái niệm mới, nhưng các ngôn ngữ lập trình được triển khai trong các ngôn ngữ lập trình khác. Ví dụ: môi trường thời gian chạy PHP chủ yếu được triển khai trong C. Nếu muốn biên dịch một ngôn ngữ như PHP sang Wasm, nhà phát triển thường cần biên dịch tất cả các phần, chẳng hạn như trình phân tích cú pháp của ngôn ngữ, hỗ trợ thư viện, thu gom rác và các thành phần quan trọng khác.
Wasm chạy trong trình duyệt trong ngữ cảnh của ngôn ngữ lưu trữ JavaScript. Trong Chrome, JavaScript và Wasm chạy trong V8, công cụ JavaScript nguồn mở của Google. Ngoài ra, V8 đã có trình thu gom rác. Điều này có nghĩa là các nhà phát triển sử dụng, ví dụ: PHP được biên dịch sang Wasm, cuối cùng sẽ gửi một bản triển khai trình thu gom rác của ngôn ngữ được chuyển (PHP) đến trình duyệt đã có trình thu gom rác, điều này thật lãng phí. Đây là lúc WasmGC phát huy tác dụng.
Để tìm hiểu thêm về WasmGC, hãy đọc bài viết Tính năng thu gom rác WebAssembly (WasmGC) hiện được bật theo mặc định trong Chrome. Nếu bạn muốn tìm hiểu sâu hơn, hãy xem bài đăng trên blog V8 Một cách mới để đưa các ngôn ngữ lập trình được thu gom rác một cách hiệu quả vào WebAssembly
Tối ưu hoá lệnh gọi đuôi Wasm
Lệnh gọi được cho là ở vị trí đuôi nếu đó là lệnh cuối cùng được thực thi trước khi trả về từ hàm hiện tại. Trình biên dịch có thể tối ưu hoá các lệnh gọi như vậy bằng cách loại bỏ khung lệnh gọi và thay thế lệnh gọi bằng một lệnh nhảy. Điều này đặc biệt hữu ích cho các hàm đệ quy. Ví dụ: hãy lấy hàm C này để tính tổng các phần tử của danh sách liên kết:
int sum(List* list, int acc) {
if (list == nullptr) return acc;
return sum(list->next, acc + list->val);
}
Với lệnh gọi thông thường, lệnh gọi này sẽ tiêu tốn không gian ngăn xếp O(n): mỗi phần tử của danh sách sẽ thêm một khung mới vào ngăn xếp lệnh gọi. Với một danh sách đủ dài, điều này có thể nhanh chóng làm tràn ngăn xếp. Bằng cách thay thế lệnh gọi bằng một lệnh nhảy, tính năng tối ưu hoá lệnh gọi đuôi sẽ biến hàm đệ quy này thành một vòng lặp sử dụng không gian ngăn xếp O(1):
int sum(List* list, int acc) {
while (list != nullptr) {
acc = acc + list->val;
list = list->next;
}
return acc;
}
Việc tối ưu hoá này đặc biệt quan trọng đối với các ngôn ngữ chức năng. Các ngôn ngữ này phụ thuộc nhiều vào các hàm đệ quy và các hàm thuần tuý như Haskell thậm chí không cung cấp cấu trúc điều khiển vòng lặp. Bất kỳ loại lặp lại tuỳ chỉnh nào cũng thường sử dụng phương thức đệ quy theo cách này hay cách khác. Nếu không có tính năng tối ưu hoá lệnh gọi đuôi, việc này sẽ nhanh chóng dẫn đến tình trạng tràn ngăn xếp cho bất kỳ chương trình không quan trọng nào, nếu không thì sẽ nhanh chóng hết không gian ngăn xếp.
Ban đầu, WebAssembly không cho phép tối ưu hoá lệnh gọi đuôi như vậy, nhưng điều này đã thay đổi với Đề xuất về tiện ích lệnh gọi đuôi. Để tìm hiểu sâu hơn, hãy đọc bài viết Lệnh gọi đuôi WebAssembly trên blog V8.
Kết luận
Giờ đây, với việc WasmGC và tính năng tối ưu hoá lệnh gọi đuôi được cung cấp dưới dạng Đường cơ sở mới, nhiều ứng dụng hơn có thể sử dụng các tính năng này để đạt được hiệu suất tốt hơn, chẳng hạn như Google Trang tính đã làm bằng cách chuyển công cụ tính toán của Google Trang tính sang WasmGC.