From: Hoehle, Joerg-C. <Joe...@t-...> - 2006-05-19 11:20:38
|
Will wrote: >(ffi:def-c-struct system_variables > (n ffi:int) > (vals (ffi:c-array-ptr ffi:double-float)) > (dot_vals (ffi:c-array-ptr ffi:double-float))) >However, (setf (system_variables-n sys_var) 3) fails to >actually change the value of n. Please try (setf (slot sys_var 'n) 3) The C struct is not changed because (seft system_variables-n) operates solely on the CLOS instance. What happens is that the C-struct is converted once to a Lisp object. From then on you are in the Lisp world. Changes are not reflected to the C world. To affect the C world, you must strictly stick to the concept of foreign places. sys_var denotes a place (ffi:slot sys_var) denotes a place (identity sys_var) denotes a CLOS instance, converted from the foreign struct (system_variables-n sys_var) denotes a Lisp object, similarly. I.e., as soon as you apply a function to a place, conversion to Lisp takes places. Only macros such as FFI:SLOT which are documented to operate on places produce a new place (or modify the C data associated with the place). As you see, it's different from the concept where a CLOS instance mirrors a foreign location in a proxy-like way. With the FFI, there's no mirror, just a one-time snapshot copy. BTW, if you don't care about the CLOS class (I typically don't), there's less overhead in using (def-c-type system_variables (c-struct list [or array] ...)) instead of def-c-struct This will produce a list [or array] of the three slots, instead of a CLOS object. Regards, Jorg Hohle. |
From: Hoehle, Joerg-C. <Joe...@t-...> - 2006-06-02 15:46:34
|
Will wrote: >and also the exceptionally messy array accessor >(setf (element (deref (cast (slot sys_var 'vals)=20 > '(c-ptr (c-array double-float (slot sys_var >'n))))) 0) 10d0) Indeed, it looks weird. Would you like to explain and show more of = what you want to achieve so that maybe people here can help you? >The most direct method of access results in a substantial >performance hit, ... The FFI:slot function is itself >unexpectedly slow. One thing to know is the following: chains of deref + cast + element + = slot are slow but often unnecessary. Don't call them in a loop! Instead, you perform constant folding by hand and hold a place to the = array. Inside the loop, you use a single ffi:ELEMENT. >I then compared the speed of these to indirect access >of the memory through foreign C functions. I found >that (slot sys_var 'n) was substantially slower than >calling a C function that returns or sets the value of >sys_var.n on the other side. It's hard to believe that foreign function calling is faster than a = single usage of ffi:element, ffi:slot etc. The conversion routines are = exactly the same. Don't do (dotimes (i #) (setf (element (deref (slot x 'foo)) i) #)) Use the following pattern instead: (with-c-var (a (deref (slot x 'foo))) ; deref all the constant path (dotimes (i #) (setf (element a i) #)) >My program requires a sizable amount of interaction >between Lisp and C through this data structure, but >copying the entire object back and forth each time >seems too extreme. Do you need this? You have several choices when using the FFI: 1. work with foreign references (pointers) in Lisp, and only convert the fields you need when you need them. This is what you may know from C. If you go this way, I advise you use the #<FOREIGN-OBJECT> literally = and shy away from foreign places (which encapsulate them), except when = accessing slots. 2. work with arrays, and convert in one go, even though you may not = need all elements. 3. work with Lisp classes whose slots are copied *once* from the = foreign world. If you pass these two foreign functions, all slots are copied once = again. >cases this operation will be called a few thousand >times, if not more I'm fully with you. I've been annoyed by people that do not recognize = that there are bottlenecks in applications other than having a bad = local algorithm (e.g. quicksort vs. mergesort). I've seen system slown = down by myriads of calls to tiny "do almost nothing" functions. And = there aren't easy to optimize away. Regards, J=F6rg H=F6hle. |
From: Will <per...@ya...> - 2006-06-02 18:26:19
|
I've re-ordered the message a little bit to address a couple of easier things first. Joerg Hoehle wrote: "It's hard to believe that foreign function calling is faster than a single usage of ffi:element, ffi:slot etc. The conversion routines are exactly the same." I just tested this again. The output from time is listed below. The structure sys_var has an integer field 'n'. Even discounting the garbage collection, a call to slot is still slower. The large discrepancy in the amount of space used may hint at the difference in the nature of the calls. Note also that the Lisp source for the sys_var variable was compiled before this test was run. [8]> (time (dotimes (i 10000) (ffi:slot sys_var 'n))) Real time: 0.052893 sec. Run time: 0.047992 sec. Space: 960776 Bytes GC: 2, GC time: 0.012997 sec. NIL [9]> (time (dotimes (i 10000) (get_sysvar_n))) Real time: 0.018386 sec. Run time: 0.014997 sec. Space: 752 Bytes Just for comparison, consider the following: (defstruct foo x y) (setf f (make-foo)) (compile 'foo-x) (time (dotimes (i 10000) (foo-x f))) Real time: 0.013396 sec. Run time: 0.012998 sec. Space: 752 Bytes Before compiling, the results were: (time (dotimes (i 10000) (foo-x f))) Real time: 0.04467 sec. Run time: 0.037995 sec. Space: 80752 Bytes GC: 1, GC time: 0.004999 sec. Joerg wrote: "Don't do (dotimes (i #) (setf (element (deref (slot x 'foo)) i) #)) Use the following pattern instead: (with-c-var (a (deref (slot x 'foo))) ; deref all the constant path (dotimes (i #) (setf (element a i) #))" I've been playing with this a bit, but can't get it to work correctly. Note that I have to cast from a double-float pointer to a pointer to an array of length n. The obvious (to me) solution, didn't seem to work. My assumption is that cast will return a pointer to an array that I can then dereference to access the elements. (with-c-var (a (deref (cast (slot sys_var 'vals) '(c-ptr (c-array double-float 2))))) (setf (element a 1) 9d0)) Joerg Hoehle wrote: "Would you like to explain and show more of what you want to achieve so that maybe people here can help you?" I might as well. I'll start with the back story. I've been using a robust package for solving ordinary differential equations, called CVODE. The most recent implementation has a Python program output a C file that can be compiled and linked to CVODE. The program runs the new executable and reads the created data file. The weaknesses of this approach are probably apparent. Having a program jump to a shell, compile a program, and then execute it is inconvenient (i.e., slow and convoluted). Why does the program compile an external source file? Two reasons: (1) CVODE needs a compiled function that defines the system of equations and (2) using Python's "eval" would result in even slower execution. I needed to rewrite the Python code anyway, and decided to use Lisp since it will let me create and compile functions on the fly while maintaining C-comparable speed. The memory structures used by CVODE are fairly complex, and writing a wrapper around CVODE appeared simpler. Part of this wrapper is a structure that stores the current (with respect to time in a simulation) values of the variables determined by the set of differential equations, the current derivatives of those variables, and the number of variables. typedef struct { int n; double *vals; double *dot_vals; } system_variables; An simplified example of a Lisp function that uses these values is (defun cvode_model (time) (declare (ignore time)) (set_sysvar_dotval 0 (+ (* 0.25d0 0.75d0 (get_sysvar_val 1) (get_sysvar_val 0)) (* -1.0d0 0.25d0 (get_sysvar_val 0)))) (set_sysvar_dotval 1 (+ (* -1.0d0 0.75d0 (get_sysvar_val 1) (get_sysvar_val 0)) (* 2.0d0 (get_sysvar_val 1)))))) Where set_sysvar_dotval and get_sysvar_val are calls to C functions that access an instantiation of the structure. Note that it's vital for the values recorded in dot_vals and vals and seen by both C and Lisp to be identical. I'll unpack this a bit. C --> calls Lisp to get new values for sysvar.dot_vals Lisp --> reads the numbers stored in sysvar.vals Lisp --> sets the values of sysvar.dot_vals C --> reads the numbers stored in sysvar.dot_vals C --> updates the numbers in sysvar.vals according to sysvar.dot_vals C --> calls Lisp ... This code will be called a few thousand times during a single simulation, and several (hundreds or more) simulations will be performed in the course of this functions life. So, it's somewhat important for this to run quickly. The above implementation is about 14% slower than a pure C solution for the few simulations that I've tried, but other gains make the tradeoff reasonable (note: I'm talking at the level of milliseconds here for a complete simulation). I believe that if I were magically able to access the elements of the structure *directly* in Lisp, by which I mean accessing them as if they were native Lisp objects, I would at least reach equivalence with the pure C solution. If you made it this far, then you should reward yourself with a cup of coffee or maybe a light snack. I hope that the problem and my solution are clear at a general level from the above description, and that some of the specifics about how I need to use the FFI are also apparent. Best, Will |
From: Will <per...@ya...> - 2006-05-19 15:10:12
|
Joerg, Thanks for the response. I sent a follow-up to my original message that hasn't filtered through to the list yet. In short, I tried > Please try (setf (slot sys_var 'n) 3) and also the exceptionally messy array accessor (setf (element (deref (cast (slot sys_var 'vals) '(c-ptr (c-array double-float (slot sys_var 'n))))) 0) 10d0) I then compared the speed of these to indirect access of the memory through foreign C functions. I found that (slot sys_var 'n) was substantially slower than calling a C function that returns or sets the value of sys_var.n on the other side. For that extreme array example, it was no contest whatsoever. > As you see, it's different from the concept where a > CLOS instance mirrors a foreign location in a > proxy-like way. With the FFI, there's no mirror, > just a one-time snapshot copy. My program requires a sizable amount of interaction between Lisp and C through this data structure, but copying the entire object back and forth each time seems too extreme. On the plus side, I ran a test comparing to an original "all C" solution to the mixed Lisp and C version and the the penalty was only an extra 1/6th of the original's time (which amounts to 0.001s). I think that's entirely due to the speed hit for accessing slots through a C function (generally 4 times slower than a direct access by CLISP on a similar Lisp structure). It may seem picky to be concerned about 1/1000th of a second, but in some cases this operation will be called a few thousand times, if not more, while a user waits to see the response. Cheers~ Will |
From: Pascal B. <pj...@in...> - 2006-05-24 15:02:05
|
Will writes: > [...] It may seem picky to be > concerned about 1/1000th of a second, but in some > cases this operation will be called a few thousand > times, if not more, while a user waits to see the > response. Perhaps you should separate more the C from the lisp. Have a smaller interface! Leave the C data being accessed by the C code, and the lisp data being acceed by the lisp code, and don't try to cross the border so often. -- __Pascal Bourguignon__ http://www.informatimago.com/ "Debugging? Klingons do not debug! Our software does not coddle the weak." |
From: Will <per...@ya...> - 2006-06-01 05:14:22
|
I would love to do that, but the nature of the application won't allow any more separation. The C code that I'm calling references a user-specified, compiled function. As far as I'm aware, Lisp is the snappiest language that lets me compile functions without calling an external program (e.g., gcc). Of the free Lisp implementations, Clisp seems to have the best support for call-ins from C. Unfortunately, there seems to be no Lisp alternative for the C code that I need to use. CVODES solves systems of ODEs and performs two types of sensitivity analysis. Nevertheless, I think the real issue here is the FFI's speed in accessing C data. The most direct method of access results in a substantial performance hit, even compared to a C function call. It's not just the number of nested Lisp functions that I have to go through either. The FFI:slot function is itself unexpectedly slow. Best, Will --- Pascal Bourguignon <pj...@in...> wrote: > Will writes: > > [...] It may seem picky to be > > concerned about 1/1000th of a second, but in some > > cases this operation will be called a few thousand > > times, if not more, while a user waits to see the > > response. > > Perhaps you should separate more the C from the > lisp. Have a smaller > interface! Leave the C data being accessed by the C > code, and the > lisp data being acceed by the lisp code, and don't > try to cross the > border so often. > > -- > __Pascal Bourguignon__ > http://www.informatimago.com/ |
From: Sam S. <sd...@gn...> - 2006-09-19 19:44:32
|
Hoehle, Joerg-Cyril wrote: > Will wrote: >> (ffi:def-c-struct system_variables >> (n ffi:int) >> (vals (ffi:c-array-ptr ffi:double-float)) >> (dot_vals (ffi:c-array-ptr ffi:double-float))) >> However, (setf (system_variables-n sys_var) 3) fails to >> actually change the value of n. > > Please try (setf (slot sys_var 'n) 3) > > The C struct is not changed because (seft system_variables-n) operates solely on the CLOS instance. What happens is that the C-struct is converted once to a Lisp object. From then on you are in the Lisp world. Changes are not reflected to the C world. > > To affect the C world, you must strictly stick to the concept of foreign places. > sys_var denotes a place > (ffi:slot sys_var) denotes a place > (identity sys_var) denotes a CLOS instance, converted from the foreign struct > (system_variables-n sys_var) denotes a Lisp object, similarly. clisp docs must be lying then (slot-value is equivalent to system_variables-n): Example 31.2. external C variable and some accesses struct bar { short x, y; char a, b; int z; struct bar * n; }; extern struct bar * my_struct; my_struct->x++; my_struct->a = 5; my_struct = my_struct->n; corresponds to (def-c-struct bar (x short) (y short) (a char) (b char) ; or (b character) if it represents a character, not a number (z int) (n (c-ptr bar))) (def-c-var my_struct (:type (c-ptr bar))) (setq my_struct (let ((s my_struct)) (incf (slot-value s 'x)) s)) or (incf (slot my_struct 'x)) (setq my_struct (let ((s my_struct)) (setf (slot-value s 'a) 5) s)) or (setf (slot my_struct 'a) 5) (setq my_struct (slot-value my_struct 'n)) or (setq my_struct (deref (slot my_struct 'n))) |