[#31166] is_ruby_native_thread() — Masahiro Sakai (酒井政裕) <masahiro.sakai@...>

酒井です。

16 messages 2007/07/08
[#31269] Re: is_ruby_native_thread() — Nobuyoshi Nakada <nobu@...> 2007/07/21

なかだです。

[#31270] Re: is_ruby_native_thread() — Hidetoshi NAGAI <nagai@...> 2007/07/22

永井@知能.九工大です.

[#31298] retryの使い方 — eklerni <eklerni@...>

松尾といいます。

52 messages 2007/07/25
[#31299] Re: retryの使い方 — SASADA Koichi <ko1@...> 2007/07/26

 ささだです。

[#31300] Re: retryの使い方 — eklerni <eklerni@...> 2007/07/26

松尾です、返信ありがとうございます。

[#31303] Re: retryの使い方 — Yugui <yugui@...> 2007/07/26

Yuguiといいます。

[#31306] Re: retryの使い方 — eklerni <eklerni@...> 2007/07/26

松尾といいます。

[#31308] Re: retryの使い方 — Tanaka Akira <akr@...> 2007/07/26

In article <46A909DD.1070405@for.mail-box.ne.jp>,

[#31310] Re: retryの使い方 — eklerni <eklerni@...> 2007/07/26

Tanaka Akira さんは書きました:

[#31314] Re: retryの使い方 — Tanaka Akira <akr@...> 2007/07/30

In article <46A92530.80507@for.mail-box.ne.jp>,

[#31315] Re: retryの使い方 — eklerni <eklerni@...> 2007/07/30

Tanaka Akira さんは書きました:

[#31316] Re: retryの使い方 — Tanaka Akira <akr@...> 2007/07/30

In article <46AD7A16.8080509@for.mail-box.ne.jp>,

[#31317] Re: retryの使い方 — eklerni <eklerni@...> 2007/07/31

松尾です。

[#31381] Re: retryの使い方 — SASADA Koichi <ko1@...> 2007/08/12

 ささだです。

[#31422] Re: retryの使い方 — Yukihiro Matsumoto <matz@...> 2007/08/15

まつもと ゆきひろです

[#31425] Re: retryの使い方 — Tanaka Akira <akr@...> 2007/08/15

In article <E1ILDTi-0005T6-Be@x31>,

[#31426] Re: retryの使い方 — Yukihiro Matsumoto <matz@...> 2007/08/15

まつもと ゆきひろです

[#31433] Re: retryの使い方 — Tanaka Akira <akr@...> 2007/08/16

In article <E1ILKn6-0003Nv-0f@x31>,

[#31435] Re: retryの使い方 — Yukihiro Matsumoto <matz@...> 2007/08/16

まつもと ゆきひろです

[#31447] Re: retryの使い方 — Tanaka Akira <akr@...> 2007/08/16

In article <E1ILVN9-0006xJ-7I@x31>,

[#31450] Re: retryの使い方 — Tanaka Akira <akr@...> 2007/08/17

In article <E1ILq4x-0002Bs-Lg@x31>,

[#31451] Re: retryの使い方 — Yukihiro Matsumoto <matz@...> 2007/08/17

まつもと ゆきひろです

[ruby-dev:31319] DelegatorおよびWeakRefのメモリ使用量

From: "GO Noguchi" <gonoguti@...>
Date: 2007-07-31 20:10:05 UTC
List: ruby-dev #31319
野口と申します。

長文失礼致します。
WeakRefオブジェクトを大量に生成するプログラムで
NoMemoryErrorが発生するようになったので、調べてみたところ
delegate.rbにて委譲するメソッドを特異メソッドとして定義する際に、
同じ正規表現オブジェクトを大量に生成していることに気づきました。

特に理由がないのであれば、定数に置き換えるなどして無駄なオブジェクト
生成を避けた方がよいと思うのですが、いかがでしょうか。

--- delegate.rb.orig   Tue Feb 13 08:01:19 2007
+++ delegate.rb        Wed Aug  1 04:14:03 2007
@@ -116,6 +116,7 @@
 #
 class Delegator

+  IgnoreBacktracePat = /^\(eval\):|:in `__getobj__'$/
   #
   # Pass in the _obj_ to delegate method calls to.  All methods supported by
   # _obj_ will be delegated to.
@@ -138,8 +139,7 @@
            begin
              __getobj__.__send__(:#{method}, *args, &block)
            rescue Exception
-             $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
-             $@.delete_if{|s| /^\\(eval\\):/ =~ s}
+             $@.delete_if{|s| ::Delegator::IgnoreBacktracePat =~ s}
              Kernel::raise
            end
          end

参考までにベンチマークを取ってみました。
実行時間とプロセスのサイズを測っています。

コード:
require 'weakref'
require 'benchmark'
n = 1000
open("weakref_bench.log", "w"){|f|
  Benchmark.bm do |x|
    GC.start
    refs = Array.new(n)
    x.report("#{n} times with WeakRef") do
      n.times do |i|
        if i % 200 == 0
          GC.start
          f.puts("%07d: %s" % [i, open("|ps
awxl").readlines.grep(/ruby\s+weakref_bench.rb/)[0].chomp])
        end
        obj = {}
        refs << WeakRef.new(obj)
        obj = nil
      end
    end
  end
}
ここまで


変更前

結果:
  $ruby -v
  ruby 1.8.6 (2007-06-07 patchlevel 36) [i386-freebsd6.1]
  $ruby weakref_bench.rb
                               user     system      total        real
  1000 times with WeakRef 21.812500   1.890625  23.703125 ( 27.346026)

weakref_bench.log:
  0000000:  1001 29171 12538   2  -8  0  2676  2100 piperd S+    p1
0:00.03 ruby weakref_bench.rb
  0000200:  1001 29171 12538  44  -8  0 68388 63316 piperd S+    p1
0:03.51 ruby weakref_bench.rb
  0000400:  1001 29171 12538 126  -8  0 127672 118144 piperd S+    p1
  0:07.74 ruby weakref_bench.rb
  0000600:  1001 29171 12538 128 112  0 165688 151676 -      R+    p1
  0:12.53 ruby weakref_bench.rb
  0000800:  1001 29171 12538 107  -8  0 242488 224020 piperd S+    p1
  0:18.11 ruby weakref_bench.rb


正規表現を定数に変更後(grepの引数と書き出すファイル名を変更しています):

結果:
  $ruby weakref2_bench.rb
                                user     system      total        real
  1000 times with WeakRef  12.460938   0.679688  13.140625 ( 13.334997)

weakref2_bench.log:
  0000000:  1001 29177 12538  11  -8  0  2676  2100 piperd S+    p1
0:00.03 ruby weakref2_bench.rb
  0000200:  1001 29177 12538 162  -8  0 20644 20104 piperd S+    p1
0:02.35 ruby weakref2_bench.rb
  0000400:  1001 29177 12538 154  -8  0 35808 35288 piperd S+    p1
0:04.96 ruby weakref2_bench.rb
  0000600:  1001 29177 12538 253  -8  0 57356 56852 piperd S+    p1
0:07.62 ruby weakref2_bench.rb
  0000800:  1001 29177 12538 251  -8  0 97232 96768 piperd S+    p1
0:10.54 ruby weakref2_bench.rb

見たところ、メモリ消費量も実行時間も半分近くにはなっているようです。

もっとも、これでもオブジェクトごとに特異メソッドを定義しているのですが
問題が発生したプログラムでは、オブジェクトごとに特異メソッドを定義する必要は
なかったので、Delegatorに対するDelegateClassのように、WeakRefに対する
WeakRefClassを、weakref.rbを参考に定義してみました。
こちらを使うとクラス一つの定義で済むので大幅にパフォーマンスが改善するようです。


コード:
require 'weakref_class'
require 'benchmark'

class Dummy < WeakRefClass( Hash )
  def initialize
    super({})
  end
end

n = 1000
open("weakref_class_bench.log", "w"){|f|
  Benchmark.bm do |x|
    GC.start
    refs = Array.new(n)
    x.report("#{n} times with WeakRefClass") do
      n.times do |i|
        if i % 200 == 0
          f.puts("%07d: %s" % [i, open("|ps
awxl").readlines.grep(/ruby\s+weakref_class_bench.rb/)[0].chomp])
          GC.start
        end
        obj = {}
        refs << Dummy.new
        obj = nil
      end
    end
  end
}
ここまで


$ruby weakref_class_bench.rb
                                  user     system      total        real
1000 times with WeakRefClass  0.070312   0.007812   0.078125 (  0.134467)

0000000:  1001 29189 12538   5  -8  0  3088  2512 piperd S+    p1
0:00.04 ruby weakref_class_bench.rb
0000200:  1001 29189 12538  13  -8  0  3088  2536 piperd S+    p1
0:00.05 ruby weakref_class_bench.rb
0000400:  1001 29189 12538  30  -8  0  3088  2536 piperd S+    p1
0:00.06 ruby weakref_class_bench.rb
0000600:  1001 29189 12538  63  -8  0  3088  2536 piperd S+    p1
0:00.08 ruby weakref_class_bench.rb
0000800:  1001 29189 12538 130  -8  0  3088  2536 piperd S+    p1
0:00.10 ruby weakref_class_bench.rb

weakref_class.rb:

require 'weakref'
module WeakRefMixIn

  RefError = WeakRef::RefError

  @@id_map =  {}                # obj -> [ref,...]
  @@id_rev_map =  {}            # ref -> obj
  @@final = lambda{|id|
    __old_status = Thread.critical
    Thread.critical = true
    begin
      rids = @@id_map[id]
      if rids
        for rid in rids
          @@id_rev_map.delete(rid)
        end
        @@id_map.delete(id)
      end
      rid = @@id_rev_map[id]
      if rid
        @@id_rev_map.delete(id)
        @@id_map[rid].delete(id)
        @@id_map.delete(rid) if @@id_map[rid].empty?
      end
    ensure
      Thread.critical = __old_status
    end
  }

  def initialize(orig)
    __setobj__(orig)
  end

  def __setobj__(obj)
    @__id = obj.__id__
    __old_status = Thread.critical
    begin
      Thread.critical = true
      unless @@id_rev_map.key?(self)
        ObjectSpace.define_finalizer obj, @@final
        ObjectSpace.define_finalizer self, @@final
      end
      @@id_map[@__id] = [] unless @@id_map[@__id]
    ensure
      Thread.critical = __old_status
    end
    @@id_map[@__id].push self.__id__
    @@id_rev_map[self.__id__] = @__id
  end

  def method_missing(m, *args)
    target = self.__getobj__
    unless target.respond_to?(m)
      super(m, *args)
    end
    target.__send__(m, *args)
  end
  def respond_to?(m)
    return true if super
    return self.__getobj__.respond_to?(m)
  end

  def __getobj__
    unless @@id_rev_map[self.__id__] == @__id
      raise RefError, "Illegal Reference - probably recycled", caller(2)
    end
    begin
      ObjectSpace._id2ref(@__id)
    rescue RangeError
      raise RefError, "Illegal Reference - probably recycled", caller(2)
    end
  end

  # Returns true if the referenced object still exists, and false if it has
  # been garbage collected.
  def weakref_alive?
    @@id_rev_map[self.__id__] == @__id
  end

  # Clone support for the object returned by \_\_getobj\_\_.
  def clone
    super
    __setobj__(__getobj__.clone)
  end

  # Duplication support for the object returned by \_\_getobj\_\_.
  def dup(obj)
    super
    __setobj__(__getobj__.dup)
  end

end

def WeakRefClass(superclass)
  klass = Class.new
  klass.module_eval {
    include WeakRefMixIn
  }
  methods = superclass.public_instance_methods(true)
  methods -= ::Kernel.public_instance_methods(false)
  methods -= %w[to_s to_a inspect == =~ ===]
  methods -= WeakRefMixIn.public_instance_methods(false)
  methods -= WeakRefMixIn.private_instance_methods(false)
  methods -= WeakRefMixIn.protected_instance_methods(false)
  for method in methods
    begin
      klass.module_eval <<-EOS
        def #{method}(*args, &block)
          begin
            __getobj__.__send__(:#{method}, *args, &block)
          rescue
            $@[0, 2] = nil
            raise
          end
        end
      EOS
    rescue SyntaxError
      raise NameError, "invalid identifier %s" % method, caller(3)
    end
  end
  return klass
end

ここまで


---
GO Noguchi<gonoguti@yahoo.co.jp>

In This Thread

Prev Next