Project

General

Profile

Actions

Feature #18042

closed

YARV code optimization

Added by motoroller (Iskandar Gohar) over 2 years ago. Updated over 2 years ago.

Status:
Feedback
Assignee:
-
Target version:
-
[ruby-core:104665]

Description

Hi! Long period of time I think about programmatically code optimization for YARV. In compiled languages like C/C++ the compiler can do whatever it wants with the code and does for performance optimization. Firstly, ruby developers think about code readability, secondary about performance. Because ruby translates .rb file into bytecode we can do whit this bytecode anything to win in performance and do not lose in the expressiveness of the code.

But I came to the conclusion that a static bytecode optimizer is not possible, because in translation stage we don't know about which object/class we use. So, did someone think about runtime code analyzing? If we have some type of statistic we can dynamically transform bytecode to optimized version, use optimized version of C functions. Also I thought if we have statistic we can reduce some GC overhead

# before  
array.map(&:method1).map(&:method2) 

# if I know for sure that map is not overridden and calls from Enumerable I can rebuild code like this 
array.map do 
  _1.method1 
  _1.method2 
end 

In this example 2 map calls generate one redundant array which will be destructed by GC, so we can transform it to seconds version. It can be applied not only for map/select functions, but many other.

What do you think about it?

Actions #1

Updated by motoroller (Iskandar Gohar) over 2 years ago

  • Subject changed from YARV code optimization to deleted
  • Description updated (diff)

Updated by chrisseaton (Chris Seaton) over 2 years ago

This is how the various JITs for Ruby work. You usually think of a JIT emitting machine code, but they could also emit optimised YARV instructions.

Updated by motoroller (Iskandar Gohar) over 2 years ago

as I see mjit only translates yarv into C

Actions #4

Updated by hsbt (Hiroshi SHIBATA) over 2 years ago

  • Description updated (diff)
  • Subject changed from deleted to YARV code optimization

Updated by k0kubun (Takashi Kokubun) over 2 years ago

  • Status changed from Open to Feedback

as I see mjit only translates yarv into C

It's interesting that many people seem to think it's what MJIT does. We indeed copy-paste the original C code for most instructions, but it's not for optimizations but just for ease of maintenance. Translating VM instructions to C as is doesn't give any speedup. The speedup by eliminating PC/SP motion is also trivial. C compilers can do some analysis over multiple instructions for their optimizations, but in reality, it's also limited to opt_ instructions. While I'm not sure if you've ever seen benchmarks of MJIT, when MJIT gives a performance improvement, it's often leveraging runtime information. I'm not saying it has done enough of it though.

Updated by Hanmac (Hans Mackowiak) over 2 years ago

motoroller (Iskandar Gohar) wrote:

# before  
array.map(&:method1).map(&:method2) 

# if I know for sure that map is not overridden and calls from Enumerable I can rebuild code like this 
array.map do 
  _1.method1 
  _1.method2 
end 

there are differences with the code:

  • first lets say that array is [a, b]
  • the first example would call x = a.method1, y = b.method1 and then x.method2, y.method2
  • the second one would call a.method1, a.method2 then b.method1, b.method2

there is a difference in the order of the method calls, first one would first call all method1 then method2, the second one would call method1 and method2 in sequence

Updated by nobu (Nobuyoshi Nakada) over 2 years ago

Hanmac (Hans Mackowiak) wrote in #note-6:

motoroller (Iskandar Gohar) wrote:

# before  
array.map(&:method1).map(&:method2) 

# if I know for sure that map is not overridden and calls from Enumerable I can rebuild code like this 
array.map do 
  _1.method1 
  _1.method2 
end 

there are differences with the code:

And the second should be _1.method1.method2.

Actions #8

Updated by motoroller (Iskandar Gohar) over 2 years ago

@nobu (Nobuyoshi Nakada) Yes you are right, but I want to say that we don't need temp object in this case, so wy can't we do some optimizations? or one more case: code like this [1,2,3,4,5,6].select(&:even?).count will allocate new array only for return his size, isn't it superfluous? one more think I thought: rails programmers love create helper methods like

def hours_to_minutes(hours)
  hours * SECONDS_PER_MINUTE
end

def payments_sum
  sum_over_period(user.payments)
end

first example it's simple calculation and maybe we can transform body to inline variant without opt_send_without_block instruction? second example just call other methods, it's wrapper and we can remove this call and use only body.

Updated by chrisseaton (Chris Seaton) over 2 years ago

Yes these are all optimisations that compilers can do and you can see them being done in implementations of Ruby like TruffleRuby, but it requires a complex system of 'deoptimisation' to be able to go from the optimised code back to the full code, in case the assumptions you made when you compiled don't hold any more. You may have to do that deoptimisation while the code is already running.

Updated by motoroller (Iskandar Gohar) over 2 years ago

But way MRI team don't want to borrow the developments of other ruby implementations? because it's hard to support?

Updated by chrisseaton (Chris Seaton) over 2 years ago

I think you'd have to do a lot of work, to even get the basic benefits of this approach. So they may not want what is likely to be a very large volume of code for what is likely to be a small benefit.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0