メタプログラミングの隠れたコスト
メタプログラミングは非常に派手な言葉のように聞こえますが、それは何か良いことですか?
便利な場合もありますが、メタプログラミングの使用にはいくらかのコストがかかることに多くの人が気づいていません。
同じページにいるので…
メタプログラミングとは 正確に?
私はメタプログラミングを次のような方法を使用するものとして定義しています:
- コードの構造を変更します(
define_method
など) ) - 文字列を実際のRubyコードの一部であるかのように実行します(
instance_eval
など)。 ) - 何らかのイベントへの反応として何かを行います(
method_missing
など) )
では、メタプログラミングのコストはいくらですか?私はそれらを3つのグループに分類します:
- 速度
- 読みやすさ
- 検索性
注 :4番目のグループがあるとも言えます:セキュリティ 。その理由は
eval
です 渡されたものに対していかなる種類のセキュリティチェックも行わないメソッド。自分で行う必要があります。
それぞれについて詳しく見ていきましょう!
スピード
ほとんどのメタプログラミング方法は通常の方法よりも遅いため、最初のコストは速度です。
ベンチマークコードは次のとおりです。
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>
結果(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
ご覧のとおり、両方のメタプログラミングメソッド(define_method
&method_missing
)通常の方法よりもかなり遅いです。
これが私が発見した興味深いものです…
上記の結果はRuby 2.2.4
からのものです 、ただし、これらのベンチマークをRuby 2.3
で実行する場合 またはRuby 2.4
これらの方法は遅くなっているようです!
Ruby2.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
このベンチマークを数回実行して、まぐれではないことを確認しました。
しかし、注意を払い、1秒あたりの反復を見ると(i/s
)Ruby 2.3以降、通常のメソッドが高速化されたようです。それがmethod_missing
の理由です かなり遅く見えます🙂
読みやすさ
instance_eval
を使用する場合、エラーメッセージはあまり役に立たない可能性があります / class_eval
メソッド。
次のコードを見てください:
class Thing class_eval("def self.foo; raise 'something went wrong'; end") end Thing.foo
これにより、次のエラーが発生します:
(eval):1:in 'foo': 'something went wrong...' (RuntimeError)
ファイル名が欠落していることに注意してください(eval
と表示されています) 代わりに)&正しい行番号。幸いなことに、これには修正があります。これらのeval
メソッドは2つの追加パラメーターを取ります:
- ファイル名
- 行番号
組み込み定数の使用__FILE__
&__LINE__
class_eval
のパラメータとして エラーメッセージに正しい情報が表示されます。
例 :
class Thing class_eval( "def foo; raise 'something went right'; end", __FILE__, __LINE__ ) end
なぜこれがデフォルトではないのですか?
わかりませんが、これらの方法を使用する場合は注意が必要です🙂
検索性
メタプログラミング手法を使用すると、コードの検索性が低下し、アクセスしにくくなり(ドキュメントが不適切なため)、デバッグが困難になります。
メソッド定義を探している場合、特にメソッドの名前が実行時に作成されている場合は、CTRL + F(または使用するショートカット)を実行してメタプログラミングで定義されたメソッドを見つけることはできません。
次の例では、3つのメソッドを定義しています。 メタプログラミングの使用:
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
ドキュメントを生成するツール(Yard
など) またはRDoc
)これらのメソッドを見つけてリストすることができません。
これらのツールは、「静的分析」と呼ばれる手法を使用して、クラスとメソッドを検索します。この手法では、(def
を使用して)直接定義されたメソッドのみを見つけることができます。 構文)。
yard doc
を実行してみてください 最後の例では、見つかったメソッドはcreate_post_tags
だけであることがわかります。 。
このように見えます :
yard
に伝える方法があります @method
を使用して、追加のメソッドを文書化する タグですが、それが常に実用的であるとは限りません。
例 :
class Thing # @method build_report define_method(:build_report) end
また、grep
のようなツールを使用する場合 、ack
、またはメソッド定義を検索するエディタでは、メタプログラミングメソッドを見つけるのが難しくなります 通常の方法より。
「Sidekiqはメタプログラミングを使用していないと思います 95%の確率で役立つ以上に、コードがわかりにくくなっていることがわかったからです。」 – Sidekiqの作成者、Mike Perham
結論
メタプログラミングについてすべてが悪いわけではありません。適切な状況でコードをより柔軟にすることが役立つ場合があります。
より良い決定を下せるように、追加費用に注意してください。
この投稿を共有することを忘れないでください 便利だと思ったら🙂
-
最高の無制限の無料VPNサービス(およびその隠れたコスト)
VPNを使用する利点はよく知られています。 1つをワークフローに統合することにした場合は、無料のソリューションから始めてみてください。多くの無料サービスでは、送信できるデータの量が制限されているため、無制限の無料VPNオプションを探すのは理にかなっています。 これが、帯域幅を制限しない最高の無料の無制限VPNです。無料のVPNには通常いくつかの大きなリスクがあるため、これらのアプリを使用する際の隠れたコストについて見ていきます。 注: 以下の速度テストのベースラインとして、VPNを使用せずに書いた時点でのSpeedtest.netの結果は、11ms ping、30.21Mbpsダウン、11
-
Rubyエイリアスキーワードの使用方法
Rubyメソッドに別の名前を付けるには、次の2つの方法があります。 エイリアス(キーワード) alias_method 彼らはわずかに異なる方法で同じことをするので、これは紛らわしいトピックになる可能性があります。 この画像は違いの要約です : しっかりと理解するために、これらの違いをさらに詳しく調べてみましょう! エイリアスキーワード まず、aliasがあります 、これはRubyキーワードです(ifなど) 、def 、class 、など) このように見えます : alias print_something puts print_something 1 prin