VIẾT CODE RUBY HIỆU QUẢ
1. Benchmark Để đánh giá được hiểu quả của chương trình thì điều đầu tiên là chúng ta phải tính được chi phí của nó, trong các thư viện dành cho Ruby có khá nhiều các thư viện cung cấp khả năng này và Benchmark là một thư viện như thế. Module Benchmark cung cấp các phương thức để đo thời gian và ...
1. Benchmark
Để đánh giá được hiểu quả của chương trình thì điều đầu tiên là chúng ta phải tính được chi phí của nó, trong các thư viện dành cho Ruby có khá nhiều các thư viện cung cấp khả năng này và Benchmark là một thư viện như thế. Module Benchmark cung cấp các phương thức để đo thời gian và report thời gian chạy của các đoạn mã Ruby. Để sử dụng module này chúng ta chỉ cần khai báo như sau:
Ở hình vẽ trên chúng ta có thể nhận thấy x.report được dùng cả ở cả ba vòng lặp for, times, upto, nó được dùng để đo và hiển thị thời gian chạy của cả 3 vòng lặp. Sau đây là kết quả khi đoạn mã trên được thực thi
Kết quả hiển thị gồm có 4 cột đo thời gian (milliseconds) là user, system, total, real. Trong đó:
-
user: đo thời gian chạy của chính đoạn mã trên
-
system: thời gian mà hiệu điều hành phải dùng để quản lý chương trình trên
-
total: = user + system
-
real: tổng chi phí thời gian thực sự dành cho đoạn mã trên chạy, trong đó đã loại bỏ đi thời gian ngắt, chờ đợi. Do vậy thời gian này luôn nhỏ hơn total vì thông thường khi chạy một ứng dụng, thì ứng dụng thường phải chờ đợi một khoảng thời gian nào đó cho các ứng dụng khác chạy, hay cho các xử lý của hệ điều hành do đó total bao gồm cả các khoảng thời gian chờ đợi này.
2. Một số mẹo nhỏ để viết code ruby hiệu quả
- Instance variables và Accessors
Truy cập đến các thuộc tính class, module được khai báo bởi attr_accessor, attr_reader or attr_writer đòi hỏi chi phí cao hơn là sử dụng trực tiếp các instance variables, bởi trong các quá trình thực thi, để truy nhập được các biến này thì phải thông qua các method trên. Do đó nên sử dụng các setter, getter hợp lý, đúng lúc.
Thay vì :
self.attibute = expr
Chúng ta có thể viết:
@attribute = expr
- Sử dụng biến cục bộ nếu có thể được
Truy cập đến các biến cục bộ là nhanh hơn nhiều so với truy cập đến các biến toàn cục, các class, module, method khác trong Ruby, bởi trong Ruby chỉ có biến cục bộ là được xác định ở thời gian phân tích cú pháp, trong khi đó class, module, method, constants, instance variable được tạo, thay đổi, phá hủy ở thời gian chạy (runtime)
- Cache dữ liệu bằng biến
Xem xét sự khác biệt giữa 2 function sau:
def capital_letters ("A".."Z").to_a end
Và
def capital_letters @capital_letters ||= (“A”..”Z”).to_a end
Ở function thứ nhất, mỗi khi gọi đến hàm capital_letters, thì to_a() sẽ phải chạy lại, trong khi đó ở function 2 chúng ta đã cache sẵn kết quả vào biến @capital_letters, chỉ khi trường hợp @capital_letter bằng nil thì to_a() mới chạy, còn nếu nó đã có giá trị rồi thì hàm to_a() sẽ không phải thực thi lại nữa mà nó trả ra luôn kết quả đã được cache.
- Sử dụng nil?
x = nil if x.nil? puts “x is nil” else puts “x is not nil” end
Better:
unless x puts “x is nil” else puts “x is not nil” end
Hai đoạn mã trên về logic là tương đương, về cách viết thì cách 1 trong sáng dễ hiểu hơn, tuy nhiên ở cách 2 chúng ta không phải gọi lại nil một lần nữa
- Sử dụng return
Trình biên dịch của Ruby tự động lưu giá trị được tính ở cuối cùng method hay một block và mặc định trả ra giá trị này mà không cần dùn câu lệnh return. Ví dụ
def test a = 2 b = 4 c = 5 end
Tương đương :
def test a= 2 b = 4 c = 5 return c end
- Gán song song nhiêu giá trị
a, b = 1,2 Swap: a, b = b, a arr = [1, 2] x, y = arr => x = 1, y = 2
- Định nghĩa sẵn constants vs inlining
Điều này sẽ giúp code của bạn dễ đọc và sẽ không phải khai báo đi khai báo lại các biến có cùng giá trị.
Tốt hơn:
Kết luận
” Premature optimization is the root of all evil “ Donald Knuth
Vì vậy hãy tối ưu code khi bạn thực sự cần, còn không hãy để nó trong sáng dễ đọc, dễ hiểu điều này làm cho chương trình của bạn dễ bảo trì và phát triển hơn