2011-12-23 2 views
18

まず第一に、私はextendincludeがどのように機能しているのかを知っています。彼らは通常、どのように使用されていますか。それは良いアイデアかどうかは私の質問の一部ではありません。ルビーではどれくらいのコストがかかりますか?

私の質問は:どれくらい高価ですかextend?これは、インスタンスとシングルトンオブジェクトを拡張する一般的なJavascriptテクニックです。 1つはRubyでも同様のことができますが、多くのオブジェクトで使用すると遅くなるでしょうか?

答えて

22

は、あなたがオブジェクトの上にextendを呼び出す場合のRuby 1.9.3-p0とで何が起こるか見てみましょう:

/* eval.c, line 879 */ 
void 
rb_extend_object(VALUE obj, VALUE module) 
{ 
    rb_include_module(rb_singleton_class(obj), module); 
} 

ので、モジュールは、オブジェクトのシングルトンクラスに混入されます。シングルトンクラスをフェッチするのはどれくらいの費用がかかりますか?まあ、rb_singleton_class_of(obj)singleton_class_of(obj)class.c:1253)です。シングルトンクラスが以前にアクセスされた(したがって既に存在している)場合、すぐにその値が返されます。ない場合は、新しいクラスが同様にあまりにも高価ではありませんどのmake_singleton_classによって作成されます。

/* class.c, line 341 */ 
static inline VALUE 
make_singleton_class(VALUE obj) 
{ 
    VALUE orig_class = RBASIC(obj)->klass; 
    VALUE klass = rb_class_boot(orig_class); 

    FL_SET(klass, FL_SINGLETON); 
    RBASIC(obj)->klass = klass; 
    rb_singleton_class_attached(klass, obj); 

    METACLASS_OF(klass) = METACLASS_OF(rb_class_real(orig_class)); 
    return klass; 
} 

これは、すべてのO(1)です。 rb_include_moduleclass.c:660)が呼び出されます。これは、シングルトンクラスがすでに含まれているモジュールの数に関して、O(n)です。モジュールが既に存在するかどうかをチェックする必要があるためですシングルトンクラスでは、これは問題ありません)。

結論: は非常に高価な操作ではありませんので、したい場合は頻繁に使用できます。私が想像できる唯一のことは、extendの後にインスタンスへのメソッド呼び出しの解決がモジュールの追加のレイヤーをチェックする必要があるため、もう少し複雑になる可能性があるということです。シングルトンクラスがすでに存在することが分かっている場合は、どちらも問題になりません。その場合、extendはほとんど複雑さをもたらさない。 しかし、インスタンスを動的に拡張すると、あまりに広範囲に適用するとコードが非常に読みにくくなる可能性がありますので注意してください。 (それはまったく同じことをしたが、興味深いことに、メタクラスのModule#includeObject#extendよりも、実際に遅い

  user  system  total  real 
Object#extend        0.200000 0.060000 0.260000 ( 0.272779) 
Object#extend (existing singleton class) 0.130000 0.000000 0.130000 ( 0.130711) 
Module#include       0.280000 0.040000 0.320000 ( 0.332719) 
Object#define_singleton_method   0.350000 0.040000 0.390000 ( 0.396296) 
create object without extending   0.060000 0.010000 0.070000 ( 0.071103) 
create object with extending    0.340000 0.000000 0.340000 ( 0.341622) 
create object with temp class    0.080000 0.000000 0.080000 ( 0.076526) 

require 'benchmark' 

module DynamicMixin 
    def debug_me 
    puts "Hi, I'm %s" % name 
    end 
end 

Person = Struct.new(:name) 

def create_people 
    100000.times.map { |i| Person.new(i.to_s) } 
end 

if $0 == __FILE__ 
    debug_me = Proc.new { puts "Hi, I'm %s" % name } 

    Benchmark.bm do |x| 
    people = create_people 
    case ARGV[0] 
    when "extend1" 
     x.report "Object#extend" do 
     people.each { |person| 
      person.extend DynamicMixin 
     } 
     end 
    when "extend2" 
     # force creation of singleton class 
     people.map { |x| class << x; self; end } 
     x.report "Object#extend (existing singleton class)" do 
     people.each { |person| 
      person.extend DynamicMixin 
     } 
     end 
    when "include" 
     x.report "Module#include" do 
     people.each { |person| 
      class << person 
      include DynamicMixin 
      end 
     } 
     end 
    when "method" 
     x.report "Object#define_singleton_method" do 
     people.each { |person| 
      person.define_singleton_method("debug_me", &debug_me) 
     } 
     end 
    when "object1" 
     x.report "create object without extending" do 
     100000.times { |i| 
      person = Person.new(i.to_s) 
     } 
     end 
    when "object2" 
     x.report "create object with extending" do 
     100000.times { |i| 
      person = Person.new(i.to_s) 
      person.extend DynamicMixin 
     } 
     end 
    when "object3" 
     class TmpPerson < Person 
     include DynamicMixin 
     end 

     x.report "create object with temp class" do 
     100000.times { |i| 
      person = TmpPerson.new(i.to_s) 
     } 
     end 
    end 
    end 
end 

結果:

は、この小さなベンチマークは、パフォーマンスに関する状況を示していますメタクラスにアクセスするために特別なRuby構文が必要なため)。 Object#extendは、シングルトンクラスがすでに存在する場合には2倍以上の速さです。 Object#define_singleton_methodは最も遅いですが(ただし、1つのメソッドだけを動的に追加したい場合は、よりクリーンにできます)

