Hi,
James Bielman wrote:
>Thu Feb 2 18:41:28 CST 2006 James Bielman <jamesjb(a)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