Cải thiện hiệu năng của Ruby: viết lại C bằng Ruby
So sánh hiệu năng của Ruby
- Trong một kho lưu trữ so sánh ngôn ngữ gần đây, Ruby được đánh giá nhanh hơn R và Python, nhưng vẫn là ngôn ngữ chậm thứ ba.
- Benchmark gồm hai bài thử là "Loops" và "Fibonacci", lần lượt nhấn mạnh vòng lặp và câu lệnh điều kiện, chi phí gọi hàm và hiệu năng đệ quy.
So sánh hiệu năng giữa Ruby và Node.js
- Trên M3 MacBook Pro, Ruby 3.3.6 mất 28 giây cho ví dụ vòng lặp và 12 giây cho ví dụ Fibonacci.
- Node.js mất khoảng 1 giây cho cả hai ví dụ.
- Trên M2 MacBook Air, hiệu năng của Ruby còn kém hơn.
Ý nghĩa của benchmark
- Những benchmark như vậy có thể không thực sự mang nhiều ý nghĩa.
- Python được đánh giá là ngôn ngữ chậm nhất, nhưng lại là ngôn ngữ được dùng nhiều nhất trên GitHub.
- Ngôn ngữ lập trình cần hiệu quả, nhưng tính hữu dụng và năng suất của ngôn ngữ quan trọng hơn hiệu năng.
Áp dụng YJIT
- Khi áp dụng YJIT, hiệu năng Fibonacci được cải thiện đáng kể.
- Với ví dụ vòng lặp, mức cải thiện hiệu năng là không đáng kể.
Tối ưu hóa mã Ruby
Range#each được viết bằng C nên không thể được YJIT tối ưu hóa.
Integer#times đã được chuyển từ C sang Ruby trong Ruby 3.3, nhờ đó có thể được YJIT tối ưu hóa.
Array#each được chuyển từ C sang Ruby trong Ruby 3.4.
Tối ưu hóa Integer#times
Integer#succ chạy nhanh hơn i += 1.
- YJIT tối ưu hóa
Integer#times để cải thiện hiệu năng đáng kể.
Tối ưu hóa Array#each
Array#each được chuyển từ C sang Ruby trong Ruby 3.4 nên có thể được YJIT tối ưu hóa.
- Mô-đun
Primitive được dùng để đánh giá mã C trong Ruby.
Kho lưu trữ Ruby Microbench
- Benchmark được chạy với nhiều phiên bản Ruby khác nhau và có sử dụng YJIT.
- Ruby 3.4 YJIT cho thấy hiệu năng được cải thiện mạnh.
Tối ưu hóa range#each
- Có thể cải thiện hiệu năng bằng cách triển khai lớp
Range bằng Ruby thuần.
Thư viện chuẩn YJIT
- Nhóm YJIT đang cải thiện hiệu năng bằng cách thay thế mã C bằng Ruby.
- Khối
with_yjit được dùng để sử dụng triển khai Ruby khi YJIT được kích hoạt.
Khảo sát tối ưu hóa của YJIT
- YJIT tối ưu hóa hiệu năng bằng cách chuyển bytecode của Ruby VM thành mã máy.
- Phân tích mã máy của
Integer#succ giúp hiểu quá trình tối ưu hóa của YJIT.
1 bình luận
Ý kiến Hacker News
Ví dụ về vòng lặp lặp lại 1 tỷ lần và dùng vòng lặp lồng nhau. Có thể đoán benchmark này sẽ tiêu tốn hơn 99% thời gian ở hai dòng đầu tiên
ukhông được biết tại thời điểm biên dịch, vòng lặp trong vẫn có thể được thay thế bằng vài lệnhCó nhắc đến các phiên bản Ruby trong tương lai: Ruby 3.4.0 dự kiến ra mắt vào dịp Giáng sinh năm nay, còn Ruby 3.5.0 vào dịp Giáng sinh năm sau
Vẫn còn rất nhiều tình cảm dành cho Ruby. Cảm ơn Matz
Đã có một PR cải thiện hiệu năng của
Integer#succvào đầu năm 2024, và nhờ đó mới hiểu vì sao lại dùngInteger#succInteger#succđược dùng khi viết lại các phương thức vòng lặp; trong trình thông dịch,opt_succ (i = i.succ)được xử lý nhanh hơnputobject 1; opt_plus (i += 1)#succvì tính dễ đọc, và dùng nó hai lần trong phương thức#bytescủa thư viện UUID để giữ được “chế độ cắt bit” khi đọc mãChia sẻ trải nghiệm liên quan đến TruffleRuby, cho biết TruffleRuby nhanh hơn Node.js và tiệm cận Bun hoặc Golang
Ruby đã trở nên rất nhanh, còn TruffleRuby thì thậm chí còn ấn tượng hơn
Trước đây không biết YJIT được viết bằng Rust
Python là ngôn ngữ chậm nhất trong benchmark, nhưng tính đến tháng 10/2024 lại là ngôn ngữ được dùng nhiều nhất trên Github
Có một kho so sánh ngôn ngữ cũ hơn, bao gồm nhiều ngôn ngữ hơn
Nó đã mang lại thay đổi lớn cho các lời giải Advent of Code, và trông giống nhau đến ngạc nhiên