もっとも興味深い結果は、下の2つですが、次のとおりです。オブジェクトを作成してからそれを拡張するのは、オブジェクトを作成する場合の約4倍です!たとえば、ループ内に多くのオブジェクトを作成すると、そのオブジェクトを1つずつ拡張すると、パフォーマンスに大きな影響を与える可能性があります。明示的にmixinを含む一時クラスを作成する方がはるかに効率的です。

+0

によって、この記事を参照してください。 extendを呼び出すと、グローバルとインラインのRubyのすべてのメソッドキャッシュが無効になります。プログラムの実行中に頻繁にクラスやオブジェクトを拡張すると、かなり遅くなります。 – akuhn

8

注意しなければならないことは、rubyがメソッド実装を名前から検索するために使用するキャッシュを再設定することです。

これは、数年前のrailsconfのセッションでパフォーマンス上の問題が発生する可能性があることを覚えています。実際のパフォーマンスへの影響は何か分かりません。私を孤立してベンチマークするのは難しいものです。ニクラスベンチマークの適合、私はすべてのすべての人々を通って延び、その後のループでください最初のケースで

require 'benchmark' 

module DynamicMixin 
    def debug_me 
    puts "Hi, I'm %s" % name 
    end 
end 

Person = Struct.new(:name) 

def create_people 
    100000.times.map { |i| Person.new(i.to_s) } 
end 

if $0 == __FILE__ 
    debug_me = Proc.new { puts "Hi, I'm %s" % name } 

    Benchmark.bm do |x| 
    people = create_people 

    x.report "separate loop" do 
     people.each { |person| 
     person.extend DynamicMixin 
     } 
     people.each {|p| p.name} 
    end 

    people = create_people 

    x.report "interleaved calls to name" do 
     people.each { |person| 
     person.extend DynamicMixin 
     person.name 
     } 

    end 

    end 
end 

を行なったし、.nameメソッドを呼び出します。キャッシュの無効化はまだ起こっていますが、一度名前を呼び出すとキャッシュが暖かくなり、寒いことはありません。

2番目のケースでは、キャッシュを延長して.nameいつも冷たい私は.nameの

を呼び出すときに私が得る数字だから、インターリーブ呼び出しが遅い

 user  system  total  real 
separate loop 0.210000 0.030000 0.240000 ( 0.230208) 
interleaved calls to name 0.260000 0.030000 0.290000 ( 0.290910) 

です。唯一の理由は、メソッドルックアップキャッシュがクリアされていることは確かではありません。

+0

キャッシュの無効化がオブジェクトレベルまたはグローバルレベルで行われるかどうか知りたいですか?私は、これがこれと無関係の他のすべてのクラスを無効にするのであれば心配しています。 – lulalala

+1

ルビーのバージョンと実装によって異なります。現在のバージョンのMRIでは、それはクラスごとのキャッシュだと思います –

1

extendを呼び出すと、Rubyのすべてのメソッドキャッシュがグローバルとインラインの両方で無効になります。これは、どのクラス/オブジェクトをも拡張するたびにすべてのメソッドキャッシュがフラッシュされ、どのメソッド呼び出しもコールドキャッシュにヒットします。

なぜこれが悪く、メソッドキャッシュは何のために使用されていますか?

メソッドキャッシュは、Rubyプログラムを実行するときに時間を節約するために使用されます。たとえば、value.fooを呼び出すと、ランタイムはvalueの最新のクラスに関する情報とクラス階層fooがあるインラインキャッシュを追加します。これは、同じコールサイトからの将来のコールを高速化するのに役立ちます。

プログラムの実行中に頻繁にクラス/オブジェクトを拡張すると、処理速度が大幅に遅くなります。拡張するクラス/オブジェクトをプログラムの先頭に限定するのが最善です。

メソッドの定義やメソッドの解決に影響するその他の変更についても同様です。問題の詳細についてはinfomration

これは完全な話ではない、悲しいかな故ジェームズGolick、http://jamesgolick.com/2013/4/14/mris-method-caches.html

関連する問題