Computer >> Máy Tính >  >> Lập trình >> Ruby

Chi phí ẩn của việc lập trình siêu hình

Lập trình siêu âm nghe có vẻ là một từ rất lạ, nhưng liệu nó có tốt không?

Nó có thể hữu ích, nhưng nhiều người không nhận ra rằng việc sử dụng lập trình siêu hình có một số chi phí.

Chỉ để chúng ta ở trên cùng một trang…

siêu lập trình là gì chính xác?

Tôi định nghĩa lập trình siêu hình là sử dụng bất kỳ phương pháp nào:

  • Thay đổi cấu trúc mã của bạn (như define_method )
  • Chạy một chuỗi như thể nó là một phần của mã Ruby thực của bạn (như instance_eval )
  • Làm điều gì đó như một phản ứng đối với một số sự kiện (như method_missing )

Vì vậy, chi phí của lập trình siêu hình là gì? Tôi phân loại chúng thành 3 nhóm:

  • Tốc độ
  • Khả năng đọc
  • Khả năng tìm kiếm

Lưu ý :Bạn cũng có thể nói rằng có một nhóm thứ tư: Bảo mật . Lý do cho điều đó là eval các phương pháp này không thực hiện bất kỳ loại kiểm tra bảo mật nào đối với những gì đang được chuyển vào. Bạn phải tự mình thực hiện điều đó.

Hãy cùng khám phá chi tiết hơn từng điều đó!

Tốc độ

Cái giá phải trả đầu tiên là tốc độ vì hầu hết các phương pháp lập trình siêu hình đều chậm hơn các phương pháp thông thường.

Đây là một số mã đo điểm chuẩn:

require 'benchmark/ips'

class Thing
  def method_missing(name, *args)
  end

  def normal_method
  end

  define_method(:speak) {}
end

t = Thing.new

Benchmark.ips do |x|
  x.report("normal method")  { t.normal_method }
  x.report("missing method") { t.abc }
  x.report("defined method") { t.speak }

  x.compare!
end

Kết quả (Ruby 2.2.4) :

normal method:   7344529.4 i/s
defined method:  5766584.9 i/s - 1.34x  slower
missing method:  4777911.7 i/s - 1.54x  slower

Như bạn có thể thấy cả hai phương pháp lập trình siêu thị (define_method &method_missing ) chậm hơn một chút so với phương pháp bình thường.

Đây là điều thú vị mà tôi đã khám phá ra…

Kết quả trên là từ Ruby 2.2.4 , nhưng nếu bạn chạy các điểm chuẩn này trên Ruby 2.3 hoặc Ruby 2.4 có vẻ như các phương pháp này ngày càng chậm hơn!

Kết quả điểm chuẩn của Ruby 2.4 :

normal method:   8252851.6 i/s
defined method:  6153202.9 i/s - 1.39x  slower
missing method:  4557376.3 i/s - 1.87x  slower

Tôi đã chạy điểm chuẩn này nhiều lần để đảm bảo rằng nó không phải là một điểm chuẩn.

Nhưng nếu bạn chú ý và nhìn vào số lần lặp lại mỗi giây (i/s ) Có vẻ như các phương thức thông thường đã nhanh hơn kể từ Ruby 2.3. Đó là lý do method_missing trông chậm hơn rất nhiều 🙂

Khả năng đọc

Thông báo lỗi có thể ít hữu ích hơn khi sử dụng instance_eval / class_eval phương pháp.

Hãy xem đoạn mã sau:

class Thing
  class_eval("def self.foo; raise 'something went wrong'; end")
end

Thing.foo

Điều này sẽ dẫn đến lỗi sau:

(eval):1:in 'foo': 'something went wrong...' (RuntimeError)

Lưu ý rằng chúng tôi thiếu tên tệp (nó cho biết eval thay vào đó) &số dòng chính xác. Tin tốt là có một bản sửa lỗi cho điều này, eval này phương thức có thêm hai tham số:

  • tên tệp
  • một số dòng

Sử dụng các hằng số tích hợp sẵn __FILE__ &__LINE__ làm tham số cho class_eval bạn sẽ nhận được thông tin chính xác trong thông báo lỗi.

Ví dụ :

class Thing
  class_eval(
    "def foo; raise 'something went right'; end",
    __FILE__,
    __LINE__
  )
end

Tại sao đây không phải là mặc định?

Tôi không biết, nhưng điều cần lưu ý nếu bạn định sử dụng các phương pháp này 🙂

Khả năng tìm kiếm

Phương pháp siêu lập trình làm cho mã của bạn ít tìm kiếm hơn, ít truy cập hơn (thông qua tài liệu kém hơn) và khó gỡ lỗi hơn.

Nếu bạn đang tìm kiếm một định nghĩa phương pháp, bạn sẽ không thể thực hiện CTRL + F (hoặc bất kỳ phím tắt nào bạn sử dụng) để tìm một phương thức được xác định thông qua lập trình siêu mẫu, đặc biệt nếu tên của phương pháp được tạo tại thời điểm chạy.

Ví dụ sau xác định 3 phương pháp sử dụng lập trình siêu hình:

class RubyBlog
  def create_post_tags
    types = ['computer_science', 'tools', 'advanced_ruby']

    types.each do |type|
      define_singleton_method(type + "_tag") { puts "This post is about #{type}" }
    end
  end
end

rb = RubyBlog.new

rb.create_post_tags
rb.computer_science_tag

Các công cụ tạo tài liệu (như Yard hoặc RDoc ) không thể tìm thấy các phương pháp này và liệt kê chúng.

Các công cụ này sử dụng một kỹ thuật được gọi là “Phân tích tĩnh” để tìm các lớp và phương thức. Kỹ thuật này chỉ có thể tìm thấy các phương thức được xác định trực tiếp (sử dụng def cú pháp).

Thử chạy yard doc với ví dụ cuối cùng, bạn sẽ thấy rằng phương pháp duy nhất được tìm thấy là create_post_tags .

Có vẻ như thế này :

Chi phí ẩn của việc lập trình siêu hình

Có một cách để nói với yard để ghi lại các phương pháp bổ sung, sử dụng @method , nhưng điều đó không phải lúc nào cũng thực tế.

Ví dụ :

class Thing
  # @method build_report
  define_method(:build_report)
end

Ngoài ra, nếu bạn định sử dụng một công cụ như grep , ack hoặc trình chỉnh sửa của bạn để tìm kiếm định nghĩa phương pháp, sẽ khó hơn khi tìm phương pháp lập trình siêu thị hơn các phương pháp thông thường.

“Tôi không nghĩ Sidekiq sử dụng bất kỳ lập trình siêu hình nào bởi vì tôi thấy nó che khuất mã nhiều hơn là nó giúp ích cho 95% thời gian. ” - Mike Perham, Người tạo ra Sidekiq

Kết luận

Không phải mọi thứ đều xấu về lập trình siêu hình. Nó có thể hữu ích trong những trường hợp thích hợp để làm cho mã của bạn linh hoạt hơn.

Chỉ cần lưu ý về các chi phí phụ để bạn có thể đưa ra quyết định tốt hơn.

Đừng quên chia sẻ bài đăng này nếu bạn thấy nó hữu ích 🙂