So sánh thời gian biên dịch giữa Rust và C++
(quick-lint-js.com)Tôi đọc được một bài viết thú vị ở nơi khác nên muốn giới thiệu.
Rust và C++ thường được đem ra so sánh ở nhiều khía cạnh. Tuy nhiên, để so sánh trực tiếp thời gian biên dịch thì khá khó, vì hiếm khi có cùng một dự án được viết bằng cả hai ngôn ngữ. quick-lint-js cho biết họ đã so sánh thời gian biên dịch bằng cách viết lại một phần của dự án vốn được viết bằng C++ sang Rust. Tuy vậy, môi trường biên dịch không bao gồm Windows.
Tiêu chí khi port
- Loại trừ thư viện bên thứ ba
- Thực hiện trên Linux và macOS
- Bộ kiểm thử rất toàn diện
- FFI, con trỏ, container tiêu chuẩn và tự viết, lớp và hàm tiện ích, I/O, đồng thời, generic, macro, SIMD, kế thừa, ...
Kết luận
- Thời gian biên dịch của Rust tương đương hoặc chậm hơn C++ (ít nhất là trong dự án này)
- Rust cần phải viết nhiều mã hơn C++
- Ở bản dựng toàn phần (Full), C++ tương đương hoặc nhanh hơn
- Ở bản dựng gia tăng (Incremental), có lúc ngắn hơn và có lúc dài hơn (thậm chí có trường hợp rất dài)
- Quyết định không port phần còn lại của quick-lint-js sang Rust (nếu có cải thiện về thời gian build thì có thể sẽ làm?)
4 bình luận
Ở Hàn Quốc cũng có khá nhiều người dùng Rust nhỉ. Tôi tò mò không biết mọi người đang làm việc trong những lĩnh vực nào.
Sau lần biên dịch đầu tiên, nếu chỉnh sửa các file
cppđơn lẻ thay vì các filehcó nhiều dependency trong C++, thì thời gian biên dịch sẽ rất nhanh; nên tôi cũng tò mò không biết Rust thế nào.Tôi thấy phần câu hỏi thường gặp trong tài liệu Rust có giải thích khá chi tiết về vấn đề này nên chia sẻ lại.
====================================
Có vẻ việc biên dịch Rust khá chậm. Tại sao vậy?
Vì nó phải dịch mã sang mã máy và thực hiện tối ưu hóa. Rust cung cấp các mức trừu tượng bậc cao nhưng vẫn được biên dịch thành mã máy hiệu quả, và quá trình chuyển đổi này đương nhiên sẽ tốn thời gian, đặc biệt là khi có tối ưu hóa.
Tuy nhiên, thời gian biên dịch của Rust không tệ như nhiều người nghĩ, và có cơ sở để tin rằng trong tương lai nó sẽ còn được cải thiện. Nếu so sánh các dự án có quy mô tương tự viết bằng C++ và Rust, thì thời gian biên dịch toàn bộ dự án nhìn chung là gần tương đương. Lý do chính khiến người ta cảm thấy Rust biên dịch chậm là vì C++ và Rust có mô hình biên dịch khác nhau: đơn vị biên dịch của C++ là từng tệp, còn Rust là một crate gồm nhiều tệp. Vì vậy, trong quá trình phát triển, nếu chỉ sửa một tệp C++, thời gian biên dịch có thể ngắn hơn rất nhiều so với Rust. Hiện đang có một nỗ lực lớn nhằm tái cấu trúc trình biên dịch Rust để hỗ trợ biên dịch gia tăng, và khi hoàn thành, thời gian biên dịch của Rust cũng sẽ được cải thiện theo hướng giống mô hình của C++.
Ngoài mô hình biên dịch, còn có nhiều yếu tố trong thiết kế ngôn ngữ Rust ảnh hưởng đến thời gian biên dịch.
Thứ nhất, Rust có một hệ thống kiểu tương đối phức tạp, và cần dành một lượng thời gian biên dịch không nhỏ để áp đặt các ràng buộc giúp Rust an toàn trong lúc chạy.
Thứ hai, trình biên dịch Rust có khoản nợ kỹ thuật cũ, đặc biệt là chất lượng của LLVM IR được tạo ra chưa tốt, nên LLVM phải tốn thời gian để “sửa” lại. Trong tương lai, các bước tối ưu hóa và chuyển đổi dựa trên MIR có thể sẽ giúp giảm tải mà trình biên dịch Rust đặt lên LLVM.
Thứ ba, việc Rust dùng LLVM cho sinh mã là con dao hai lưỡi. Nhờ LLVM, Rust đạt hiệu năng runtime đẳng cấp thế giới, nhưng LLVM là một framework khổng lồ không được tối ưu với trọng tâm là thời gian biên dịch, và đặc biệt dễ bị ảnh hưởng bởi đầu vào chất lượng thấp.
Cuối cùng, chiến lược Rust dùng để đơn hình hóa (
monomorphise) các kiểu tổng quát (generic) tương tự C++ tạo ra mã chạy nhanh, nhưng có nhược điểm là phải sinh ra lượng mã lớn hơn đáng kể so với các chiến lược dịch khác. Sự phình to mã này có thể được đánh đổi bằng cách dùng trait object để lấy dynamic dispatch cùng những ưu và nhược điểm đi kèm.Điều chắc chắn là các dự án Rust ngốn dung lượng đĩa rất nhiều khi build. Tôi vẫn còn nhớ lần giật mình khi thấy một dự án dùng hơn cả trăm thư viện đã ngốn tới khoảng 2GB lúc nào không hay...