I'm doing a proof of concept to see if nekovm would be a good target for a ruby 
backend (I didn't see anything over the last year in the ML archives to that 
effect). I'm getting wildly different times based on how I go about 
hand-translating. I'd love some hints/tips to try to see if this POC will pan 
out or not.

The goal is for a pure-OO language like ruby to translate down to neko and be 
at least as performant as MRI ruby (which is admittedly rather slow, esp in 
method dispatch). Ruby, like neko, has immediates for small integers, but 
they're pure OO, so I was boxing them in neko so I could simulate actual method 
calls (tho I wasn't using call/apply yet). The performance was surprising to me 
(from 5x faster to ~18x slower than ruby), and I'm guessing it is mainly due to 
the amount of allocations I was doing. No real profiling data tho (how?).

I'm sure in some places I'm just missing the point and could use a nudge.

// fib.neko:

puts = function(o) { $print(o); $print("\n") }

// minimal boxing function
O = function(v) {
 switch $typeof(v) {
 $tint => {
   var o = $new(Integer)
   o.value = v
   o
 }
 default =>
   v
 };
}

// Light Object "class", not fully wired up for a full OO system
Object             = $new(null)
Object.superclass  = null
Object.to_s        = function()     "<some object blah blah>"

// Simple Integer "class", not fully wired up for full OO system. can inherit
Integer            = $new(null)
Integer.superclass = Object
Integer.add        = function(x, y) x + y
Integer.__add      = function(o)    O($iadd(this.value, o.value))
Integer.__sub      = function(o)    O($isub(this.value, o.value))
Integer.__compare  = function(o)    $compare(this.value, o.value)
Integer.to_s       = function()     $string(this.value)
$objsetproto(Integer,Object)

////////////////////////////////////////////////////////////
// In sorted order:

// unboxed "OO" version, Directly calls Integer.add.         t=  1.27s
// totally incorrect translation, but speedy!
fib  = function(n)
   if n <= 2
       1
   else
       Integer.add(fib(n-1), fib(n-2))

// pure ruby version:                                        t=  7.07s

// un/box locally. will break if user overrides Integer#+/-. t= 16.44s
fib2_4 = function(n)
   if n.value <= 2
       O(1)
   else
       O(fib2(O(n.value - 1)).value + fib2(O(n.value - 2)).value)

// strip down the comparison to primatives. cached literals  t= 70.98s
one = O(1)
two = O(2)
fib2_2 = function(n)
   if n.value <= 2 
       O(1)
   else
       fib2(n - one) + fib2(n - two)

// Fully OO version, with cached literals                    t=100.52s
// calls "+" and lets the system figure it out.
fib2_1 = function(n)
   if n <= two
       one
   else
       fib2(n - one) + fib2(n - two)

// don't use one/two globals, box everything.                t=127.14s
fib2_3 = function(n)
   if n <= O(2)
       O(1)
   else
       fib2(n - O(1)) + fib2(n - O(2))

fib2 = fib2_4 // fastest OO version

puts(fib(10))            // => 55, yay!
puts(fib2(O(10)).to_s()) // => 55, yay!

iters = 100000

if true { // OO version
   var i = 0
   var j = O(0)
   while i < iters {
     j += fib2(O(10))
     i += 1
   }
   puts(j.to_s())
} else { // unboxed "OO" version
   var i = 0
   var j = 0
   while i < iters {
     j += fib(10)
     i += 1
   }
   puts(j)
}

--
Neko : One VM to run them all
(http://nekovm.org)

Reply via email to