Rubyの改良点のベンチマーク
裏話をまったく知らずにGoogleでRubyの改良点を検索すると、改良点が遅いという考えに気付くかもしれません。
それらが最初に提案されたように、改良は遅かったでしょう。それらは、インタプリタがメソッドルックアップのようなものを最適化することを不可能にしていたでしょう。
しかし、改良の実際の実装は、元の提案よりも少し制限されています。そのため、今日のRubyに存在するように、改良点について一連のベンチマークを実行するのは興味深いと思いました。
TL; DR
洗練は遅くはありません。または、少なくとも「通常の」方法よりも遅くはないようです。
メソッド呼び出しのベンチマークを行います。したがって、いくつかのメソッドが必要になります。
ここでは、ばかげた小さなメソッドの2つのバージョンを作成しています。 1つは「通常」で、もう1つは改良の内部です:
# 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
を介してアクティブ化する必要があります 声明。そこで、同じように動作する2つのクラスを作成します。 1つは改良を使用し、もう1つは使用しません。
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
リファインメントを使用してオブジェクトをインスタンス化するのが遅いのか、リファインメントを介して追加されたメソッドを呼び出すのが遅いのかを知りたいと思いました。
すべてのベンチマークは、OSXElCapitanでMRI2.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
-
Rubyでの挿入ソートを理解する
注:これは、Rubyを使用したさまざまなソートアルゴリズムの実装を検討するシリーズのパート4です。パート1ではバブルソート、パート2では選択ソート、パート3ではマージソートについて説明しました。 データを並べ替えるためのさまざまな方法を引き続き検討するため、挿入並べ替えに目を向けます。挿入ソートが好きな理由はたくさんあります!まず、挿入ソートは安定です。 、これは、等しいキーを持つ要素の相対的な順序を変更しないことを意味します。 インプレースアルゴリズムでもあります 、は、並べ替えられた要素を格納するための新しい配列を作成しないことを意味します。最後に、挿入ソートは、すぐにわかるように、実
-
Ruby2.6の9つの新機能
Rubyの新しいバージョンには、新しい機能とパフォーマンスの改善が含まれています。 変更についていきますか? 見てみましょう! 無限の範囲 Ruby 2.5以前のバージョンは、すでに1つの形式の無限範囲をサポートしています( Float ::INFINITY を使用) )、しかしRuby2.6はこれを次のレベルに引き上げます。 新しい無限の範囲 次のようになります: (1..) これは、(1..10)のような終了値がないため、通常の範囲とは異なります。 。 使用例 : [a, b, c].zip(1..) # [[a, 1], [b, 2], [c, 3]] [1,2,3,