Why Ruby do not optimize code at all?

On 29.03.2008 18:03, s.ross wrote:

proper design and / or algorithms make a huge difference. :slight_smile:

Yes, “controlled”. And yes, a good algo beats a machine optimization
almost every time. But still, some of the stupid optimization tricks
people have traditionally used for the last several percent destroy
the readability of code. I’ve never been terribly unhappy with the
performance of Ruby, but if I were … say … Industrial Light and
Magic and choosing between Ruby and Python to glue my effects stuff
together, I might be swayed toward Python because it preserves
bytecode (right?).

I am not sure what you mean by “preserve bytecode”.

It might be nice, once we figure out how to express
Ruby in an optimizable bytecode to create JIT optimizers that are
platform specific. Who knows what results that might yield.

There is JRuby already. IMHO this is a good way to go because a lot of
engineering went into JVM’s already and that way Ruby can benefit from
that without having to invest the same effort into the VM.

My original point, spelling not withstanding, is that this is a topic
(along with threading) that typically gets relegated to the premature
optimizations bin, and doesn’t rise to the level of “this should be
possible one day and it should work with only the predictable
glitches.” If I were heavily invested in my Java or C programming
skills, and wanted to keep Ruby out of my shop – and there are people
who do fight to keep new technologies away – lack of optimization
might be an attack plan.

I guess even with such optimizations Ruby will never come close in
performance to C and maybe even not to Java because of the highly
dynamic nature. OTOH, if explicit enabling of certain optimizations
disables some of the dynamic features that might be ok…

Again, just my opinion.

No problem with that. I just wanted to make the half serious point that
there is indeed a lot of room for programmer controlled optimization
that does not need -O3. :slight_smile:

Kind regards

robert

On Mar 29, 2008, at 10:40 AM, Robert K. wrote:

I am not sure what you mean by “preserve bytecode”.

Some interpreted languages (not saying which ones :slight_smile: have an option
for preserving the pre-parsed bytecode, saving the startup parsing
pass. This is useful for certain use-cases like Web apps or filters.

ts pisze:

it worked but a modification was made to ruby and the optimisation
break the code now

It’s not a premature optimisation, just unimplemented yet fallback with
/* TODO: if some === method is overrided */ comment in the source
(insns.def) for opt_case_dispatch specialized instruction (very easy to
accomplish having vm_opt_method_table and ruby_vm_redefined_flag).

For some time there’s been similar issue though for case:

a = 0
10.times do |i|
class Fixnum
private :+
end
a+=i
end

Where private didn’t record redefined bit in ruby_vm_redefined_flag
(vm.c). Now it’s fixed and checked against in rb_export_method
(eval_method.c) and rb_vm_check_redefinition_opt_method (vm.c).

So all standard ruby semantics is still obeyed without sacrificing 1.9
performance.

lopex

Marcin Mielżyński wrote:

It’s not a premature optimisation, just unimplemented yet fallback with
/* TODO: if some === method is overrided */ comment in the source
(insns.def) for opt_case_dispatch specialized instruction (very easy to
accomplish having vm_opt_method_table and ruby_vm_redefined_flag).

it’s a premature optimisation and even worth, in this case, a bug
because it’s known, at compile time, that the method #=== is
defined for Symbol, this mean that this optimisation must not exist
for Symbol#===

Guy Decoux

Marcin Mielżyński wrote:

Not true. The instruction itself is responsible for the fallback (just
not implemented yet).

vgs% ./ruby -ve ‘case “a”; when :a; p :ok; end’
ruby 1.9.0 (2008-04-01 revision 15881) [i686-linux]
vgs%

It’s a bug : it’s useless to add an optimisation which break
some code and in this case the fallback is precisely to call
Symbol#===, i.e. to bypass the optimisation.

Why you don’t do the same optimisation with Regexp#===,
Range#===, … to break more scripts ?

Guy Decoux

ts wrote:

Not true. The instruction itself is responsible for the fallback (just
not implemented yet).

lopex

ts wrote:

Marcin Mielżyński wrote:

Not true. The instruction itself is responsible for the fallback (just
not implemented yet).

vgs% ./ruby -ve ‘case “a”; when :a; p :ok; end’
ruby 1.9.0 (2008-04-01 revision 15881) [i686-linux]
vgs%

Actually, same happens for 1.8.6 (more Symbol#=== issue), but I’m aware
of your point.

It’s a bug : it’s useless to add an optimisation which break
some code and in this case the fallback is precisely to call
Symbol#===, i.e. to bypass the optimisation.

Well, that’s why I’d rather call it temporary breakage since the sources
say the author’s been aware of it from very beginning (chance the TODO:
comment). Just, opt_case_dispatch impl is not complete yet, still
being faster by default by not using funcall dispatch.

Actually a bug (but different) I’d call a compiler issue generating
opt_case_dispatch even with specialized_instruction option turned off,
so:

opts = VM::InstructionSequence.compile_option
opts[:specialized_instruction] = false
VM::InstructionSequence.compile_option = opts

s = VM::InstructionSequence.new %{
class Symbol
def === other
puts “foo”
end
end
case :s
when :s
end
}

s.eval

s.disam:

0000 putnil (
2)
0001 putnil
0002 defineclass :Symbol, class:Symbol, 0
0006 pop
0007 putobject :s (
7)
0009 dup
0010 opt_case_dispatch , 25
0013 putobject :s (
8)
0015 topn 1
0017 send :===, 1, nil, 0,
0023 branchif 29
0025 pop (
7)
0026 putnil
0027 leave
0028 pop
0029 pop (
9)
0030 putnil
0031 leave (
8)
== disasm: <ISeq:class:Symbol@>=============================
0000 putnil (
3)
0001 definemethod :===, ===, 0
0005 putnil
0006 leave
== disasm: <ISeq:===@>========================================
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1]
s1)
[ 2] other
0000 putnil (
4)
0001 putstring “foo”
0003 send :puts, 1, nil, 8,
0009 leave

