Câu chuyện phía sau Bun Install
(bun.com)- Cài đặt gói của Bun hoạt động với tốc độ rất nhanh so với các trình quản lý gói hiện có
- Cốt lõi của tốc độ cài đặt cao nằm ở cách tiếp cận theo góc nhìn lập trình hệ thống và giảm thiểu system call
- Hiệu năng được cải thiện nhờ áp dụng các chiến lược tinh chỉnh như lập trình native bằng ngôn ngữ Zig, sử dụng bộ nhớ đệm nhị phân và tối ưu hóa theo từng hệ điều hành
- Ngay cả trong quá trình giải nén tarball và sao chép tệp, Bun cũng áp dụng các phương pháp hiệu năng cao tận dụng đặc tính phần cứng
- Thông qua tối ưu hóa cấu trúc dữ liệu như đồ thị phụ thuộc và lockfile, Bun nâng cao hiệu quả cache CPU và khả năng truy cập bộ nhớ
Vì sao Bun Install nhanh
bun installcủa Bun trung bình mang lại hiệu năng cài đặt gói nhanh hơn npm 7 lần, pnpm 4 lần và yarn 17 lần- Đây không chỉ là khác biệt trong benchmark, mà còn đến từ việc tiếp cận bài toán cài đặt gói từ góc nhìn lập trình hệ thống thay vì JavaScript
- Bun tích cực áp dụng tối ưu hiệu năng ở nhiều tầng như giảm số lượng system call, lưu cache nhị phân cho manifest, tối ưu giải nén tarball và sao chép tệp native theo hệ điều hành
Giới hạn của Node.js và kiến trúc trình quản lý gói
- Từ sau khi Node.js ra mắt năm 2009, mô hình IO bất đồng bộ dựa trên event loop và thread pool cũng được lan sang các trình quản lý gói
- Vào thời điểm đó, do hạn chế phần cứng như ổ đĩa chậm và mạng chậm, chiến lược IO bất đồng bộ với tần suất system call cao là hợp lý
- Tuy nhiên, trên các hệ thống hiện đại, SSD NVMe, mạng nhanh và CPU hiệu năng cao đã trở nên phổ biến, và nút thắt thực sự không còn là IO mà là overhead của system call
Chi phí của system call và chuyển đổi chế độ
- Khi chương trình yêu cầu thực hiện tác vụ như đọc tệp, nó phải chuyển từ user mode sang kernel mode, và quá trình này tiêu tốn một lượng chu kỳ CPU đắt đỏ (1000~1500 cycles)
- Về bản chất, cài đặt gói cần tới hàng chục nghìn đến hàng trăm nghìn system call, nên chỉ riêng chi phí chuyển đổi tác vụ thực tế cũng có thể tiêu tốn vài giây thời gian CPU
- Ví dụ, khi cài đặt React và các phụ thuộc, npm sử dụng khoảng 1 triệu system call, yarn 4 triệu, pnpm 500 nghìn, còn bun là 160 nghìn
Khác biệt trong cách tiếp cận giữa các trình quản lý gói hiện có và Bun
- npm, pnpm và yarn đều dựa trên Node.js, nên JavaScript phải chạy qua nhiều lớp trừu tượng như libuv, event loop, thread pool và tầng trung gian của system call
- Trong quá trình đó, việc chuyển đổi đối số, hàng đợi worker pool, phân nhánh công việc trong event loop và các system call futex (đồng bộ khóa) bị cộng dồn, khiến việc quản lý system call thậm chí còn chậm hơn cả IO
- Các trình quản lý gói viết bằng Node.js khó đạt được hiệu năng tiệm cận native do các giới hạn mang tính cấu trúc này
Bun: engine cài đặt native được triển khai bằng Zig
- Bun gọi trực tiếp system call bằng ngôn ngữ Zig, bỏ qua hoàn toàn JavaScript engine và các lớp trừu tượng
- Ví dụ, việc đọc tệp được thực hiện bằng cách gọi trực tiếp system call
openat()trong mã Zig để trả về dữ liệu ngay lập tức - Vì vậy, quá trình đọc hàng chục nghìn tệp có thể chạy cực nhanh mà không cần thread pool, event loop hay bước chuyển đổi dữ liệu riêng biệt
- Theo benchmark, Bun có thể đọc 146.057 file
package.jsonmỗi giây, trong khi Node.js chậm hơn hơn 2 lần ở mức khoảng 60 nghìn file
Tối ưu quản lý phụ thuộc và DNS
- Khi chạy
bun install, Bun sẽ phân tích phụ thuộc đồng thời kích hoạt DNS prefetch một cách bất đồng bộ - Chẳng hạn trên macOS, Bun sử dụng API DNS bất đồng bộ không chính thức của Apple (
getaddrinfo_async_start()), hỗ trợ xử lý đồng thời tác vụ mạng mà không chặn thread - Các trình quản lý gói hiện có dựa trên thread pool của libuv, nên trên thực tế vẫn chạy mã blocking bên trong và gây lãng phí tài nguyên
Lưu cache nhị phân cho manifest gói
- npm và các công cụ tương tự lưu cache manifest ở dạng JSON, còn Bun sau khi parse một lần sẽ chuyển kết quả đó sang dạng nhị phân (
.npm) để lưu trữ - Giảm thiểu trùng lặp chuỗi và overhead parse, đồng thời trong bộ nhớ thực tế có thể truy cập giá trị ngay bằng cách tính offset mà không cần tạo object mới, parse lại hay garbage collection
- Bun dùng header ETag và
If-None-Matchđể chỉ kiểm tra phần thay đổi, giúp xác minh tính cập nhật mà không cần parse dữ liệu không cần thiết - Theo benchmark, cài đặt từ cache của Bun còn nhanh hơn cả fresh install của npm
Hiệu năng xử lý tarball (tệp nén)
- Các trình quản lý gói thông thường nhận tarball dưới dạng stream, và mỗi khi bộ nhớ đệm không đủ sẽ liên tục phát sinh cấp phát lại, sao chép và thay đổi kích thước
- Bun nhận toàn bộ tarball rồi mới giải nén, đồng thời dùng 4 byte cuối của gzip để biết trước kích thước sau giải nén → chỉ cấp phát bộ nhớ đúng một lần
- Bun cũng tận dụng
libdeflateđể giải nén nhanh, đồng thời loại bỏ hoàn toàn việc sao chép dư thừa và thay đổi kích thước không cần thiết
Tối ưu đồ thị phụ thuộc và cấu trúc dữ liệu
- Các trình quản lý gói hiện có tạo cây phụ thuộc dựa trên object/pointer JavaScript, dẫn tới bộ nhớ bị phân tán ngẫu nhiên và thường xuyên xảy ra cache miss của CPU (vấn đề pointer chasing)
- Bun áp dụng mô hình Structure of Arrays (SoA), lưu toàn bộ package, chuỗi và phụ thuộc trong các khối bộ nhớ liên tục lớn
- Truy cập dựa trên offset/độ dài cho phép CPU đọc nhiều package cùng lúc theo đơn vị cache line (cấu trúc thân thiện với cache)
- Lockfile cũng được lưu theo mô hình SoA thay vì JSON/YAML, giúp loại bỏ trùng lặp chuỗi và thuận lợi cho truy cập bộ nhớ tuần tự
- Bun từng thử nghiệm định dạng lockfile nhị phân (
bun.lockb), nhưng sau đó chuyển sang định dạng văn bản dễ đọc hơn do làm giảm khả năng cộng tác với Git
Tối ưu sao chép tệp theo từng hệ điều hành
macOS
- Dùng
clonefile: sao chép toàn bộ thư mục bằng cơ chế Copy-On-Write chỉ với một system call - Giảm tối đa việc dùng trùng lặp dung lượng đĩa và tăng tốc độ cài đặt lên mức cao nhất
- Nếu
clonefilethất bại, Bun sẽ fallback theo từng bước từ cloning theo từng thư mục sangcopyfile
Linux
- Ưu tiên thử hard link: không tạo tệp mới mà chỉ tạo tham chiếu mới tới tệp hiện có (không cần di chuyển dữ liệu trên đĩa)
- Nếu không thể dùng hard link, trên Btrfs/XFS Bun sẽ áp dụng Copy-On-Write bằng
ioctl_ficlone - Sau đó mới fallback lần lượt sang
copy_file_range,sendfile, và cuối cùng là cáchcopyfilethông thường
Tổng kết
- Bun đã vượt qua giới hạn hiệu năng truyền thống của trình quản lý gói nhờ giảm thiểu system call, cấu trúc nhị phân, tối ưu theo hệ điều hành và cải tiến cấu trúc dữ liệu
- Nhờ đó, Bun không chỉ cài đặt cực nhanh mà còn cải thiện cả hiệu quả bộ nhớ và CPU
- So với các trình quản lý dựa trên Node.js hiện có, Bun có thể được áp dụng vào dự án mà không cần thay runtime riêng biệt (vẫn giữ khả năng tương thích)
- Trong các codebase lớn ngoài thực tế, Bun mang lại trải nghiệm rút ngắn quá trình cài đặt từ vài phút xuống còn vài mili giây đến vài giây
- Đây là một ví dụ xuất sắc về tối ưu hóa được thiết kế sát với cấp độ hệ thống, phần cứng và hệ điều hành, rất đáng để nghiên cứu và tham khảo
1 bình luận
Ý kiến trên Hacker News
Tôi đã thử kiểm chứng nhận định rằng chiếc MacBook M4 Max tôi đang dùng lẽ ra đã có thể lọt vào top 50 siêu máy tính TOP500 nếu tính theo tiêu chuẩn năm 2009
Để vào được TOP500 năm 2009 thì cần hiệu năng trên 75 TFlop/s
M4 Max đạt 18.4 TFlop/s ở FP32, nhưng TOP500 dùng FP64 (LINPACK)
Dựa trên benchmark của M2, FP64 chỉ bằng khoảng 1/4 FP32, nên ước tính vào khoảng 9 TFlop/s
Mức đó không đủ để lọt vào TOP500 năm 2009
Tham khảo danh sách TOP500 năm 2009
Nếu mỗi kết nối thực hiện nhiều tác vụ I/O đồng thời thì phải nhân lên với hàng nghìn kết nối
Tôi từng nghe rằng máy chủ dành khoảng 95% tổng thời gian để chờ I/O, nhưng trên thực tế điều đó áp dụng cho từng thread chứ không phải toàn bộ máy chủ
Máy chủ thực tế thường có mức sử dụng CPU lên tới 70~80% (cao hơn nữa thì tail latency sẽ xấu đi rất nhanh)
Nếu ở full load mà CPU chỉ dùng 5% thì đó là dấu hiệu thiếu tiến trình song song hoặc thiếu bộ nhớ
Đây là chi tiết kỹ thuật nhỏ thôi, nhưng những sai sót như vậy có thể làm giảm độ tin cậy của bài viết này (nói với tư cách một fan của Bun)
Tôi có cảm giác kết luận đó giống như một kiểu hallucination do LLM tạo ra
Đặc biệt phần kết luận rất giống văn phong lấy ra từ LLM
"Điều khiến tôi hiểu ra là không phải các package manager được benchmark là sai, mà chúng là lời giải phù hợp với thời đại của chúng"
"Điểm cần nhấn mạnh là cách tiếp cận của Bun không hẳn mang tính cách mạng, mà là kết quả của việc nhìn thẳng, tỉnh táo vào nguyên nhân khiến mọi thứ chậm đi ngày nay"
"Việc cài package nhanh hơn 25 lần không phải phép màu, mà là hệ quả tự nhiên của việc xây dựng công cụ phù hợp với phần cứng hiện đại"
Dù chủ đề phức tạp, bài viết giải thích rất dễ hiểu và đơn giản nên tôi cực kỳ thích
Thật đáng khâm phục khi vẫn còn những người đầy nhiệt huyết phá vỡ hiện trạng và dám nhận các bài toán khó
Mỗi tháng phần cứng máy tính lại tiến bộ, trong khi phần mềm thì ngày càng chậm đi, điều đó thật sự có cảm giác bất thường
Tôi mong ai cũng viết code hiệu quả hơn
Zig là một ngôn ngữ rất mới, nên thật thú vị khi thấy nó được dùng nghiêm túc trong thực tế
Tôi mới thử bun lần đầu và thấy rất ấn tượng
Nhờ có server tích hợp sẵn và SQLite, chỉ cần cài mỗi bun là đủ nên việc phát triển thuận tiện hơn rất nhiều
Bình thường tôi chỉ dùng vanilla js và vốn không thích hệ sinh thái node cho lắm, nên thấy mình lẽ ra nên dùng bun sớm hơn
Tôi đã thử Bun nhiều lần và trải nghiệm sử dụng rất hài lòng
Nó cho cảm giác tốt hơn Node
Nhưng lần nào rồi tôi cũng đụng phải một vấn đề chí mạng nên cuối cùng lại quay về Node
Ban đầu là module crypto không tương thích với Nodejs (giờ đã được sửa), sau đó là Playwright không chạy trên Bun
Dạo này Node cũng hỗ trợ server tích hợp sẵn và SQLite
Nếu cần nhiều tính năng hơn thì Hono cũng là một lựa chọn thay thế tốt
Tôi không thật sự hiểu đoạn bài viết giải thích rằng hardlink trên Linux và clonefile trên MacOS là tương đương nhau
Với hardlink thì nếu sửa một bản sao, chẳng phải file trong mọi project đều sẽ bị thay đổi ngoài ý muốn sao?
Dù phần giải thích về mặt kỹ thuật khá phức tạp, bài viết vẫn được viết cực kỳ dễ đọc và vui nhộn, thật đáng nể
Tôi đã xem hầu hết các sản phẩm và video của cô ấy, và có thể cảm nhận được sự chuẩn bị rất kỹ lưỡng
Nếu có thời gian, tôi rất khuyến khích mọi người xem các bài viết và nội dung YouTube của cô ấy
Gần đây có lẽ cô ấy hoạt động ít hơn vì công việc hiện tại
Ở mục Binary Manifest Caching, có vẻ như thời gian benchmark của "npm (cached)" đã bị thiếu
Chỉ có bun, bun (cached) và npm, và phần thống kê tóm tắt dường như cũng không khớp hẳn
Tôi rất thích văn phong của bài đăng này
Có vẻ đây sẽ là một ví dụ rất tốt để tái sử dụng khi giải thích tầm quan trọng của io_uring
Tôi cũng tò mò liệu bản cập nhật io gần đây trong Zig v0.15 có thể mang lại thêm lợi ích hiệu năng cho Bun hay không
Tôi đã chờ đợi bun hơn một năm rồi
Tôi từng nghĩ 2025 sẽ là năm bun thật sự phổ biến, nhưng bất ngờ là đến giờ nó vẫn chưa nổi đến vậy
Trong top 100 nghìn repo trên GitHub, tính đến năm 2025 thì ở các repo mới, npm được dùng nhiều hơn 35 lần và pnpm nhiều hơn 11 lần
Deno cũng không nổi tiếng như tôi tưởng
Tôi tò mò không biết lý do là gì
Có phải vì runtime khó đạt tương thích hơn package manager?
Tôi muốn nghe ý kiến từ những người đã thử bun nhưng không chọn dùng nó
Thống kê tham khảo liên quan
Bình luận HN liên quan
Tôi đã nhiều lần cố thích cả Bun lẫn Deno và cũng đã thử chúng vài lần, nhưng cuối cùng đều gặp lỗi chí mạng nên không thể tiếp tục dùng
Vấn đề lớn nhất gần đây tôi gặp với Bun là stream bị đóng quá sớm
Link issue liên quan
Với Deno thì tôi gặp vấn đề rò rỉ bộ nhớ
Link issue liên quan
Cuối cùng tôi nghĩ hệ sinh thái Node sẽ tiếp nhận các ưu điểm của Bun/Deno trước
Bun là một tay chơi mới, được rót vốn venture capital, đang cạnh tranh với sản phẩm mã nguồn mở đã được kiểm chứng và đang thống trị là Node
Có động cơ lock-in, và rốt cuộc thì nó cũng không khác Node về mặt nền tảng đến vậy
Nó không có lợi thế chiến lược nào thật sự rõ ràng, cũng không mang lại điều gì mới mà Node không làm được
Thực tế tôi chưa thấy trường hợp nào dùng nghiêm túc, chỉ thấy các trường hợp dùng nhẹ nhàng
Nhìn vào issue tracker thì có vẻ ngôn ngữ Zig không an toàn lắm nên crash xảy ra khá thường xuyên
Tôi sẽ tiếp tục ở lại với Node
Tôi cũng tò mò ý kiến của người khác
Theo tôi, Node là một dự án đã trưởng thành, mang tính dân chủ và đậm chất cộng đồng dẫn dắt
Một phần cũng vì nó đã vượt qua rất tốt sự cố fork io.js
Trong khi đó bun hay deno thì đều là các dự án được VC chống lưng, nên tôi không có cảm giác đó là cộng đồng dân chủ dẫn dắt
Tôi là fan cuồng của Bun
Tôi dùng Bun cho mọi project có thể, và cả các script one-off cũng viết bằng Bun/TS
Dù vậy, vẫn có một số issue tuy ít nhưng đáng lo nên tôi còn do dự chưa dám deploy production
Ví dụ, khi chạy một webserver Express đơn giản trong Docker bằng bun thì nó từng bị treo
Chỉ cần đổi sang node là chạy bình thường
Một năm trước, tổ hợp Bun + Prisma cũng từng làm server chết vì rò rỉ bộ nhớ (chắc giờ đã được sửa rồi)
Dù vậy tôi vẫn rất thích Bun, đến mức chấp nhận những nhược điểm đó vì nhìn chung nó giúp tiết kiệm thời gian phát triển
Sự tiện lợi từ transpile, module, workspace v.v. là cực lớn
Tôi hoàn toàn hiểu vì sao nó vẫn chưa phổ biến như npm
Đọc bài này thật sự rất thích thú
Đây là một ví dụ cho thấy các nguyên lý khoa học máy tính quan trọng thế nào trong phát triển phần mềm ngoài đời thực
Big O, tính cục bộ theo thời gian/không gian, độ phức tạp thuật toán, user/kernel space, file system, copy-on-write v.v.
Trong kiểu phát triển package ở tầng thấp như thế này, gần như mọi khái niệm học trong chương trình CS đều được áp dụng vào thực tế
CS nghiên cứu tính toán và lý thuyết (ngôn ngữ lập trình, thuật toán, mật mã học, machine learning v.v.)
Còn SE là việc áp dụng các nguyên tắc kỹ thuật để xây dựng phần mềm có khả năng mở rộng và độ tin cậy cao
Tôi không hiểu rõ vì sao việc đọc xong toàn bộ file nén rồi mới giải nén lại có lợi
Tôi đoán rằng bắt đầu giải nén trước cả khi tải xong sẽ có lợi hơn, hơn cả bất lợi do số lần sao chép lại bộ nhớ tăng lên