11 điểm bởi GN⁺ 2026-03-21 | 2 bình luận | Chia sẻ qua WhatsApp
  • Trình phân tích WASM viết bằng Rust có cấu trúc vốn nhanh, nhưng chi phí sao chép dữ liệu và tuần tự hóa ở ranh giới JS-WASM đã bộc lộ là nút thắt hiệu năng
  • Trả về object trực tiếp qua serde-wasm-bindgen chậm hơn 9~29% so với tuần tự hóa JSON, do chi phí chuyển đổi tinh vi giữa các runtime
  • Khi port toàn bộ pipeline sang TypeScript, hệ thống đạt được hiệu năng cho mỗi lần gọi nhanh hơn 2,2~4,6 lần trên cùng một kiến trúc
  • Trong xử lý streaming, việc cải thiện O(N²) → O(N) bằng bộ nhớ đệm theo câu giúp đạt tốc độ xử lý tổng thể nhanh hơn 2,6~3,3 lần
  • Kết quả cho thấy, WASM phù hợp với tác vụ nặng về tính toán và ít gọi, nhưng không phù hợp với việc phân tích object JS hoặc các hàm bị gọi thường xuyên

Cấu trúc và giới hạn của trình phân tích Rust WASM

  • Trình phân tích openui-lang là một pipeline 6 bước dùng để chuyển DSL do LLM tạo ra thành cây component React
    • Các bước: autocloser → lexer → splitter → parser → resolver → mapper → ParseResult
    • Mỗi bước đảm nhận token hóa, phân tích cú pháp, diễn giải biến, chuyển đổi AST, v.v.
  • Bản thân mã Rust chạy nhanh, nhưng quá trình sao chép chuỗi, tuần tự hóa JSON và giải tuần tự giữa JS↔WASM diễn ra ở mỗi lần gọi
    • Sao chép chuỗi đầu vào (JS→WASM), phân tích trong Rust, tuần tự hóa kết quả sang JSON, sao chép JSON (WASM→JS), rồi giải tuần tự ở JS
  • Chi phí tại ranh giới này chi phối toàn bộ hiệu năng, còn tốc độ tính toán của Rust không phải là nút thắt

Thử nghiệm với serde-wasm-bindgen và thất bại

  • Để tránh tuần tự hóa JSON, tác giả áp dụng serde-wasm-bindgen để trả trực tiếp struct Rust thành object JS
  • Tuy nhiên, kết quả quan sát được là chậm hơn 30%
    • JS không thể đọc trực tiếp bộ nhớ của struct Rust, và layout bộ nhớ giữa các runtime khác nhau nên cần chuyển đổi theo từng field
    • Trong khi đó, tuần tự hóa JSON chỉ cần tạo chuỗi một lần trong Rust, rồi được xử lý bằng JSON.parse đã được tối ưu ở phía JS
  • Kết quả benchmark
    Fixture JSON round-trip serde-wasm-bindgen Thay đổi
    simple-table 20.5µs 22.5µs -9%
    contact-form 61.4µs 79.4µs -29%
    dashboard 57.9µs 74.0µs -28%

Chuyển sang TypeScript và cải thiện hiệu năng

  • Toàn bộ cấu trúc 6 bước giống hệt được port hoàn toàn sang TypeScript, loại bỏ ranh giới WASM và chạy trực tiếp trong heap của V8
  • Kết quả benchmark theo từng lần gọi
    Fixture TypeScript WASM Mức tăng tốc
    simple-table 9.3µs 20.5µs 2.2 lần
    contact-form 13.4µs 61.4µs 4.6 lần
    dashboard 19.4µs 57.9µs 3.0 lần
  • Chỉ riêng việc loại bỏ WASM đã giúp giảm mạnh chi phí cho mỗi lần gọi, nhưng sự kém hiệu quả trong cấu trúc streaming vẫn còn tồn tại

