Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

メタプログラミングの隠れたコスト

メタプログラミングは非常に派手な言葉のように聞こえますが、それは何か良いことですか?

便利な場合もありますが、メタプログラミングの使用にはいくらかのコストがかかることに多くの人が気づいていません。

同じページにいるので…

メタプログラミングとは 正確に?

私はメタプログラミングを次のような方法を使用するものとして定義しています:

  • コードの構造を変更します(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_methodmethod_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

結論

メタプログラミングについてすべてが悪いわけではありません。適切な状況でコードをより柔軟にすることが役立つ場合があります。

より良い決定を下せるように、追加費用に注意してください。

この投稿を共有することを忘れないでください 便利だと思ったら🙂


  1. 最高の無制限の無料VPNサービス(およびその隠れたコスト)

    VPNを使用する利点はよく知られています。 1つをワークフローに統合することにした場合は、無料のソリューションから始めてみてください。多くの無料サービスでは、送信できるデータの量が制限されているため、無制限の無料VPNオプションを探すのは理にかなっています。 これが、帯域幅を制限しない最高の無料の無制限VPNです。無料のVPNには通常いくつかの大きなリスクがあるため、これらのアプリを使用する際の隠れたコストについて見ていきます。 注: 以下の速度テストのベースラインとして、VPNを使用せずに書いた時点でのSpeedtest.netの結果は、11ms ping、30.21Mbpsダウン、11

  2. Rubyエイリアスキーワードの使用方法

    Rubyメソッドに別の名前を付けるには、次の2つの方法があります。 エイリアス(キーワード) alias_method 彼らはわずかに異なる方法で同じことをするので、これは紛らわしいトピックになる可能性があります。 この画像は違いの要約です : しっかりと理解するために、これらの違いをさらに詳しく調べてみましょう! エイリアスキーワード まず、aliasがあります 、これはRubyキーワードです(ifなど) 、def 、class 、など) このように見えます : alias print_something puts print_something 1 prin