Hi,
James Bielman wrote:
Thu Feb 2 18:41:28 CST 2006 James Bielman jamesjb@jamesjb.com
- Add an optimization for defining non-translatable types.
Is that something akin to sealing? Maybe that's a good step towards optimization. I've noticed the following behaviour (with CFFI-CVS from last week or so), which led me to write this note:
Since the addition of the new type translators, I'd got some bad feelings in my stomach (and wrote about it), without the time to look closer. Now I looked.
Somehow I feel there's a missing API for compile-time transformations in the new branch, similar to the old duality provided by translate-to-c <-> to-c-FORM.
To make a long story short, here's output for test struct.5 (defctype my-int :int) (defcstruct s5 (a my-int)) (deftest struct.5 (with-foreign-object (s 's5) (setf (foreign-slot-value s 's5 'a) 42) (foreign-slot-value s 's5 'a)) 42)
The old CFFI produces (just skim over the rightmost column): 12 Bytecode-Instruktionen: 0 (LOAD&PUSH 1) 1 (CALL1&PUSH 0) ; FFI:FOREIGN-ADDRESS 3 (CONST&PUSH 1) ; 42 4 (LOAD&PUSH 1) 5 (CONST&PUSH 2) ; FFI:INT 6 (CONST&PUSH 3) ; 0 7 (CALL 4 4) ; FFI::WRITE-MEMORY-AS 10 (LOAD&PUSH 0) 11 (CONST&PUSH 2) ; FFI:INT 12 (CONST&PUSH 3) ; 0 13 (CALL 3 5) ; FFI:MEMORY-AS 16 (SKIP&RET 3) Neat. Perfect.
The new branch of CFFI produces: 18 Bytecode-Instruktionen: 0 (LOAD&PUSH 1) 1 (CALL1&PUSH 0) ; FFI:FOREIGN-ADDRESS 3 (CONST&PUSH 1) ; 42 4 (CONST&PUSH 2) ; #<CFFI::FOREIGN-TYPEDEF MY-INT> 5 (CALL2&PUSH 3) ; CFFI::TRANSLATE-TYPE-TO-FOREIGN 7 (LOAD&PUSH 0) 8 (LOAD&PUSH 2) 9 (CONST&PUSH 4) ; FFI:INT 10 (CONST&PUSH 5) ; 0 11 (CALL 4 6) ; FFI::WRITE-MEMORY-AS 14 (SKIP 1) 16 (LOAD&PUSH 0) 17 (CONST&PUSH 4) ; FFI:INT 18 (CONST&PUSH 5) ; 0 19 (CALL&PUSH 3 7) ; FFI:MEMORY-AS 22 (CONST&PUSH 2) ; #<CFFI::FOREIGN-TYPEDEF MY-INT> 23 (CALL2 8) ; CFFI::TRANSLATE-TYPE-FROM-FOREIGN 25 (SKIP&RET 3)
Ouch. Two generic function calls, which each call another generic function (e.g. translate-to-foreign), some of which cons, e.g. (defmethod translate-type-to-foreign (value (type foreign-typedef)) ;;We build a list out of the second value returned... ;;IMHO bug: 1. undocumented and 2. most don't provide a second value
So we compare o 4 GF calls in total, and some consing o to nothing like this previously o with something a C compiler compiles to 2-4 native instructions.
IMHO this hurts. It hurts even more so, since CFFI originaly started out from disatisfaction with UFFI on cmucl, where James Bielman observed boxing of float values IIRC, and he initially reported performance improvements over UFFI using his approach. Right now, I'd expect CFFI to lag far behind UFFI in performance (at least with typedefs and structs, other tests are still compiled fine).
The type translators may have gained a lot of flexibility. However such gain should not come at the cost of lack of compile-time optimization. People often enough turn towards an FFI when some fast library is needed they don't want to port...
I believe there's a need for some translate-*-FORM protocol, which can generate code for known types, and eliminate run-time GF calls when present, by which I mean that user-defined custom fancy converters may still call GF at run-time, they are welcome. But GF is not the typical case, which gets optimized as tight as can be.
Regards, Jorg Hohle