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

Mã Ruby điểm chuẩn

Ruby có một công cụ đo điểm chuẩn trong thư viện chuẩn của nó để giúp đo lường hiệu suất của mã của bạn. Nó hữu ích nhất khi so sánh hai cách triển khai để tìm ra cách triển khai nhanh nhất.

Trong ví dụ này, chúng tôi được giao nhiệm vụ chuyển đổi một Hash với các khóa chuỗi (như {"foo" => "bar"} đến một với các ký hiệu (như {:foo => "bar"} ). Trong suốt các ví dụ, chúng tôi sẽ sử dụng một hàm băm với một khóa và một giá trị cho mỗi chữ cái trong bảng chữ cái tiếng Anh.

Để nhanh chóng tạo ra hàm băm này mà không cần phải gõ ra, chúng tôi sẽ chuyển đổi một loạt các chữ cái thành hàm băm thử nghiệm của chúng tôi. Chúng tôi sẽ đưa nó vào input biến để sử dụng sau này.

input = ("a".."z").map {|letter| [letter, letter]}.to_h
# => {"a"=>"a", "b"=>"b", "c"=>"c", "d"=>"d", "e"=>"e", "f"=>"f", "g"=>"g", "h"=>"h", "i"=>"i", "j"=>"j", "k"=>"k", "l"=>"l", "m"=>"m", "n"=>"n", "o"=>"o", "p"=>"p", "q"=>"q", "r"=>"r", "s"=>"s", "t"=>"t", "u"=>"u", "v"=>"v", "w"=>"w", "x"=>"x", "y"=>"y", "z"=>"z"}

Bây giờ chúng ta đã có input để kiểm tra việc triển khai của chúng tôi, chúng tôi sẽ viết một biến để xem nó hoạt động như thế nào. Một lớp lót tốt để chuyển đổi tất cả các khóa trong hàm băm đầu vào của chúng tôi thành các ký hiệu thay vì chuỗi trông như thế này:

input.map { |key, value| [key.to_sym, value] }.to_h
# => {:a=>"a", :b=>"b", :c=>"c", :d=>"d", :e=>"e", :f=>"f", :g=>"g", :h=>"h", :i=>"i", :j=>"j", :k=>"k", :l=>"l", :m=>"m", :n=>"n", :o=>"o", :p=>"p", :q=>"q", :r=>"r", :s=>"s", :t=>"t", :u=>"u", :v=>"v", :w=>"w", :x=>"x", :y=>"y", :z=>"z"}

Việc triển khai này sử dụng map phương pháp lặp qua băm để chạy một khối cho mỗi cặp khóa-giá trị. Trong khối, nó chuyển đổi khóa thành một ký hiệu và trả về một mảng hai phần tử với khóa ký hiệu mới được tạo và giá trị chưa được chạm.

Kết quả từ map lệnh là một mảng với 26 mảng khóa-giá trị. Vì chúng tôi cần một hàm băm, chúng tôi sử dụng #to_h để chuyển đổi mảng mới của chúng ta trở lại thành một hàm băm.

Benchmark.measure

Bây giờ chúng ta đã có một triển khai hoạt động, chúng ta có thể sử dụng mô-đun Benchmark của Ruby để xem nó hoạt động như thế nào.

require 'benchmark'
 
input = ('a'..'z').map { |letter| [letter, letter] }.to_h
 
puts Benchmark.measure {
  50_000.times do
    input.map { |key, value| [key.to_sym, value] }.to_h
  end
}

Benchmark.measure lấy một khối, được thực thi trong khi theo dõi thời gian thực thi. Nó trả về một chuỗi báo cáo, được in ra bảng điều khiển bằng cách sử dụng puts .

Vì đây là một đoạn mã nhanh, chúng tôi chạy nó 50.000 lần để đảm bảo rằng chúng tôi nhận được một số kết quả hiển thị.

$ ruby bench.rb
  0.810000   0.000000   0.810000 (  0.816964)

Chuỗi báo cáo hiển thị bốn số, đại diện cho thời gian CPU của người dùng (thời gian dành để thực thi mã của bạn), thời gian CPU hệ thống (thời gian dành cho nhân), cả thời gian CPU của người dùng và hệ thống được cộng lại và thời gian thực tế (hoặc thời gian trên đồng hồ treo tường) để khối thực thi trong dấu ngoặc.

Thời gian tường cho chúng ta thấy rằng chúng ta có thể chạy khối mã trên 50.000 lần trong hơn 800 mili giây một chút. Mặc dù đó là một con số ấn tượng, nhưng chúng tôi không biết điều đó có nghĩa là gì trừ khi chúng tôi so sánh nó với một cách triển khai mã khác.

Benchmark.bm

Bên cạnh Benchmark.measure , Ruby cung cấp Benchmark.bm , có thể chạy nhiều mẫu mã và in kết quả của chúng. Đối với mỗi mẫu, chúng tôi sẽ gọi Benchmark#report với tên và khối sẽ được thực thi.

require 'benchmark'
 
input = ("a".."z").map { |letter| [letter, letter] }.to_h
n = 50_000
 
Benchmark.bm do |benchmark|
  benchmark.report("Hash[]") do
    n.times do
      input.map { |key, value| [key.to_sym, value] }.to_h
    end
  end
 
  benchmark.report("{}.tap") do
    n.times do
      {}.tap do |new_hash|
        input.each do |key, value|
          new_hash[key.to_sym] = value
        end
      end
    end
  end
end

Trong điểm chuẩn này, chúng tôi sẽ sử dụng Benchmark.bm để kiểm tra hai triển khai bằng cách chạy mỗi 50.000 lần. Khối đo lường đầu tiên giống với ví dụ trước đó.

Trong khối đo lường thứ hai, chúng tôi sử dụng một triển khai dài hơn, tạo ra một hàm băm mới. Nó lặp lại hàm băm khóa chuỗi và thêm một phần tử vào hàm băm mới cho mọi mục. Bằng cách này, nó không phải chuyển đổi hàm băm thành một mảng và quay lại hàm băm khi nó hoàn thành.

Chạy lại điểm chuẩn sẽ cho chúng ta thấy việc triển khai này nhanh hơn 25%, mặc dù mã dài hơn (và kém thông minh hơn một chút) so với một đoạn mã mà chúng tôi đã thử trước đây.

$ ruby bench.rb
       user     system      total        real
Hash[]  0.850000   0.000000   0.850000 (  0.851106)
{}.tap  0.610000   0.020000   0.630000 (  0.637070)

Thêm điểm chuẩn

Khi làm việc trên một đoạn mã quan trọng trong cơ sở mã của bạn, việc chạy các điểm chuẩn để so sánh các cách triển khai khác nhau có thể cung cấp thêm thông tin chi tiết về tốc độ thực thi của chúng. Bằng cách so sánh các cách triển khai khác nhau để hiểu chúng tác động như thế nào đến hiệu suất, bạn sẽ có thể tránh các mẫu chống và viết Ruby nhanh hơn.

Mẹo :Rất nhiều thành ngữ phổ biến được đánh giá trước và kết quả của chúng được công bố dưới dạng nhanh-ruby. Đọc qua các ví dụ có thể giúp bạn tiết kiệm một số điểm chuẩn trong tương lai.

Có nhiều tùy chọn hơn bạn có thể kiểm tra cho ví dụ này và thư viện đo điểm chuẩn của Ruby có nhiều tính năng phức tạp hơn mà bạn có thể thử, nhưng điều này sẽ giới thiệu tốt về cách thức hoạt động của điểm chuẩn trong Ruby. Nếu bạn muốn biết thêm về đo điểm chuẩn hoặc có bất kỳ câu hỏi hoặc đề xuất nào, vui lòng cho chúng tôi biết tại @AppSignal.