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)