- So sánh hiệu năng giữa Rust và C là một vấn đề phức tạp, phụ thuộc vào cách định nghĩa giả định “mọi điều kiện đều như nhau”
- Với assembly nội tuyến, cả hai ngôn ngữ đều có thể tạo ra cùng một mã assembly, nên không có khác biệt về tốc độ do bản thân ngôn ngữ
- Ở bố cục bộ nhớ của struct, Rust có thể đạt kích thước nhỏ hơn nhờ sắp xếp lại các field, nhưng cũng có thể dùng thuộc tính
#[repr(C)] để có bố cục giống hệt C
- Do khác biệt về kiểm tra lúc chạy và cách lập trình viên triển khai mã, cấu trúc mã và hiệu năng trong dự án thực tế có thể thay đổi
- Kết luận là không có khác biệt hiệu năng do giới hạn nội tại của ngôn ngữ, mà kết quả phụ thuộc vào dự án và yếu tố con người
Nêu vấn đề và sự mơ hồ của giả định
- Câu hỏi “nếu cùng điều kiện thì Rust có thể nhanh hơn C không” được nêu ra trên Reddit là điểm khởi đầu
- Cách diễn đạt “mọi điều kiện đều như nhau” bản thân nó là một khái niệm rất khó xác định khi so sánh ngôn ngữ
- So sánh hiệu năng không chỉ thay đổi theo khác biệt ngôn ngữ mà còn theo dạng mã, quyết định của lập trình viên và tối ưu hóa của trình biên dịch
So sánh assembly nội tuyến
- Rust hỗ trợ assembly nội tuyến ở cấp độ ngôn ngữ, còn C triển khai điều này qua phần mở rộng của trình biên dịch
- Cả hai ngôn ngữ đều có thể viết cùng một ví dụ dùng lệnh
rdtsc
- Assembly được tạo ra từ
rustc 1.87.0 và clang 20.1.0 có hình thức hoàn toàn giống nhau
- Trường hợp này không chứng minh chênh lệch hiệu năng giữa các ngôn ngữ, nhưng cho thấy Rust có thể thực hiện mức kiểm soát cấp thấp tương đương C
Khác biệt về bố cục struct
- Struct trong Rust có thể tối ưu việc sử dụng bộ nhớ bằng cách sắp xếp lại các field
- Trong ví dụ, struct của Rust có kích thước 16 byte, còn struct tương ứng trong C là 24 byte
- Trong C, cần tự thay đổi thứ tự field thì mới đạt được cùng kích thước
- Nếu dùng thuộc tính
#[repr(C)] trong Rust, có thể ép bố cục bộ nhớ giống hệt C
Yếu tố xã hội và con người
- Nhờ các kiểm tra an toàn của Rust, có những trường hợp lập trình viên dám thử các tối ưu hóa mạnh tay hơn
- Trong dự án Stylo của Mozilla, hai lần thử song song hóa bằng C++ đều thất bại, nhưng với Rust thì đã triển khai thành công
- Ngay cả trong cùng một dự án, hiệu năng và độ ổn định của mã kết quả cũng có thể khác nhau tùy ngôn ngữ và trình độ của lập trình viên
- Vì kết quả của “cùng một công việc” khác nhau giữa người mới và chuyên gia, cũng như theo mức độ thành thạo ngôn ngữ, nên rất khó so sánh đơn giản
Kiểm tra thời gian biên dịch và thời gian chạy
- Nhiều kiểm tra an toàn của Rust được thực hiện khi biên dịch, nhưng một phần vẫn còn là kiểm tra lúc chạy
- Ví dụ, khi truy cập
array[0], Rust thực hiện kiểm tra biên, còn C thì không
- Nếu dùng
get_unchecked() trong Rust thì có thể đạt hành vi giống C
- Khi trình biên dịch có thể chứng minh được tính an toàn, cả hai ngôn ngữ đều có thể loại bỏ các kiểm tra nhờ tối ưu hóa
- Những khác biệt này ảnh hưởng đến cách viết mã và cuối cùng có thể dẫn tới khác biệt hiệu năng
Kết luận
- Ngay cả khi giả định C là “ngôn ngữ nhanh nhất”, không có lý do gì Rust lại không thể đạt mức hiệu năng tương đương
- Thứ quyết định khác biệt hiệu năng không phải giới hạn nội tại của ngôn ngữ mà là đặc thù dự án, năng lực lập trình viên, ràng buộc thời gian và các biến số bên ngoài khác
- Vì vậy, câu hỏi “Rust có nhanh hơn C không?” nên được hiểu là vấn đề của bối cảnh kỹ thuật phần mềm hơn là so sánh ngôn ngữ
6 bình luận
Ý kiến trên Hacker News
Tóm lại, tốc độ tối đa gần như ngang nhau, nhưng trong code thực tế thì khác biệt khá lớn
Đặc biệt đa luồng là biến số rất lớn. Rust yêu cầu mọi biến toàn cục phải an toàn luồng dù có dùng luồng hay không, và borrow checker giới hạn việc truy cập bộ nhớ vào một trong hai kiểu: chia sẻ hoặc thay đổi
Vì vậy trong Rust, việc viết code đa luồng gần như là mặc định. Trong khi đó ở C, ngay cả việc tạo luồng cũng đã là gánh nặng do vấn đề tương thích nền tảng và rủi ro khi debug
Việc dựng luồng trong C không hẳn khó, nhưng vẫn rườm rà hơn Rust với
std::thread::spawn(move || { ... });Tác động lớn hơn nằm ở mô hình đồng thời của ngôn ngữ hơn là độ an toàn bộ nhớ. Go dù không an toàn bộ nhớ vẫn có thể song song hóa dễ dàng bằng
go f()Cá nhân tôi lại thấy heisenbug trong Go thường xuyên hơn
#pragma omp forlà C cũng có thể song song hóa đơn giảnNhờ traits, Rust có thể tạo ra các lớp trừu tượng vừa nhanh vừa linh hoạt hơn
Ở C có thể bắt chước bằng macro hoặc function pointer, nhưng trong Rust thì phía gọi có thể chọn dynamic dispatch hoặc static dispatch
Trong môi trường nhúng, function pointer làm hỏng cache và giảm hiệu năng, còn Rust traits cho phép tối ưu inline nên hiệu quả hơn nhiều
Dù là Rust hay C thì cuối cùng vẫn phải xử lý ở cấp byte, và dạo này các công cụ vá nhị phân cũng đã tốt hơn nhiều nên dễ tận dụng hơn
Box<dyn Trait>trong chữ ký hàm thì phía gọi sẽ bị ép dùng dynamic dispatchNếu dùng
impl Traitthì phía gọi vẫn còn quyền lựa chọnCá nhân tôi thấy Rust, C và C++ gần như cùng thuộc một họ ngôn ngữ cấp thấp, nên chênh lệch hiệu năng là rất nhỏ
Quy tắc aliasing nghiêm ngặt của Rust có lợi cho tối ưu hóa, còn UB (hành vi không xác định) trong C/C++ tồn tại để phục vụ hiệu năng
Ngoài ra generic của Rust và C++ cũng mạnh hơn C rất nhiều, nên ví dụ sắp xếp dựa trên template sẽ dễ tối ưu inline hơn
qsort()Tôi nghĩ những cuộc tranh cãi tốc độ giữa các ngôn ngữ phần lớn là vô nghĩa
Thứ quyết định hiệu năng không phải bản thân ngôn ngữ mà là cách hiện thực compiler
Rust, C và C++ đều là ngôn ngữ cấp thấp, nhưng định nghĩa của “nhanh” mới là điều quan trọng
Là đang nói đến tốc độ tối đa của code do chuyên gia tối ưu, hay là xác suất để một lập trình viên bình thường viết được code nhanh trong phạm vi ngân sách
Nhưng nếu tối ưu thủ công thì khác biệt giữa các ngôn ngữ gần như biến mất
Dù vậy, Rust vẫn nhỉnh hơn một chút ở chỗ đây là ngôn ngữ giúp viết code nhanh hiệu quả dễ hơn
Tôi từng nghĩ ưu điểm của Rust là đa luồng và cấp phát trên stack
Nhờ mô hình ownership, có thể đưa nhiều thứ lên stack hơn so với C/C++, từ đó giảm overhead của malloc/free
Chủ đề kiểu này dễ gây tranh cãi cảm tính, nên tôi muốn bàn về sự khác biệt trong cách tư duy hơn là các con số cụ thể
Khi bàn về “tốc độ” của ngôn ngữ thì cần nhìn vào hai điểm
Rust và C hầu như không có runtime check nên nhanh hơn Python hay JS
Tuy nhiên Rust truyền đạt thông tin aliasing tốt hơn nên có thêm dư địa tối ưu hóa
Ở chế độ debug thì nó chậm ngang Ruby, nhưng ở chế độ release thì đạt tốc độ mức C
So với C, C++ hay Rust có nhiều tính năng thời điểm biên dịch hơn nên dễ viết code nhanh hơn
Ví dụ đoạn code này gần như không thể làm được trong C
Trong C thì cần đến công cụ bên ngoài như re2c
Assembly không phải là một phần của tiêu chuẩn C nên khó so sánh trực tiếp với Rust, và Rust thực ra có tính chất gần với dự án GCC hơn
Việc một ngôn ngữ có “nhanh” hay không cuối cùng vẫn phụ thuộc vào cách triển khai và bối cảnh
So với tốc độ của bản thân ngôn ngữ, sự kết hợp giữa compiler và phần cứng mới ảnh hưởng lớn hơn nhiều
Trung bình thì tôi không rõ ngôn ngữ nào là nhanh nhất, nhưng có vẻ độ phân tán của C++ sẽ là lớn nhất.
Trong lĩnh vực hệ thống nhúng, người ta còn lập trình với cả việc tính đến kích thước cache line của phần cứng. Vấn đề có lẽ nằm ở chỗ lập trình viên có thể tối ưu hóa đến cực hạn trên ngôn ngữ được bao xa, cùng với hiệu năng của thư viện chuẩn và trình biên dịch. Dù sao thì cả hai đều hỗ trợ mức thấp, nên khác biệt về một chút overhead có lẽ cũng chỉ ở mức không đáng kể. Vì vậy đây có vẻ không phải là một cuộc tranh luận quá có ý nghĩa.. Nếu cần tối ưu hóa đến mức cực hạn thì cuối cùng vẫn phải có sự can thiệp của con người. Vì trình biên dịch không hoàn hảo như ta nghĩ.
Tôi nghĩ Rust sẽ trở thành lựa chọn thay thế cho C++ hơn là cho C. C gần như là ngôn ngữ duy nhất (có lẽ cũng là cuối cùng) mà người ta có thể đoán được trình biên dịch sẽ tạo ra mã như thế nào…
Có phải bạn cố ý làm vậy đúng không, hừ hừ.
Điều đó phụ thuộc vào năng lực của trình biên dịch.
Nếu lắp ráp cùng một đoạn mã thì sẽ ra ngay thôi.