뒷이야기를 모른 채 Google에서 Ruby 개선을 검색하면 개선이 느리다는 생각이 들 수 있습니다.
원래 제안된 대로 개선이 느렸을 것입니다. 그들은 인터프리터가 메소드 조회와 같은 것들을 최적화하는 것을 불가능하게 만들었을 것입니다.
그러나 개선 사항의 실제 구현은 원래 제안보다 약간 더 제한적입니다. 그래서 나는 오늘날 Ruby에 있는 것처럼 개선에 대한 일련의 벤치마크를 실행하는 것이 흥미로울 것이라고 생각했습니다.
TL;DR
수정은 느리지 않습니다. 또는 적어도 "정상적인" 방법보다 느리지 않은 것 같습니다.
더미 로드
메서드 호출을 벤치마킹할 것입니다. 따라서 몇 가지 방법이 필요합니다.
여기서는 어리석은 작은 방법의 두 가지 버전을 만들고 있습니다. 하나는 "정상"이고 다른 하나는 구체화 내부입니다.
# 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
우리는 정제를 직접 사용할 수 없습니다. using
을(를) 통해 활성화해야 합니다. 성명. 따라서 동일하게 동작하는 두 개의 클래스를 생성하겠습니다. 하나는 미세 조정을 사용하고 다른 하나는 사용하지 않습니다.
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
벤치마크
구체화를 사용하여 개체를 인스턴스화하거나 구체화를 통해 추가된 메서드를 호출하는 것이 더 느린지 알고 싶었습니다.
모든 벤치마크는 OSX El Capitan에서 MRI 2.2.2로 실행되었습니다.
객체 생성
"using" 키워드로 인해 클래스 초기화 속도가 느려집니까? 아닙니다.
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
메서드 호출
미세 조정이 "일반" 메서드 조회 속도에 영향을 줍니까? 아니요.
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
개선된 방법을 사용하는 것이 동등한 "일반" 방법을 사용하는 것보다 느립니까? 아닙니다.
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
전체 코드
벤치마크를 직접 실행하려면 다음 코드를 참조하세요.
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