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

Đánh giá điểm chuẩn của Ruby Refinements

Nếu bạn tìm kiếm các sàng lọc Ruby trên Google mà không biết bất kỳ câu chuyện phía sau nào, bạn có thể nghĩ rằng quá trình sàng lọc diễn ra chậm chạp.

Như chúng được đề xuất ban đầu, quá trình sàng lọc sẽ diễn ra chậm chạp. Họ sẽ khiến trình thông dịch không thể tối ưu hóa những thứ như tra cứu phương pháp.

Tuy nhiên, việc thực hiện các sàng lọc thực tế bị hạn chế hơn một chút so với đề xuất ban đầu. Vì vậy, tôi nghĩ sẽ rất thú vị khi chạy một loạt các điểm chuẩn về các sàng lọc như tồn tại trong Ruby ngày nay.

TL; DR

Quá trình cải tiến không hề chậm. Hoặc ít nhất chúng dường như không chậm hơn bất kỳ phương pháp "bình thường" nào.

Tải giả

Chúng ta sẽ gọi phương thức đo điểm chuẩn. Vì vậy, chúng tôi sẽ cần một vài phương pháp.

Ở đây, tôi đang tạo ra hai phiên bản của một phương pháp nhỏ ngớ ngẩn. Một là "bình thường" và một là trong quá trình sàng lọc:

# As our "dummy load" we're going to create shrugs. 
# 1 shrug == "¯\_(ツ)_/¯"
# 2 shrugs == "¯\_(ツ)_/¯¯\_(ツ)_/¯"
# ...etc.

SHRUG = "¯\_(ツ)_/¯"

# We'll make a refinement that generates shrugs
module Shruggable
  refine Fixnum do
    def shrugs
      SHRUG * self
    end
  end
end

# ...and we'll make a normal method that also generates shrugs
def integer_to_shrugs(n)
  SHRUG * n
end

Chúng tôi không thể sử dụng sàng lọc trực tiếp. Nó phải được kích hoạt qua using bản tường trình. Vì vậy, tôi sẽ tạo hai lớp hoạt động giống nhau. Một cái sử dụng sự sàng lọc, và cái kia thì không.

class TestUsing
  using Shruggable
  def noop
  end

  def shrug
    10.shrugs
  end
end

class TestWithoutUsing
  def noop
  end

  def shrug
    integer_to_shrugs(10)
  end
end

Điểm chuẩn

Tôi muốn biết liệu việc khởi tạo các đối tượng bằng cách sử dụng sàng lọc hoặc gọi các phương thức được thêm thông qua sàng lọc có chậm hơn không.

Tất cả các điểm chuẩn được chạy với MRI 2.2.2 trên OSX El Capitan.

Tạo đối tượng

Từ khóa "using" có làm cho một lớp khởi tạo chậm hơn không? Không.

Benchmark.ips do |bm|
  bm.report("class initialization") { TestUsing.new }
  bm.report("class initialization WITH using") { TestWithoutUsing.new }
  bm.compare!
end

# Calculating -------------------------------------
# class initialization   142.929k i/100ms
# class initialization WITH using
#                        145.323k i/100ms
# -------------------------------------------------
# class initialization      5.564M (± 8.3%) i/s -     27.728M
# class initialization WITH using
#                           5.619M (± 7.4%) i/s -     28.047M
# Comparison:
# class initialization WITH using:  5618601.3 i/s
# class initialization:  5564116.5 i/s - 1.01x slower

Lệnh gọi phương thức

Các sàng lọc có ảnh hưởng đến tốc độ tra cứu phương pháp "bình thường" không? Không.

Benchmark.ips do |bm|
  bm.report("run method") { TestUsing.new.noop }
  bm.report("run method in class WITH using") { TestWithoutUsing.new.noop }
  bm.compare!
end

# Calculating -------------------------------------
#           run method   141.905k i/100ms
# run method in class WITH using
#                        144.435k i/100ms
# -------------------------------------------------
#           run method      5.010M (± 6.4%) i/s -     24.975M
# run method in class WITH using
#                           5.086M (± 5.3%) i/s -     25.421M
# Comparison:
# run method in class WITH using:  5086262.3 i/s
#           run method:  5010273.6 i/s - 1.02x slower

Việc sử dụng một phương pháp từ sàng lọc có chậm hơn so với sử dụng một phương pháp "bình thường" tương đương không? Không.

Benchmark.ips do |bm|
  bm.report("shrug") { TestUsing.new.shrug }
  bm.report("shrug via refinement") { TestWithoutUsing.new.shrug }
  bm.compare!
end

# Calculating -------------------------------------
#                shrug    96.089k i/100ms
# shrug via refinement    95.559k i/100ms
# -------------------------------------------------
#                shrug      1.825M (± 9.3%) i/s -      9.128M
# shrug via refinement      1.929M (± 6.2%) i/s -      9.651M

# Comparison:
# shrug via refinement:  1928841.5 i/s
#                shrug:  1825069.4 i/s - 1.06x slower

