Hi
to my dispair (because it makes traits slow) method lookup is about
50% slower for methods defined via define_method vs. def.
If this is completely stupid a question just say so and sorry for
having wasted your time, but here I go.
Would there be any chance to get the same speed for method
lookup/invocation for methods defined via define_method?
Right now we have this:
647/147 > cat dispatch1.rb && ruby1.9 dispatch1.rb
# vim: sw=2 ts=2 ft=ruby expandtab tw=0 nu syn=on:
# file: dispatch1.rb
require 'benchmark'
module M
def a; 42 end
define_method :b do 42 end
def c; 42 end
end
o = Object.new.extend M
N=1_000_000
Benchmark::bmbm do |bm|
bm.report "method by def" do
N.times do
o.a
end
end
bm.report "define_method" do
N.times do
o.b
end
end
bm.report "def but after" do
N.times do
o.a
end
end
end
Rehearsal -------------------------------------------------
method by def 0.220000 0.000000 0.220000 ( 0.220175)
define_method 0.320000 0.000000 0.320000 ( 0.327819)
def but after 0.210000 0.000000 0.210000 ( 0.221985)
---------------------------------------- total: 0.750000sec
user system total real
method by def 0.210000 0.000000 0.210000 ( 0.218688)
define_method 0.310000 0.000000 0.310000 ( 0.326463)
def but after 0.200000 0.000000 0.200000 ( 0.222299)
----------------------------------------------------------------------------------------------------------------------------------------------------
Thx in advance
Robert
--
http://ruby-smalltalk.blogspot.com/
---
Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein
on 22.04.2008 17:40
on 22.04.2008 20:48
On Wed, Apr 23, 2008 at 12:39:29AM +0900, Robert Dober wrote: > to my dispair (because it makes traits slow) method lookup is about > 50% slower for methods defined via define_method vs. def. > If this is completely stupid a question just say so and sorry for > having wasted your time, but here I go. Why do you believe it is method lookup that is slower? Your benchmark tests both lookup and dispatch (I say this because I suspect it is the dispatch that is slow). > Would there be any chance to get the same speed for method > lookup/invocation for methods defined via define_method? > Right now we have this: Method dispatch to a method defined with define_method is not much less than the cost of calling a proc, which is essentially what's happening under the hood: [pbrannan@zaphod tmp]$ cat test.rb require 'benchmark' N=1_000_000 Benchmark::bmbm do |bm| p = proc { } o = Object.new osc = class << o; self; end osc.instance_eval { define_method(:foo, &p) } bm.report "define_method" do N.times { o.foo } end bm.report "proc.call" do N.times { p.call } end end [pbrannan@zaphod tmp]$ ruby1.9 -v test.rb ruby 1.9.0 (2008-03-20 revision 0) [i686-linux] Rehearsal ------------------------------------------------- define_method 1.090000 0.000000 1.090000 ( 1.095923) proc.call 1.210000 0.000000 1.210000 ( 1.218726) ---------------------------------------- total: 2.300000sec user system total real define_method 1.080000 0.020000 1.100000 ( 1.106690) proc.call 1.210000 0.000000 1.210000 ( 1.219874) To make methods defined with define_method faster, one would need to make proc invocation faster. I don't see any low-hanging fruit here, though. It's already much faster than 1.8: [pbrannan@zaphod tmp]$ ruby -v test.rb ruby 1.8.6 (2007-09-24 patchlevel 111) [i686-linux] Rehearsal ------------------------------------------------- define_method 3.190000 0.000000 3.190000 ( 3.196841) proc.call 3.250000 0.010000 3.260000 ( 3.255551) ---------------------------------------- total: 6.450000sec user system total real define_method 3.210000 0.000000 3.210000 ( 3.210368) proc.call 3.280000 0.000000 3.280000 ( 3.284931) Paul
on 22.04.2008 21:04
On Tue, Apr 22, 2008 at 8:46 PM, Paul Brannan <pbrannan@atdesk.com> wrote: > On Wed, Apr 23, 2008 at 12:39:29AM +0900, Robert Dober wrote: > > to my dispair (because it makes traits slow) method lookup is about > > 50% slower for methods defined via define_method vs. def. > > If this is completely stupid a question just say so and sorry for > > having wasted your time, but here I go. > > Why do you believe it is method lookup that is slower? Your benchmark > tests both lookup and dispatch (I say this because I suspect it is the > dispatch that is slow). Completely correct forgive my sloppy language here I was preciser below. > > > > Would there be any chance to get the same speed for method > > lookup/invocation for methods defined via define_method? > > Right now we have this: > > Method dispatch to a method defined with define_method is not much less > than the cost of calling a proc, which is essentially what's happening > under the hood: > Well spotted, I was thinking about this for some time and of course only *after* posting this I guess I had an idea of what is going on, as you said it is the proc that is called for methods defined with define_method and this is slower. However, if and only if, the block in the define_method does not use the closure we could really inline the block as the code of a method defined with def. Can this be determined at compile time? I have a strong feeling that yes, well I guess that might be an ultimate optimization item, hopefully. E.g. module M x = 42 define_method :a do x end # Cannot be inlined define_method :b do some_call; @x end # Can be inlined as if def were used end Did I miss something else? Cheers Robert -- http://ruby-smalltalk.blogspot.com/ --- Whereof one cannot speak, thereof one must be silent. Ludwig Wittgenstein
on 22.04.2008 22:45
Hi -- On Wed, 23 Apr 2008, Robert Dober wrote: > Completely correct forgive my sloppy language here I was preciser below. > Well spotted, I was thinking about this for some time and of course > E.g. > module M > x = 42 > define_method :a do x end # Cannot be inlined > define_method :b do some_call; @x end # Can be inlined as if def were used > end > > Did I miss something else? class C x = 100 define_method(:a) { x } define_method(:b){ puts eval(gets.chomp)} end David
on 23.04.2008 00:40
On Tue, Apr 22, 2008 at 10:44 PM, David A. Black <dblack@rubypal.com> wrote: > > class C > x = 100 > define_method(:a) { x } > define_method(:b){ puts eval(gets.chomp)} > end > > > David David I am afraid I do not get the point, you just gave another example of a method that cannot be "inlined" because of the eval, but that is easily detectable during compile time. I would need an example where the binding is needed and this fact cannot be detected during compilation! Cheers Robert -- http://ruby-smalltalk.blogspot.com/ --- Whereof one cannot speak, thereof one must be silent. Ludwig Wittgenstein
on 23.04.2008 00:41
> I would need an example where the binding is needed and this fact
Sorry: "binding".sub("binding", "closure")
on 23.04.2008 00:49
Hi -- On Wed, 23 Apr 2008, Robert Dober wrote: >> David > David I am afraid I do not get the point, > you just gave another example of a method that cannot be "inlined" > because of the > eval, but that is easily detectable during compile time. > I would need an example where the closure is needed and this fact > cannot be detected during compilation! define_method(:c) { puts send([101, 118, 97, 108].pack("C*"), gets.chomp) } :-) I don't know... it seems to me that determining statically whether or not a block will refer back to its context would be really, really difficult, at the very least. David
on 23.04.2008 01:03
Robert Dober wrote: > David I am afraid I do not get the point, > you just gave another example of a method that cannot be "inlined" > because of the > eval, but that is easily detectable during compile time. > I would need an example where the binding is needed and this fact > cannot be detected during compilation! Maybe David's point was that gets.chomp might return "x".
on 23.04.2008 01:54
On Wed, Apr 23, 2008 at 12:48 AM, David A. Black <dblack@rubypal.com> wrote: > > define_method(:c) { puts send([101, 118, 97, 108].pack("C*"), > gets.chomp) } > > :-) > > I don't know... it seems to me that determining statically whether or > not a block will refer back to its context would be really, really > difficult, at the very least. Not at all, David, at least not from your examples, it might follow this simple strategy Whenever a reference to the closure is made, --> return :do_not_inline Whenever metaprogramming like eval or send is done can I determine the parameters of send/eval e.g. simple literals --> evaluate them and react accordingly else --> return :do_not_inline return --> :inline It goes without saying that your examples will not be inlined allthough evaluating send([101, 118, 97, 108].pack("C*").gets.chomp could be done it surely is not worth it. In other words the attempt to inline a define_method block into a def would follow an early failure strategy. I give you an example: trait { behavior :a do 42 end } when define_method is called with the block above a very simple and defensive and fast check would still bring 50% performance gain for the invocations of a. Maybe the check can even be done against the bytecode, but I do not know when the block above is compiled. Now this is not a feature request but a request to learn more about the feasibility. Robert -- http://ruby-smalltalk.blogspot.com/ --- Whereof one cannot speak, thereof one must be silent. Ludwig Wittgenstein
on 23.04.2008 02:20
It really isn't possible to statically determine whether eval is called
in a given block, because it's possible that any given function may be
aliased to eval - even after the function is compiled. For example,
consider the following harmless code snippet:
class Foo
foo = 12
define_method(:bar) { puts "foo" }
end
Perfectly harmless, right? Well, not if I do this:
class Foo
alias_method :puts, :eval
end
Now Foo.new.bar returns 12. In fact, any method could become a call to
eval.
- Nathan
on 23.04.2008 04:39
On Apr 22, 2008, at 6:48 PM, David A. Black wrote: > I don't know... it seems to me that determining statically whether or > not a block will refer back to its context would be really, really > difficult, at the very least. Detecting open variables (i.e. variables not bound in the body of lambda) is fairly easy assuming you have access to the parse tree (which, of course, any Ruby impl would have). There are special cases, as Robert points out "eval" would have to be handled explicitly. The question is if there are other special cases beyond eval that need special handling. IIRC, Charles Nutter was advocating not allowing "eval" to be aliased for this exact reason.
on 23.04.2008 06:19
On Wed, Apr 23, 2008 at 4:38 AM, Jim Weirich <jim.weirich@gmail.com> wrote: > any Ruby impl would have). There are special cases, as Robert points out > "eval" would have to be handled explicitly. The question is if there are > other special cases beyond eval that need special handling. > > IIRC, Charles Nutter was advocating not allowing "eval" to be aliased for > this exact reason. Yes this was an excellent point made by Nathan and a very understandably decision by Charles! Now one still could just keep track of aliases to eval or just set one flag that disables the inline feature as soon as any form of eval or send is aliased. My aggressive fail fast algorithm should of course be shortened not to even try to evaluate literal arguments of these methods and that would make it pretty trivial as you have pointed out. One could even delay inlining the proc in the Hotspot manner say after the concerned method was called TRESHOLD times. Well I still would be very happy to stand corrected for every other oversight.... But assuming that the above can be done, would it be worthy? I mean how many method invocation would one need that the analysis and inlining effort would be payed back? Cheers Robert > > -- > -- Jim Weirich > -- jim.weirich@gmail.com > > > > > > -- http://ruby-smalltalk.blogspot.com/ --- Whereof one cannot speak, thereof one must be silent. Ludwig Wittgenstein
on 23.04.2008 08:08
Jim Weirich wrote: > question is if there are other special cases beyond eval that need > special handling. > > IIRC, Charles Nutter was advocating not allowing "eval" to be aliased > for this exact reason. And JRuby "cheats" currently, deoptimizing in the presence of any "eval" or similar method call, whether they've been aliased or not. Future versions will use runtime profiling to determine whether you're "actually" calling eval or not. But it's a decent trick at the moment, allowing us to optimize away heap-based variables in many cases. - Charlie
on 23.04.2008 08:20
David A. Black wrote: >>> define_method(:a) { x } >> cannot be detected during compilation! > > define_method(:c) { puts send([101, 118, 97, 108].pack("C*"), > gets.chomp) } > > :-) > > I don't know... it seems to me that determining statically whether or > not a block will refer back to its context would be really, really > difficult, at the very least. And thereforce certain types of blocks should not be allowed access to the enclosing scope/frame. I think the above examples are pretty bad karma too, and it's hard to argue how their utility outweighs the gross performance cost they incur. Check out normal method dispatch versus other types in JRuby. The difference here is largely whether we can perform certain optimizations that depend on knowing whether certain variables escape the scope: user system total real control: 100x x100 local var access 0.009000 0.000000 0.009000 ( 0.009000) core class: 100x x100 Fixnum#to_i 0.322000 0.000000 0.322000 ( 0.322000) ruby method: 100k x100 self.foo 0.888000 0.000000 0.888000 ( 0.888000) ruby method: 100k x100 self.foos 1.474000 0.000000 1.474000 ( 1.474000) ruby method: 100k x100 self.foos(1) 1.610000 0.000000 1.610000 ( 1.610000) ruby method: 100k x100 self.foos(4) 5.874000 0.000000 5.874000 ( 5.874000) ruby method: 100k x100 self.foo1 1.137000 0.000000 1.137000 ( 1.137000) ruby method: 100k x100 self.foo2 0.972000 0.000000 0.972000 ( 0.972000) ruby method: 100k x100 self.foo3 1.070000 0.000000 1.070000 ( 1.070000) ruby method: 100k x100 self.foo4 5.355000 0.000000 5.355000 ( 5.355000) __send__ method: 100k x100 self.foo 2.360000 0.000000 2.360000 ( 2.360000) ruby method(opt): 100k x100 self.baz 2.042000 0.000000 2.042000 ( 2.042000) ruby method(no opt): 100k x100 self.baz 1.166000 0.000000 1.166000 ( 1.166000) ruby method(block): 100k x100 self.quux 0.853000 0.000000 0.853000 ( 0.853000) ruby method(block{}): 10k x100 self.quux 0.497000 0.000000 0.497000 ( 0.497000) define_method method: 100k x100 calls 2.538000 0.000000 2.538000 ( 2.538000) And then the same run with some experimental "more optimistic" frame/scope optimizations: user system total real control: 100x x100 local var access 0.006000 0.000000 0.006000 ( 0.007000) core class: 100x x100 Fixnum#to_i 0.253000 0.000000 0.253000 ( 0.253000) ruby method: 100k x100 self.foo 0.413000 0.000000 0.413000 ( 0.413000) ruby method: 100k x100 self.foos 1.104000 0.000000 1.104000 ( 1.105000) ruby method: 100k x100 self.foos(1) 0.884000 0.000000 0.884000 ( 0.884000) ruby method: 100k x100 self.foos(4) 1.067000 0.000000 1.067000 ( 1.067000) ruby method: 100k x100 self.foo1 0.435000 0.000000 0.435000 ( 0.434000) ruby method: 100k x100 self.foo2 0.286000 0.000000 0.286000 ( 0.286000) ruby method: 100k x100 self.foo3 0.352000 0.000000 0.352000 ( 0.352000) ruby method: 100k x100 self.foo4 0.620000 0.000000 0.620000 ( 0.620000) __send__ method: 100k x100 self.foo 1.687000 0.000000 1.687000 ( 1.687000) ruby method(opt): 100k x100 self.baz 1.571000 0.000000 1.571000 ( 1.571000) ruby method(no opt): 100k x100 self.baz 0.448000 0.000000 0.448000 ( 0.448000) ruby method(block): 100k x100 self.quux 0.443000 0.000000 0.443000 ( 0.443000) ruby method(block{}): 10k x100 self.quux 0.434000 0.000000 0.434000 ( 0.434000) define_method method: 100k x100 calls 2.472000 0.000000 2.472000 ( 2.472000) Finally Ruby 1.9 for comparison: user system total real control: 100x x100 local var access 0.010000 0.000000 0.010000 ( 0.006355) core class: 100x x100 Fixnum#to_i 0.880000 0.000000 0.880000 ( 0.892870) ruby method: 100k x100 self.foo 0.890000 0.000000 0.890000 ( 0.895106) ruby method: 100k x100 self.foos 5.310000 0.010000 5.320000 ( 5.307588) ruby method: 100k x100 self.foos(1) 5.490000 0.010000 5.500000 ( 5.506018) ruby method: 100k x100 self.foos(4) 5.710000 0.010000 5.720000 ( 5.709975) ruby method: 100k x100 self.foo1 0.950000 0.000000 0.950000 ( 0.949075) ruby method: 100k x100 self.foo2 1.000000 0.000000 1.000000 ( 0.997541) ruby method: 100k x100 self.foo3 1.040000 0.000000 1.040000 ( 1.048048) ruby method: 100k x100 self.foo4 1.130000 0.000000 1.130000 ( 1.129857) __send__ method: 100k x100 self.foo 1.060000 0.010000 1.070000 ( 1.063126) ruby method(opt): 100k x100 self.baz 2.430000 0.000000 2.430000 ( 2.433559) ruby method(no opt): 100k x100 self.baz 1.050000 0.000000 1.050000 ( 1.046770) ruby method(block): 100k x100 self.quux 0.970000 0.000000 0.970000 ( 0.974937) ruby method(block{}): 10k x100 self.quux 0.610000 0.000000 0.610000 ( 0.606299) define_method method: 100k x100 calls 2.170000 0.010000 2.180000 ( 2.172675) The ability to make optimizations depending on the callee's access to your frame/scope makes a tremendous difference in performance. With the more optimistic optimizations, JRuby's method dispatch can be as much as 4-8x as fast as Ruby 1.9. Benchmark source is here: http://svn.codehaus.org/jruby/trunk/jruby/test/bench/language/bench_method_dispatch.rb - Charlie
on 23.04.2008 08:24
David A. Black wrote: > Hi -- > > On Wed, 23 Apr 2008, Robert Dober wrote: >> David I am afraid I do not get the point, >> you just gave another example of a method that cannot be "inlined" >> because of the >> eval, but that is easily detectable during compile time. >> I would need an example where the closure is needed and this fact >> cannot be detected during compilation! ... > I don't know... it seems to me that determining statically whether or > not a block will refer back to its context would be really, really > difficult, at the very least. Also...regardless of whether such determination can be made statically, it can certainly be made dynamically. I have prototype code for JRuby that does exactly this during the interpretation phase, using that information during JIT to better optimize the method. So there's certainly a way to do it, but more consistent and helpful static hints would make it a lot easier and at least possible on static-compiling implementations like Ruby 1.9, IronRuby, and Rubinius. - Charlie
on 23.04.2008 09:01
Charles Oliver Nutter wrote: > ... > implementations like Ruby 1.9, IronRuby, and Rubinius. Will jruby de-optimize dynamically, as in this case? class C x = 100 define_method(:b){puts foo("x")} def foo arg; "FOO #{arg}"; end end c = C.new c.b class << c; alias foo eval; end c.b
on 23.04.2008 10:38
Robert Dober wrote: > However, if and only if, the block in the define_method does not use > the closure we could really inline the block as the code of a method > defined with def. What do you want to say with "inline" ? vgs% ./ruby -e 'puts VM::InstructionSequence.compile("proc {|x| x = 12 }.call(1)").disasm' == disasm: <ISeq:<compiled>@<compiled>>================================= == catch table | catch type: break st: 0000 ed: 0007 sp: 0000 cont: 0007 |------------------------------------------------------------------------ 0000 putnil ( 1) 0001 send :proc, 0, block in <compiled>, 8, <ic> 0007 putobject 1 0009 send :call, 1, nil, 0, <ic> 0015 leave == disasm: <ISeq:block in <compiled>@<compiled>>======================== == catch table | catch type: redo st: 0000 ed: 0006 sp: 0000 cont: 0000 | catch type: next st: 0000 ed: 0006 sp: 0000 cont: 0006 |------------------------------------------------------------------------ local table (size: 1, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s3) [ 1] x<Arg> 0000 putobject 12 ( 1) 0002 dup 0003 setdynamic x, 0 0006 leave vgs% vgs% ./ruby -e 'puts VM::InstructionSequence.compile("def a(x) x = 12; end; a(1)").disasm' == disasm: <ISeq:<compiled>@<compiled>>================================= 0000 putnil ( 1) 0001 definemethod :a, a, 0 0005 putnil 0006 putobject 1 0008 send :a, 1, nil, 8, <ic> 0014 leave == disasm: <ISeq:a@<compiled>>========================================== local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s1) [ 2] x<Arg> 0000 putobject 12 ( 1) 0002 dup 0003 setlocal x 0005 leave vgs% Guy Decoux
on 23.04.2008 11:15
On Wed, Apr 23, 2008 at 10:37 AM, ts <decoux@moulon.inra.fr> wrote: > | catch type: break st: 0000 ed: 0007 sp: 0000 cont: 0007 > |------------------------------------------------------------------------ > == disasm: <ISeq:<compiled>@<compiled>>================================= > 0002 dup > 0003 setlocal x > 0005 leave > vgs% > > > > Guy Decoux > > basically I mean that IS.compile("def a; 42 end") == IS.compile("define_method :a do 42 end"). Read == as "could be" please. R. -- http://ruby-smalltalk.blogspot.com/ --- Whereof one cannot speak, thereof one must be silent. Ludwig Wittgenstein
on 23.04.2008 11:27
Robert Dober wrote: > basically I mean that IS.compile("def a; 42 end") == > IS.compile("define_method :a do 42 end"). Well you can do it like me vgs% ./ruby -e 'puts VM::InstructionSequence.compile("def a() 12; end").disasm' == disasm: <ISeq:<compiled>@<compiled>>================================= 0000 putnil ( 1) 0001 definemethod :a, a, 0 0005 putnil 0006 leave == disasm: <ISeq:a@<compiled>>========================================== 0000 putobject 12 ( 1) 0002 leave vgs% vgs% ./ruby -e 'puts VM::InstructionSequence.compile("define_method :a do 12; end").disasm' == disasm: <ISeq:<compiled>@<compiled>>================================= == catch table | catch type: break st: 0000 ed: 0009 sp: 0000 cont: 0009 |------------------------------------------------------------------------ 0000 putnil ( 1) 0001 putobject :a 0003 send :define_method, 1, block in <compiled>, 8, <ic> 0009 leave == disasm: <ISeq:block in <compiled>@<compiled>>======================== == catch table | catch type: redo st: 0000 ed: 0002 sp: 0000 cont: 0000 | catch type: next st: 0000 ed: 0002 sp: 0000 cont: 0002 |------------------------------------------------------------------------ 0000 putobject 12 ( 1) 0002 leave vgs% You really think that it exist a big difference *at compile time* ? Guy Decoux
on 23.04.2008 15:25
Joel VanderWerf wrote:
> Will jruby de-optimize dynamically, as in this case?
Not at the moment, and it's an entertaining way to break JRuby for fun
and profit. I could add in the check for eval being aliased, and display
a warning and deoptimize everything, but I currently do not because I've
been waiting to see if any libraries break. So far, nobody does this.
The dynamic optimizations that will eventually go into place would check
for this, so that if the methods seen previously suddenly change
(aliased, redefined) the original code will be backed down.
- Charlie
on 23.04.2008 15:51
On Wed, Apr 23, 2008 at 10:24:43PM +0900, Charles Oliver Nutter wrote: > Not at the moment, and it's an entertaining way to break JRuby for fun > and profit. I could add in the check for eval being aliased, and display > a warning and deoptimize everything, but I currently do not because I've > been waiting to see if any libraries break. So far, nobody does this. Maybe it should be vocal, even if you don't deoptimize: test.rb:42: warning: aliasing eval? you evil, evil person. If it's not vocal, one might not notice right away that something broke. Paul
on 23.04.2008 16:09
On Wed, Apr 23, 2008 at 04:03:09AM +0900, Robert Dober wrote: > module M > x = 42 > define_method :a do x end # Cannot be inlined > define_method :b do some_call; @x end # Can be inlined as if def were used > end You can "inline" (it's really not the right word) either one using nodewrap, but it's very evil to do: /home/user> irb1.9 irb(main):001:0> require 'nodewrap' => true irb(main):002:0> p = proc { 42 } => #<Proc:0xb7b848d8@(irb):2> irb(main):003:0> p.body => <ISeq:block (3 levels) in irb_binding@(irb)> irb(main):004:0> class Foo; end => nil irb(main):005:0> Foo.add_method :foo, p.body, Noex::PUBLIC => nil irb(main):006:0> Foo.new.foo => 42 Paul
on 23.04.2008 16:38
Paul Brannan wrote:
> If it's not vocal, one might not notice right away that something broke.
Probably a good idea. I'll file a bug.
- Charlie
on 23.04.2008 18:02
On Wed, Apr 23, 2008 at 11:25 AM, ts <decoux@moulon.inra.fr> wrote: > > > You really think that it exist a big difference *at compile > time* ? No Guy, at *runtime* about 50% slower. Please have a look at Charles' benchmarks I would never dare posting mine now ;). Salut Robert -- http://ruby-smalltalk.blogspot.com/ --- Whereof one cannot speak, thereof one must be silent. Ludwig Wittgenstein
on 23.04.2008 18:12
On Wed, Apr 23, 2008 at 10:37 AM, ts <decoux@moulon.inra.fr> wrote: > Robert Dober wrote: > > However, if and only if, the block in the define_method does not use > > the closure we could really inline the block as the code of a method > > defined with def. > > What do you want to say with "inline" ? As others pointed out "inline" surely is not the best name, but I was completely ignorant when starting this thread. I learnt so much already here I am not stopping now ;) Charles how would you denominate this optimization if I may dare asking YAFM [ yet another five minutes (c) Robert Dober] of your time. Thx a lot in advance R.
on 23.04.2008 18:19
Robert Dober wrote: > No Guy, at *runtime* about 50% slower. Please have a look at Charles' > benchmarks I would never dare posting mine now ;). and you're not surprised by the difference between self.foo and self.foos ? Guy Decoux
on 23.04.2008 19:56
On Wed, Apr 23, 2008 at 6:19 PM, ts <decoux@moulon.inra.fr> wrote: > Robert Dober wrote: > > No Guy, at *runtime* about 50% slower. Please have a look at Charles' > > benchmarks I would never dare posting mine now ;). > > and you're not surprised by the difference between self.foo and > self.foos ? I had not looked into that because it was not my point of interest, but now that you mention it, it is indeed amazing how costly the splat seems to be. Another thing I find slightly surprising is that Charles uses Benchmark#bm instead of Benchmark#bmbm, probably a JRuby internal reason but just wondering... Cheers R. -- http://ruby-smalltalk.blogspot.com/ --- Whereof one cannot speak, thereof one must be silent. Ludwig Wittgenstein
on 23.04.2008 19:58
Robert Dober wrote: > Charles how would you denominate this optimization if I may dare > asking YAFM [ yet another five minutes (c) Robert Dober] > of your time. Hmm, block unwrapping? Essentially what you want to do to make define_method methods as fast as regular methods is to make them regular methods; that means unwrapping the block and making its body the body of an actual method, rather than doing block dispatch semantics wrapped in method dispatch semantics. And with 1.9's much more restrictive block arguments, you're not losing much there. Obviously you lose the fact that it might be a true closure, and you'd have to go through the AST/bytecode and check for variable depths > 0, but it would be worth it to make performance decent. IMHO define_method would be a lot more useful if it actually made a (fast) method rather than an ugly (slow) conglomeration of a method and a proc. - Charlie
on 23.04.2008 20:08
On Apr 23, 2008, at 1:56 PM, Charles Oliver Nutter wrote: > IMHO define_method would be a lot more useful if it actually made a > (fast) method rather than an ugly (slow) conglomeration of a method > and a proc. Amen.
on 23.04.2008 22:17
On Thu, Apr 24, 2008 at 02:56:54AM +0900, Charles Oliver Nutter wrote:
> conglomeration of a method and a proc.
But if you don't need to access variables outside your scope, then why
use define_method at all? In that case def works just fine.
Paul
on 23.04.2008 22:37
Paul Brannan wrote: >> useful if it actually made a (fast) method rather than an ugly (slow) >> conglomeration of a method and a proc. > > But if you don't need to access variables outside your scope, then why > use define_method at all? In that case def works just fine. define_method :foo, &some_proc - Charlie
on 24.04.2008 06:01
On Wed, Apr 23, 2008 at 8:06 PM, Jim Weirich <jim.weirich@gmail.com> wrote: > On Apr 23, 2008, at 1:56 PM, Charles Oliver Nutter wrote: > > > IMHO define_method would be a lot more useful if it actually made a (fast) > method rather than an ugly (slow) conglomeration of a method and a proc. > > > > Amen. Yup that's why I asked :) Robert -- http://ruby-smalltalk.blogspot.com/ --- Whereof one cannot speak, thereof one must be silent. Ludwig Wittgenstein