Cách ghi nhớ trong Ruby để giảm khối lượng tính toán bằng Singleton
Bài đăng này đã không được cập nhật trong 8 năm
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
All rights reserved