Vấn đề O(N²) trong phân tích streaming và cách cải thiện

  • Khi đầu ra của LLM được truyền theo nhiều chunk, việc phải phân tích lại toàn bộ chuỗi tích lũy mỗi lần gây ra sự kém hiệu quả O(N²)
    • Ví dụ: tài liệu 1000 ký tự được phân tích 50 lần, mỗi lần 20 ký tự → tổng cộng xử lý 25.000 ký tự
  • Giải pháp là đưa vào bộ nhớ đệm tăng dần theo câu (incremental caching)
    • Các câu đã hoàn thành được lưu vào cache, chỉ câu đang dở mới bị phân tích lại
    • AST đã cache được hợp nhất với AST mới để trả kết quả
  • Benchmark theo toàn bộ luồng stream
    Fixture TS ngây thơ TS tăng dần Mức tăng tốc
    simple-table 69µs 77µs Không có
    contact-form 316µs 122µs 2.6 lần
    dashboard 840µs 255µs 3.3 lần
  • Càng nhiều câu, hiệu quả của cache càng lớn và tổng thông lượng được cải thiện theo tuyến tính

Bài học về việc sử dụng WASM

  • Trường hợp phù hợp
    • Tác vụ nặng về tính toán, ít tương tác: xử lý ảnh/video, mã hóa, mô phỏng vật lý, codec âm thanh, v.v.
    • Port thư viện native hiện có: SQLite, OpenCV, libpng, v.v.
  • Trường hợp không phù hợp
    • Phân tích văn bản có cấu trúc thành object JS: chi phí tuần tự hóa chiếm ưu thế
    • Các hàm nhận đầu vào ngắn nhưng bị gọi thường xuyên: chi phí ranh giới lớn hơn cả tính toán
  • Bài học cốt lõi
    1. Cần profiling điểm nghẽn trước khi chọn ngôn ngữ
    2. Truyền object trực tiếp bằng serde-wasm-bindgen tốn kém hơn
    3. Cải thiện độ phức tạp thuật toán hiệu quả hơn việc đổi ngôn ngữ
    4. WASM và JS không chia sẻ heap, nên chi phí chuyển đổi luôn tồn tại

Kết quả cuối cùng: Nhờ chuyển sang TypeScript và áp dụng incremental caching, hệ thống đạt mức tăng hiệu năng 2,2~4,6 lần cho mỗi lần gọi và 2,6~3,3 lần cho toàn bộ stream

2 bình luận

 
bbulbum 2026-03-23

