0

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à 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:

bench1.png

Ở 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

bench2.png

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

    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 nilelse
      puts “x is not nilend

Better:

    unless x
      puts “x is nilelse
      puts “x is not nilend

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ị.

validate.png

Tốt hơn:

validate2.png

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


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí