You make a good point there. However, look at the very-efficient CL code : it looks like C code. Written in CL, sure, but it often deals with calculations on vectors of fixnums where the type of everything was predefined and type checking reduced to nothing. No dynamic dispatch of generic functions on heterogenous lists here.
But an FFI does not necessarily mean you have to write C code everytime you need an efficient calculation. Have a look at the following code I've just tested :
(def fib (n)
(if (<= n 2)
n
(+ (fib (- n 1)) (fib (- n 2)))))
(time:fib 30)
-> time: 47498 msec.
<< Hidden code here >>
(def ffib (n)
(if (<= n 2)
n
(+ (ffib (- n 1)) (ffib (- n 2)))))
(time:ffib 30)
-> time: 4635 msec.
More than 10 times faster, and they are both pure Arc code. What's the magic part ? Well, it just imports new declarations of +, - and <= I previously wrote in C (with the FFI I worked on recently). These definitions only deal with fixnums, that's why they are fast. Here is the full code :
(w/inline "
char inf (long a, long b){
return a <= b;
}
long minus (long a, long b){
return a - b;
}
long plus (long a, long b){
return a + b;
}"
(cdef _<= "inf" cbyte (clong clong))
(cdef - "minus" clong (clong clong))
(cdef + "plus" clong (clong clong))
(def <= (a b) (is 1 (_<= a b))))
Well, that declaration could be encapsulated into something like :
(declare-numeric
(def ffib ...
And you would end up with something as fast as CL (and looking like optimized CL code). Well, not really, but this is still alpha version.
Actually, if you are using Anarki, a lot of the slowness comes from infix.arc redefining math operators. Just removing infix.arc from libs.arc will increase the speed of math operations by about an order of magnitude. In fact, removing infix.arc actually produced more of a speedup for me than using your ffi.
With infix.arc:
arc> (def fib (n) (if (< n 2) 1 (+ (fib (- n 1)) (fib (- n 2)))))
#<procedure: fib>
arc> (time (fib 30))
time: 34672 msec.
1346269
Without infix.arc:
arc> (def fib (n) (if (< n 2) 1 (+ (fib (- n 1)) (fib (- n 2)))))
#<procedure: fib>
arc> (time (fib 30))
time: 4219 msec.
1346269
Yeah, sorry about that. I wasn't thinking about performance when I originally put infix.arc up on Anarki, and since I was doing a lot of testing at the time I found it convenient to add it to libs.arc. Perhaps it would be best to leave it out by default though.
Oh that's right... On my machine, the FFI version is still a little faster (about 20% faster), but not that much. It now deals more efficiently with boolean values however, that might explain it... Anyway, I don't know if it's worth using FFI this way now... Not until we can compile Arc code to efficient C code at least :)
Thanks for the counter example, you've made a very elegant point. It's quite encouraging in fact. It seems to point to the strength of Arc. I think this would require a lot more effort to achieve in Python.
Of course in this case one would hope that the fundamental arithmetic and relational operators would already be implemented efficiently in an Arc native binary compiler, but the principle you've illustrated still holds regardless.