lopex

Marcin Mielżyński wrote:

Well, that’s why I’d rather call it temporary breakage since the sources
say the author’s been aware of it from very beginning (chance the TODO:
comment). Just, opt_case_dispatch impl is not complete yet, still
being faster by default by not using funcall dispatch.

The TODO has nothing to do with this problem. It has worked one day
on early version of 1.9, then matz has modified the class Symbol
and brutally ruby failed because the optimisation was not removed

s = VM::InstructionSequence.new %{
class Symbol
def === other
puts “foo”
end
end
case :s
when :s
end
}

vgs% cat b.rb
#!/usr/bin/ruby
class Symbol
def === other
puts “foo”
end
end
case :s
when :s
puts “s”
end
vgs%

vgs% /usr/bin/ruby -v b.rb
ruby 1.8.6 (2007-06-07 patchlevel 36) [i486-linux]
b.rb:3: warning: method redefined; discarding old ===
foo
vgs%

vgs% ./ruby -v b.rb
ruby 1.9.0 (2008-04-01 revision 15881) [i686-linux]
b.rb:3: warning: method redefined; discarding old ===
foo
vgs%

Guy Decoux

ts pisze:

Marcin Mielżyński wrote:

Well, that’s why I’d rather call it temporary breakage since the sources
say the author’s been aware of it from very beginning (chance the TODO:
comment). Just, opt_case_dispatch impl is not complete yet, still
being faster by default by not using funcall dispatch.

The TODO has nothing to do with this problem. It has worked one day
on early version of 1.9, then matz has modified the class Symbol
and brutally ruby failed because the optimisation was not removed

This case ?

ruby -ve “puts :a === ‘a’”
ruby 1.8.6 (2008-02-02 patchlevel 5000) [i686-linux]
false

./ruby -ve “puts :a === ‘a’”
ruby 1.9.0 (2008-04-01 revision 15368) [i686-linux]
true

I still claim opt_case_dispatch will be responsible for the fallback
like any other opt_* opcode and no optimization will be removed.

puts “s”
ruby 1.9.0 (2008-04-01 revision 15881) [i686-linux]
b.rb:3: warning: method redefined; discarding old ===
foo
vgs%

That’s a bit surprising for me:
ruby -v a.rb
ruby 1.8.6 (2008-02-02 patchlevel 5000) [i686-linux]
a.rb:2: warning: method redefined; discarding old ===
foo

./ruby -v a.rb
ruby 1.9.0 (2008-04-01 revision 15368) [i686-linux]
a.rb:2: warning: method redefined; discarding old ===
s

Since opt_case_dispatch doesn’t fallback yet.

lopex

Marcin Mielżyński wrote:

That’s a bit surprising for me:

we don’t have the same version

vgs% make test

sample/test.rb:assignment

sample/test.rb:condition …
sample/test.rb:if/unless …
sample/test.rb:case …
sample/test.rb:while/until …
sample/test.rb:exception …
sample/test.rb:array …
sample/test.rb:hash …
sample/test.rb:iterator

sample/test.rb:float

sample/test.rb:bignum …
sample/test.rb:string & char

sample/test.rb:assignment …
sample/test.rb:call …
sample/test.rb:proc …
sample/test.rb:signal …
sample/test.rb:eval …
sample/test.rb:system …
sample/test.rb:const …
sample/test.rb:clone …
sample/test.rb:marshal …
sample/test.rb:pack …
sample/test.rb:math …
sample/test.rb:struct …
sample/test.rb:variable …
sample/test.rb:trace …
sample/test.rb:defined? …
sample/test.rb:alias …
sample/test.rb:path …
sample/test.rb:gc …
test succeeded

test_load.rb .
test_method.rb

test_objectspace.rb …
test_syntax.rb

test_marshal.rb .
test_eval.rb …
test_knownbug.rb …:0:in class_eval': undefined local variable or methodfoo’ for main:Object (NameError)
from :0:in class_eval' .:0:inclass_eval’: undefined local variable or method foo' for main:Object (NameError) from :0:inclass_eval’

test_flip.rb .
test_block.rb …
test_literal.rb

test_exception.rb …
test_fork.rb .
test_massign.rb …
test_proc.rb …
test_class.rb …
test_io.rb …
test_flow.rb …
test_thread.rb …
test_jump.rb …
test_attr.rb …
test_struct.rb .
PASS 816 tests
vgs%

I first try to make work ruby then I try to optimise
it, not the opposite.

Guy Decoux