| | 1 | = Using Clozure CL's FFI = |
| | 2 | |
| | 3 | == BLAS and LAPACK libraries == |
| | 4 | |
| | 5 | The BLAS and LAPACK libraries were originally written in Fortran. |
| | 6 | It's certainly possible to call the Fortran-style routines directly |
| | 7 | from CCL, but some care is needed, because Fortran conventions differ |
| | 8 | from C conventions. (CCL's FFI is geared towards calling C |
| | 9 | functions.) |
| | 10 | |
| | 11 | In Fortran, all arguments to a function, even scalar values, are passed |
| | 12 | by reference (that is, as pointers). Arrays are also different. In |
| | 13 | Common Lisp, arrays are indexed from 0 and are stored in row-major |
| | 14 | order. In Fortran, arrays are indexed from 1, and are stored in |
| | 15 | column-major order. |
| | 16 | |
| | 17 | To show how this works in practice, we'll call the BLAS routine |
| | 18 | idamax, which finds the index of the element of a double-float vector |
| | 19 | with the maximum absolute value. |
| | 20 | |
| | 21 | {{{ |
| | 22 | Welcome to Clozure Common Lisp Version 1.7-dev-r14780 (LinuxARM32)! |
| | 23 | ? (open-shared-library "libblas.so") |
| | 24 | #<SHLIB libblas.so.3gf #x5454AF36> |
| | 25 | ? (rletz ((n :int 10) |
| | 26 | (dx (:array :double 10)) |
| | 27 | (incx :int 1)) |
| | 28 | (setf (paref dx (:array :double) 5) 100d0 |
| | 29 | (paref dx (:array :double) 8) -1000d0) |
| | 30 | (external-call "idamax_" (:* int) n (:* double) dx (:* int) incx :int)) |
| | 31 | 9 |
| | 32 | ? |
| | 33 | }}} |
| | 34 | |
| | 35 | Notice that the function parameters are passed by reference (that is, |
| | 36 | as pointers). Second, note that the returned answer is 9 and not 8 as |
| | 37 | we might expect. This is because Fortran array indexes start at 1, |
| | 38 | not at 0 as they do in Common Lisp. Finally, note the "_" suffix on |
| | 39 | the name of the Fortran function---that's simply a common Unix |
| | 40 | convention. |
| | 41 | |
| | 42 | There is also a C interface to the BLAS. This is somewhat more |
| | 43 | convenient to use, as the following example shows: |
| | 44 | {{{ |
| | 45 | ? (rletz ((x (:array :double 10))) |
| | 46 | (setf (paref x (:array :double) 5) 100d0 |
| | 47 | (paref x (:array :double) 8) -1000d0) |
| | 48 | (external-call "cblas_idamax" :int 10 (:* double) x :int 1 :int)) |
| | 49 | 8 |
| | 50 | }}} |
| | 51 | With the C interface, we don't have to pass scalar parameters by reference, |
| | 52 | and the indexes are 0-based, as a Lisp programmer would expect. (The |
| | 53 | libblas.so that we loaded earlier also contains the C interface functions |
| | 54 | on this system.) |
| | 55 | |
| | 56 | We can make this more convenient still by using the interface |
| | 57 | translator to parse the cblas.h header file and generate an interface |
| | 58 | database for it. |
| | 59 | |
| | 60 | The directions at http://trac.clozure.com/ccl/wiki/CustomFramework |
| | 61 | explain how to do this. With the interface database present, calling |
| | 62 | the BLAS function becomes even easier: |
| | 63 | {{{ |
| | 64 | ? (use-interface-dir :cblas) |
| | 65 | #<INTERFACE-DIR :CBLAS #P"cblas/" #x54547E8E> |
| | 66 | ? (rletz ((x (:array :double 10))) |
| | 67 | (setf (paref x (:array :double) 5) 100d0 |
| | 68 | (paref x (:array :double) 8) -1000d0) |
| | 69 | (#_cblas_idamax 10 x 1)) |
| | 70 | 8 |
| | 71 | }}} |
| | 72 | Now we can use the handy #_ reader macro, and we no longer have to |
| | 73 | provide the return type of the function, or the types of its |
| | 74 | arguments. The #_ reader macro looks them up in the interface |
| | 75 | database and generates the correct external-call form automatically. |
| | 76 | |
| | 77 | == Lisp Arrays and Foreign Arrays == |
| | 78 | |
| | 79 | You may be wondering if there is some way to pass lisp arrays to |
| | 80 | foreign functions. In CCL, this is generally not possible. Even if |
| | 81 | it were possible to obtain a pointer to the array data, and assuming |
| | 82 | that the lisp array data is stored in the format that the foreign |
| | 83 | function expects, the GC might run at any time and move the underlying |
| | 84 | object. (Keep in mind that CCL uses multiple threads, and any thread |
| | 85 | might initiate a GC at arbitrary times.) |
| | 86 | |
| | 87 | Data in a lisp array must therefore be copied to memory either on the |
| | 88 | stack or on the foreign heap before being passed to a foreign |
| | 89 | function. |
| | 90 | |
| | 91 | There are, however, a few features in CCL that may be useful for |
| | 92 | avoiding the need to copy. |
| | 93 | |
| | 94 | The first is an object called a heap ivector. |
| | 95 | http://ccl.clozure.com/ccl-documentation.html#f_make-heap-ivector |
| | 96 | |
| | 97 | Here's an example. (Presumably you would keep the heap ivector object |
| | 98 | around somewhere, and free it when you were completely done with it.) |
| | 99 | {{{ |
| | 100 | (multiple-value-bind (vector ptr) |
| | 101 | (ccl:make-heap-ivector 10 'double-float) |
| | 102 | (setf (aref vector 5) 100d0 |
| | 103 | (aref vector 8) -1000d0) |
| | 104 | (prog1 |
| | 105 | (#_cblas_idamax 10 ptr 1) |
| | 106 | (ccl:dispose-heap-ivector vector))) |
| | 107 | }}} |
| | 108 | |
| | 109 | The second is a macro called ccl:with-pointer-to-ivector. See the |
| | 110 | docstring. This must be used with some care, because it inhibits |
| | 111 | the GC within its body. |
| | 112 | {{{ |
| | 113 | (let ((vector (make-array 10 :element-type 'double-float))) |
| | 114 | (setf (aref vector 5) 100d0 |
| | 115 | (aref vector 8) -1000d0) |
| | 116 | (ccl:with-pointer-to-ivector (ptr vector) |
| | 117 | (#_cblas_idamax 10 ptr 1))) |
| | 118 | }}} |
| | 119 | |
| | 120 | The nice thing about the above two examples is that you can still |
| | 121 | access the vector in the normal way. |
| | 122 | |
| | 123 | In some earlier examples, we used ccl:rlet to allocate a vector on the |
| | 124 | stack. We can also allocate memory on the foreign heap either with |
| | 125 | #_malloc or with the slightly higher-level ccl:make-record. In either |
| | 126 | case, you must explicitly call ccl:free. |
| | 127 | {{{ |
| | 128 | (let ((x (ccl:make-record (:array :double 10)))) |
| | 129 | (setf (ccl:paref x (:array :double) 5) 100d0 |
| | 130 | (ccl:paref x (:array :double) 8) -1000d0) |
| | 131 | (prog1 |
| | 132 | (#_cblas_idamax 10 x 1) |
| | 133 | (ccl:free x))) |
| | 134 | }}} |
| | 135 | |
| | 136 | It's also possible to use ccl:make-gcable-record to allocate foreign |
| | 137 | memory that will be freed when it is unreachable from lisp. |
| | 138 | {{{ |
| | 139 | (let ((x (ccl:make-gcable-record (:array :double 10)))) |
| | 140 | (setf (paref x (:array :double) 5) 100d0 |
| | 141 | (paref x (:array :double) 8) -1000d0) |
| | 142 | (#_cblas_idamax 10 x 1)) |
| | 143 | }}} |
| | 144 | |
| | 145 | This works well as long as the gcable pointer is the only thing |
| | 146 | (either in the lisp world or foreign world) that refers to the memory. |
| | 147 | See the manual for a more detailed explanation: |
| | 148 | http://ccl.clozure.com/ccl-documentation.html#f_make-gcable-record |
| | 149 | |