12/08/2018, 14:08

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:

  1. 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.
  2. 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.
  3. 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:

  1. Mặc dù ko có nhiều sự khác biệt nhưng về tổng thể thì C là ổn nhất
  2. 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

0