Chẳng phải có lẽ đây là một bài viết mỉa mai kiểu vòng vo về việc tối ưu hiệu năng Rust ở mức độ cao sao..

 
GN⁺ 2026-03-21
Ý kiến trên Hacker News
  • Điểm cốt lõi thật sự không phải là TypeScript so với Rust, mà là việc sửa thuật toán streaming từ O(N²) xuống O(N)
    Chỉ riêng thay đổi này, được thực hiện bằng cách cache theo đơn vị câu lệnh (statement), đã mang lại mức cải thiện 3,3 lần
    Tách khỏi chuyện chọn ngôn ngữ nào, đây mới là nguyên nhân chính khiến độ trễ (latency) mà người dùng cảm nhận được được cải thiện
    Có cảm giác tiêu đề đã đánh giá thấp điểm kỹ thuật thú vị này

    • Dự án uv cũng vậy. Mọi người chỉ hô “rust rulez!”, nhưng lợi ích thực sự đến từ cải thiện thuật toán, chứ không phải ngôn ngữ
    • Cảm ơn vì đã vượt qua lớp clickbait để chỉ ra bản chất
      Bản thân bài viết thì thú vị, nhưng dạo này tôi đã quá mệt với những tiêu đề câu click quá đà
    • Cách diễn đạt n² có vẻ hơi cường điệu
      Họ đo thời gian của từng lần gọi rồi dùng trung vị (median), nhưng trong môi trường trình duyệt có logic phòng thủ timing attack trong JS engine, nên tôi nghi ngờ độ chính xác
    • Cuối cùng thì tôi nghĩ nó gần với một tiêu đề dễ gây hiểu nhầm hơn
  • Câu chuyện “viết lại mã từ ngôn ngữ L sang M rồi nhanh hơn” là điều quá đỗi bình thường
    Vì đó là cơ hội để gỡ rối các quyết định chồng chéo và sai lầm, đồng thời áp dụng cách tiếp cận tốt hơn mới xuất hiện
    Thực ra ngay cả khi L=M cũng vậy: tăng tốc không đến từ ngôn ngữ, mà đến từ quá trình viết lại và thiết kế lại

    • Giờ nếu một bên thứ ba không biết bản gốc mà lại viết lại phiên bản TypeScript sang Rust, biết đâu hiệu năng còn tăng nữa
    • Tôi khá thường xuyên thấy hiệu quả cải thiện xuất hiện ngay cả khi viết lại bằng cùng một ngôn ngữ
  • Tôi từng đào sâu hơn để cải thiện hiệu năng tuần tự hóa đối tượng ở ranh giới giữa Rust và JS
    Cách tiếp cận của serde có vẻ không tốt về mặt hiệu năng, nên tôi đã tổng hợp một nỗ lực cải thiện nó trong bài blog của mình

  • Tôi từng thắc mắc vì sao Open UI lại không làm việc gì liên quan đến WASM
    Nhưng rồi công ty mới này lại dùng tên Open UI, nên khá dễ gây nhầm lẫn
    Vốn dĩ Open UI W3C Community Group là nhóm đã hơn 5 năm nay xây dựng các tiêu chuẩn như popover của HTML, select có thể tùy biến, invoker command, accordion, v.v.
    Họ thực sự đang làm công việc rất tuyệt

  • Họ nói đã tích hợp serde-wasm-bindgen trong nỗ lực “bỏ qua việc JSON đi một vòng”, nhưng rốt cuộc nó trông giống như phát minh lại JSON ở dạng nhị phân
    JSON trên V8 hiện nay đã được tối ưu hóa rất mạnh, và các triển khai như simdjson có thể xử lý ở mức hàng gigabyte mỗi giây
    Tôi không nghĩ JSON có nhiều khả năng là nút thắt cổ chai

  • Tôi thực sự rất thích thiết kế của blog đó
    Đặc biệt là thanh bên ‘scrollspy’ làm nổi bật heading theo vị trí cuộn
    Theo Claude nói thì có vẻ nó được dựng bằng fumadocs.dev

    • Thú vị đấy. Tôi cũng đang nghĩ sớm muộn gì mình nên làm một trang tài liệu tử tế
  • Tôi không thực sự hiểu mục đích của bộ phân tích Rust WASM
    Bài viết không làm rõ phần đó, nên cần giải thích thêm

    • Có vẻ họ dùng một ngôn ngữ chuyên dụng để định nghĩa các UI component do LLM sinh ra
      Điều này dường như nhằm ngăn rò rỉ thông tin do prompt injection
      Bộ phân tích sẽ biên dịch các chunk được stream từ LLM để dựng UI theo thời gian thực
      Trước đây họ khởi động lại parser từ đầu cho mỗi chunk, nhưng sau đó đổi sang cách xử lý tăng dần, và trong quá trình port từ Rust sang TypeScript, hiệu năng đã cải thiện đáng kể
  • Tôi từng thắc mắc liệu TypeScript dạo này có đang chạy trên nền Golang hay không

    • Có một dự án đang tiến hành viết lại trình biên dịch TypeScript bằng Go, chắc là đang nói đến chuyện đó
  • Nói đùa thôi, nhưng biết đâu nếu lại viết lại bằng Rust thì sẽ có thêm mức tăng hiệu năng gấp 3 lần nữa /s