Cách ghi nhớ trong Ruby để giảm khối lượng tính toán bằng Singleton
Khi bắt đầu lập trình ruby on rails, có rất nhiều người không để ý đến các khái niệm cơ bản trong ruby mà bắt đầu làm việc luôn với rails. Tuy nhiên "một người làm rails tốt thì chưa chắc đã làm ruby tốt, nhưng một người làm ruby tốt thì luôn luôn làm rails tốt". Trong quá trình tìm hiểu ruby, ...
Khi bắt đầu lập trình ruby on rails, có rất nhiều người không để ý đến các khái niệm cơ bản trong ruby mà bắt đầu làm việc luôn với rails. Tuy nhiên "một người làm rails tốt thì chưa chắc đã làm ruby tốt, nhưng một người làm ruby tốt thì luôn luôn làm rails tốt". Trong quá trình tìm hiểu ruby, chúng ta sẽ có thể học được một số khái niệm ứng dụng mạnh mẽ trong các rails hay các dự án ruby khác. Bạn có thể thường nhìn thấy những mẫu code như dưới đây trong các project ruby on rails:
class User def format_name do_something_with name do_another_thing_with name end def name @name ||= begin User.first.name end end end
Trong class trên, method name được gọi lại nhiều lần, đó là lý do chúng ta sử dụng cách ghi nhớ trong ruby.
Sau đây là một cách khác để ghi nhờ mà sử dụng những khái niệm cơ bản trong ruby:
class User def format_name do_something_with name do_another_thing_with name end def name def self.name @name end @name = User.first.name end end
Sau đây là các khái niệm ruby được sử dụng trong đoạn code trên:
- Bất cứ khi nào bạn gọi method "name" lần đầu tiên, nó sẽ tạo một Singleton method cho đối tượng được khai báo.
- Bất cứ khi nào bạn sẽ gọi phương thức "name" sau lần đầu tiên, nó sẽ thực hiện Singleton method "name" thay vì method "name" chính bởi vì Singleton method có độ ưu tiên cao hơn method chính.
- Singleton method có quyền truy cập vào tất cả các biến thể hiện của một đối tượng.
Phương pháp trên có thể được sử dụng khi mã bên trong bắt begin block phức tạp hoặc lớn hoặc có thể là trong các tình huống khác nhau để code đơn giản hơn.
Bằng phương pháp này, cũng có thể nhớ các giá trị "false" hoặc "nil". Ví dụ:
class Foo < Object def thing1 do_something with: expensive_computation1 do_something_else with: expensive_computation1 end def thing2 do_something with: expensive_computation2 do_something_else with: expensive_computation2 end private def expensive_computation1 @expensive_computation1||= Model.where(id: 1742).first end def expensive_computation2 def self.expensive_computation2 @expensive_computation2 end @expensive_computation2= Model.where(id: 4217).first end end
thing1 và thing2 đều được nhớ, nhưng thing2 có thể nhớ trong trường hợp ko tìm thấy active_record, thing1 thì sẽ query lại
So sánh benchmark của các method ghi nhớ khác nhau:
require "benchmark" class A def name @name ||= begin rand end end end class B def name return(@name) if defined?(@name) @name = rand end end class C def name def self.name @name end @name = rand end end class D def name class << self def name @name end end @name = rand end end n = 20_000 n1 = 2_000 Benchmark.bm(2) do |x| x.report("A:") { n.times { k = A.new; n1.times { k.name } } } x.report("B:") { n.times { k = B.new; n1.times { k.name } } } x.report("C:") { n.times { k = C.new; n1.times { k.name } } } x.report("D:") { n.times { k = D.new; n1.times { k.name } } } end
Output:
user system total real A: 3.810000 0.000000 3.810000 ( 3.817210) B: 4.000000 0.010000 4.010000 ( 4.007852) C: 2.850000 0.010000 2.860000 ( 2.848843) D: 2.850000 0.000000 2.850000 ( 2.854403)
Nhận xét:
- Mặc dù ko có nhiều sự khác biệt nhưng về tổng thể thì C là ổn nhất
- Mặc dù Singleton method khởi động chậm hơn khởi động Boolean nhưng cách gọi Singleton method nhanh hơn nhiều và điều đó làm cho nó có thể sử dụng.
Kết luận:
Hi vọng bài viết sẽ giúp bạn có thể ứng dụng Singleton method trong ruby. Thanks