Xếp chồng lên boong

Tôi có thể làm bất cứ điều gì để làm cho điểm chuẩn sàng lọc chậm hơn mức kiểm soát của tôi không? ¯\_(ツ)_/¯


# Does repeated evaluation of the `using` keyword affect performance. Only slightly. 
# This is an unfair test, but I really wanted to force refinements to be slower
# in SOME use case :)
Benchmark.ips do |bm|
  bm.report("inline shrug") { integer_to_shrugs(10) }
  bm.report("inline shrug via refinement") do
    using Shruggable
    10.shrugs
  end
  bm.compare!
end

# Calculating -------------------------------------
#         inline shrug   100.460k i/100ms
# inline shrug via refinement
#                         72.131k i/100ms
# -------------------------------------------------
#         inline shrug      2.507M (± 5.2%) i/s -     12.557M
# inline shrug via refinement
#                           1.498M (± 4.3%) i/s -      7.502M

# Comparison:
#         inline shrug:  2506663.9 i/s
# inline shrug via refinement:  1497747.6 i/s - 1.67x slower

Mã đầy đủ

Nếu bạn muốn tự chạy điểm chuẩn, đây là mã.

require 'benchmark/ips'


# As our "dummy load" we're going to create shrugs. 
# 1 shrug == "¯\_(ツ)_/¯"
# 2 shrugs == "¯\_(ツ)_/¯¯\_(ツ)_/¯"
# ...etc.

SHRUG = "¯\_(ツ)_/¯"

# We'll make a refinement that generates shrugs
module Shruggable
  refine Fixnum do
    def shrugs
      SHRUG * self
    end
  end
end

# ...and we'll make a normal method that also generates shrugs
def integer_to_shrugs(n)
  SHRUG * n
end

# Now we'll define two classes. The first uses refinments. The second doesn't. 
class TestUsing
  using Shruggable
  def noop
  end

  def shrug
    10.shrugs
  end
end

class TestWithoutUsing
  def noop
  end

  def shrug
    integer_to_shrugs(10)
  end
end

# Does the "using" keyword make a class slower to initialize? Nope. 
Benchmark.ips do |bm|
  bm.report("class initialization") { TestUsing.new }
  bm.report("class initialization WITH using") { TestWithoutUsing.new }
  bm.compare!
end

# Calculating -------------------------------------
# class initialization   142.929k i/100ms
# class initialization WITH using
#                        145.323k i/100ms
# -------------------------------------------------
# class initialization      5.564M (± 8.3%) i/s -     27.728M
# class initialization WITH using
#                           5.619M (± 7.4%) i/s -     28.047M
# Comparison:
# class initialization WITH using:  5618601.3 i/s
# class initialization:  5564116.5 i/s - 1.01x slower

# Do refinements affect "normal" method lookup speed? Nope. 
Benchmark.ips do |bm|
  bm.report("run method") { TestUsing.new.noop }
  bm.report("run method in class WITH using") { TestWithoutUsing.new.noop }
  bm.compare!
end

# Calculating -------------------------------------
#           run method   141.905k i/100ms
# run method in class WITH using
#                        144.435k i/100ms
# -------------------------------------------------
#           run method      5.010M (± 6.4%) i/s -     24.975M
# run method in class WITH using
#                           5.086M (± 5.3%) i/s -     25.421M
# Comparison:
# run method in class WITH using:  5086262.3 i/s
#           run method:  5010273.6 i/s - 1.02x slower


# Is using a method from a refinement slower than using an equivalent "normal" method? Nope. 
Benchmark.ips do |bm|
  bm.report("shrug") { TestUsing.new.shrug }
  bm.report("shrug via refinement") { TestWithoutUsing.new.shrug }
  bm.compare!
end

# Calculating -------------------------------------
#                shrug    96.089k i/100ms
# shrug via refinement    95.559k i/100ms
# -------------------------------------------------
#                shrug      1.825M (± 9.3%) i/s -      9.128M
# shrug via refinement      1.929M (± 6.2%) i/s -      9.651M

# Comparison:
# shrug via refinement:  1928841.5 i/s
#                shrug:  1825069.4 i/s - 1.06x slower


# Does repeated evaluation of the `using` keyword affect performance. Only slightly. 
# This is an unfair test, but I really wanted to force refinements to be slower
# in SOME use case :)
Benchmark.ips do |bm|
  bm.report("inline shrug") { integer_to_shrugs(10) }
  bm.report("inline shrug via refinement") do
    using Shruggable
    10.shrugs
  end
  bm.compare!
end

# Calculating -------------------------------------
#         inline shrug   100.460k i/100ms
# inline shrug via refinement
#                         72.131k i/100ms
# -------------------------------------------------
#         inline shrug      2.507M (± 5.2%) i/s -     12.557M
# inline shrug via refinement
#                           1.498M (± 4.3%) i/s -      7.502M

# Comparison:
#         inline shrug:  2506663.9 i/s
# inline shrug via refinement:  1497747.6 i/s - 1.67x slower