"Hoehle, Joerg-Cyril" Joerg-Cyril.Hoehle@t-systems.com writes:
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)
Today's patch allows you to optimize the case where you define a typedef that you can guarantee does not need translation (as in this example). So you can do:
(defctype my-int :int :translate-p nil)
And you should get the same disassembly as before. Any TRANSLATE-* methods defined on MY-INT will simply be ignored.
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
This doesn't need to be documented---it builds a list of the optional second return values from each (possibly nested) type translator for use internally so that it can recurse down the list when freeing the object, passing the correct parameters to each free method. If user-defined translators don't return a second value, it will just pass nil to the free method.
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).
Well, the idea here was to get it right first, then get it fast. The :TRANSLATE-P argument is a start at optimizing types that won't need translation.
Ideally, it would be possible to tell at compile-time whether a given type would need translation based on the set of applicable methods on TRANSLATE-TO-FOREIGN, but since this method can also be specialized on the Lisp value to convert, all the specializers are not known.
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.
I've thought a little bit about this, and I think an interface based loosely on Common Lisp compiler macros (but always expanded) might be a good fit here---define the translator using the method when there is no way around doing the conversion at runtime, but override using the "compiler macro" (which can punt when necessary and fall back to the runtime method) when performing conversions on known types.
Obviously, what you lose with such an interface is the ability to cleanly translate different Lisp types in different ways---for example passing pointers as :STRING values without translation. This is the primary reason the new interface is defined using GFs.
James