2 điểm bởi GN⁺ 2024-04-15 | 1 bình luận | Chia sẻ qua WhatsApp

Giới hạn của WebAssembly và tầm quan trọng của Tree-shaking

  • Dù nhận được nhiều sự quan tâm và kỳ vọng, WebAssembly chỉ đạt được thành công khá hạn chế trên web

    • Có những trường hợp thành công như Photoshop, nhưng nhìn chung không có nhiều dự án tận dụng WebAssembly
    • Đặc biệt, WebAssembly không phù hợp với các ứng dụng dùng DOM nhiều
    • Sự khác biệt trong mô hình lập trình giữa JavaScript và WebAssembly là một trong những nguyên nhân chính
  • WebAssembly ngoài các ngôn ngữ như C hay Rust thì chưa thật sự thành công

    • Các ngôn ngữ như C# có bất tiện là phải đi kèm cả runtime như garbage collector
    • Tuy nhiên, các tính năng mới của WebAssembly hỗ trợ reference type và garbage collection sẽ sớm được đưa vào, nên tình hình được kỳ vọng sẽ cải thiện

Khả năng tối ưu mã của trình biên dịch là chìa khóa cho thành công của WebAssembly

  • Để WebAssembly thành công trên web, trình biên dịch phải tạo ra được mã nhỏ gọn và hiệu quả

    • Điều quan trọng là phải giữ kích thước tệp ở mức nhỏ, chỉ vài kilobyte
    • Nếu không làm được điều đó thì chỉ còn cách dựa vào sự cường điệu hoặc một tệp người dùng cụ thể
  • Trong thế giới JavaScript, việc tối ưu kích thước mã đang được thực hiện thông qua bundler và các công cụ tương tự

    • Tree-shaking là kỹ thuật chỉ đưa vào chương trình những hàm và kiểu dữ liệu thực sự được sử dụng
  • Tree-shaking là một phép ẩn dụ không phù hợp xét về mặt làm vườn lẫn thuật toán, nhưng đây vẫn là thuật ngữ được dùng rộng rãi

Tình hình Tree-shaking ở các ngôn ngữ khác

  • Với các ngôn ngữ có runtime nặng như Go hay Python, Tree-shaking vẫn chưa được tối ưu tốt

    • Ngay cả chương trình Go đơn giản nhất khi biên dịch sang WebAssembly cũng có kích thước hơn 2MB
    • Pyodide của Python cũng cần tải xuống khoảng 20MB dữ liệu
  • Trong môi trường máy chủ, kích thước binary không phải là vấn đề quá lớn

    • Vì vậy, cho những môi trường bị hạn chế như di động, các toolchain rút gọn như MicroPython, TinyGo cũng được phát triển riêng
  • Các implementation ngôn ngữ dành cho môi trường web khó tránh khỏi việc khác với bản gốc

    • Bởi bản thân việc tương tác với DOM đã là một môi trường khá đặc thù
    • Trong trường hợp của ClojureScript, các khác biệt so với Clojure được tài liệu hóa riêng

Bàn về thuật toán Tree-shaking

  • Trình biên dịch Hoot Scheme mà tác giả đang phát triển hiện tạo ra mã Wasm cỡ khoảng 70KB

    • Việc chỉ giữ lại các định nghĩa hàm (procedure) tương đối dễ
    • Nhưng vẫn có một số điểm khó như bên dưới
  • Trong mô hình đánh giá letrec*, các binding vừa mang tính đệ quy vừa có thứ tự, khiến trình biên dịch khó phân tích

    • Với record type, các callback trong vtable khiến nhiều đoạn mã bị giữ lại
  • Khi dùng các hàm có tính đa hình cao như display, rất nhiều mã liên quan sẽ bị kéo vào

    • Dùng các hàm cụ thể như write-string sẽ tốt hơn
  • Để Tree-shaking tối ưu, cần có flow analysis

    • Nếu biết rằng đối số bitvector sẽ không được truyền vào display, ta có thể loại bỏ phần mã liên quan
  • Trong Python, việc này còn khó hơn do dynamic dispatch, __getattr__ và các tính năng động khác

    • Cấu trúc module của Python cũng là một yếu tố làm Tree-shaking trở nên phức tạp

Tóm tắt

  • Nhờ hỗ trợ GC, WebAssembly giúp có thể lập trình DOM bằng các ngôn ngữ ngoài JavaScript
  • Nhưng để làm cho kích thước đầu ra đủ nhỏ, cần đầu tư đáng kể vào toolchain của từng ngôn ngữ
  • Cần phát triển các toolchain riêng có áp dụng thuật toán Tree-shaking và tối ưu hóa thư viện chuẩn

