Since you benchmark the entire program elapsed time, the loop itself — evaluating the exit condition, looking up the variable, and jumping back or out — adds to the execution time. The body of the loop is tiny, just a single FFI call, so the loop overhead is very likely significant, especially in the interpreted languages.
A simple fix is to unroll the loop several times. Maybe something like (here's the Wren one):
class FFI {
foreign static plusone(x)
foreign static count()
foreign static start()
foreign static stop()
}
var x = 0
var count = FFI.count()
// try call
FFI.plusone(x)
FFI.start()
while (x < count) {
x = FFI.plusone(FFI.plusone(FFI.plusone(FFI.plusone(FFI.plusone(x)))))
}
FFI.stop()
My hunch is that you'll see quite different relative numbers if you do this for all the languages. Right now, your benchmarks probably show the relative performance of assigning variables and looping more than they do FFI.
Since you benchmark the entire program elapsed time, the loop itself — evaluating the exit condition, looking up the variable, and jumping back or out — adds to the execution time. The body of the loop is tiny, just a single FFI call, so the loop overhead is very likely significant, especially in the interpreted languages.
A simple fix is to unroll the loop several times. Maybe something like (here's the Wren one):
My hunch is that you'll see quite different relative numbers if you do this for all the languages. Right now, your benchmarks probably show the relative performance of assigning variables and looping more than they do FFI.