Ý kiến của GN⁺

  • Khi WebAssembly hỗ trợ GC, việc dùng nhiều ngôn ngữ khác nhau cho phát triển web đã trở nên khả thi hơn, nhưng có vẻ vẫn còn nhiều khó khăn nếu muốn mang nguyên trạng toolchain của các ngôn ngữ hiện có sang. Có lẽ cần phát triển các implementation ngôn ngữ và kỹ thuật tối ưu hóa chuyên biệt cho môi trường web.

  • Để Tree-shaking hoạt động tốt với các ngôn ngữ kiểu động, có lẽ phân tích tĩnh là điều bắt buộc. Nhưng với những ngôn ngữ như Python có quá nhiều tính năng động thì điều này sẽ không hề dễ. Biết đâu một hướng đi khác là tạo mới ngay từ đầu một ngôn ngữ thuận lợi hơn cho phân tích tĩnh.

  • Những dự án thử nghiệm như Hoot hay TinyGo có vẻ sẽ là tài liệu tham khảo tốt. Tuy nhiên, để áp dụng các dự án như vậy vào sản phẩm thực tế thì có lẽ vẫn còn hơi sớm. Có lẽ chỉ còn cách cải thiện dần từng bước.

  • Nếu dự án không quá nhạy cảm về hiệu năng và ưu tiên tốc độ phát triển, các lựa chọn như Pyodide có thể đáng để thử. Nhưng nếu là sản phẩm coi trọng trải nghiệm người dùng, thì ở thời điểm hiện tại JavaScript có lẽ vẫn là lựa chọn tốt nhất.

  • Cũng có thể cân nhắc việc đưa những tính năng như Tree-shaking vào chính WebAssembly. Nhưng vì yêu cầu của từng ngôn ngữ là khác nhau nên chắc điều đó sẽ không dễ. Mặt khác, nếu xuất hiện một ngôn ngữ hỗ trợ Tree-shaking rất tốt thì có khi viết luôn bằng ngôn ngữ đó lại có lợi hơn. Thật tò mò không biết sự phân chia vai trò giữa WebAssembly và ngôn ngữ lập trình rồi sẽ hình thành ra sao.

1 bình luận

 
GN⁺ 2024-04-15
Ý kiến trên Hacker News

Tóm lại như sau:

  • Trong dự án OpenEtG, đã nỗ lực giữ kích thước tệp nhị phân WASM viết bằng Rust dưới 400KB bằng các cách sau
    • dùng phép toán số cố định thay cho float
    • dùng Vec thay cho HashMap
    • giảm thiểu việc dùng chuỗi
    • dùng bộ cấp phát nhỏ (talc)
    • giảm thiểu phụ thuộc (chỉ dùng rand, fxhash)
    • tránh sự đa dạng của generic
    • thiết kế thuật toán có tính đến kích thước
  • Tree-shaking là một cách đặt tên sai lệch, và trong trình biên dịch Virgil, nó được gọi là Reachability Analysis. Trong quá trình biên dịch, trình biên dịch lần theo từ điểm vào main và chỉ đưa mã có thể tiếp cận được vào tệp nhị phân cuối cùng.
  • Nhờ WasmGC, Java và Kotlin có thể tạo ra các tệp nhị phân WASM nhỏ chỉ khoảng 2-3KB. Tuy nhiên, cần cẩn thận khi chọn API.
  • Việc thao tác DOM bằng WASM vẫn còn phụ thuộc vào JS.
  • Lý do xuất hiện thuật ngữ Tree Shaking là vì Dead Code Elimination đã tồn tại từ rất lâu.
  • Vấn đề kích thước mã của WASM phát sinh vì phải đóng gói cả runtime ngôn ngữ lẫn thư viện chuẩn.
  • Để giải quyết điều này, có thể cân nhắc thư viện dùng chungliên kết động.
    • Pyodide là một ví dụ tiêu biểu hỗ trợ liên kết động
    • Nếu trình duyệt tải sẵn các runtime ngôn ngữ phổ biến, các trang web có thể dùng chung những runtime đó
  • Ngôn ngữ Zig phù hợp để tạo các tệp nhị phân WASM nhỏ. Tuy nhiên, nếu dưới 100KB thì kích thước không còn là yếu tố quan trọng.
  • GC tích hợp không quan trọng với mọi ứng dụng, và tốt hơn là nên tạo web app không có GC.
  • Yếu tố thành công của các ứng dụng dùng WASM vẫn là cải thiện hiệu năng.
  • Từ lâu, người ta đã lập trình DOM bằng các ngôn ngữ khác ngoài JS thông qua các ngôn ngữ biên dịch sang JS như ClojureScript, TypeScript, ReasonML.
  • Trước cả WASM, người ta cũng đã biên dịch và dùng các ngôn ngữ họ C trên web thông qua asm.js và emscripten.
  • Google Maps và Google Earth là những ứng dụng tiêu biểu sử dụng WASM.