[Openmcl-devel] Re: back to coding and optimizing

Gary Byers gb at clozure.com
Fri Feb 25 20:08:55 PST 2005



On Fri, 25 Feb 2005, Hamilton Link wrote:

> Hey Gary (or alternatively anyone else who wants to chew on this), I
> have some software that's way numerically intensive, and I was thinking
> about writing a C function or several to take consing and tag-bit
> manipulation within a set of operations down to zip, or close.
>
> What this would entail, I suspect, is storing a lot of data as raw
> memory blocks (real-valued multidimensional arrays, actually), passing
> them around and doing memory management with them with lisp or by hand
> and doing some C operations on them behind the scenes. Most of the
> operations are going to be block+block->block or block->block, and only
> very rarely will I need to look at the actual numbers in the blocks
> from lisp.
>
> So... question: how close can I come to just allocating uvectors and
> passing them into C functions? And actually for that matter how close
> to that do big honkin' vectors with the type of their contents declared
> as short float get me? I assume at least that the latter stores stuff
> without tags and takes care of tagging/untagging through svref -- but
> if I got my hands on the raw vector underneath then C would not have
> tags to worry about.
>
> The other question is, what's the best way to make the GCer play nice
> with C? Stack-allocate copies of all my uvectors? I would guess that
> copying these things about would be chunky but it might not be toooo
> bad... the alternative would be to malloc them, and have lisp only deal
> with their macptrs, right? In which case I have to free them by hand,
> which might also not be toooo bad and would I presume be somewhat
> faster.
>
> I know conversations along these lines have floated around the list
> before, so I'll go check the archive too.
>
> h
>

One of the things that's probably mentioned in the archives is a form
that would disable the GC and bind a MACPTR to the (now-constant) address
of the first byte of data in an object of type CCL::IVECTOR (an IVECTOR
is basically a vector of immediate element-type.)  I finally added
CCL:WITH-POINTER-TO-IVECTOR to bleeding-edge CVS a few minutes ago, and
it seems to work:

? (defun _write-string (s &optional (fd 2))
  "Use the #_write system call to write the bytes in a lisp string
  to a file descriptor"
  (with-pointer-to-ivector (p s)
    (#_write fd p (length s))
    (values)))
_WRITE-STRING
? (_write-string "Hello
")
Hello
?

WITH-POINTER-TO-IVECTOR disables the GC during its execution, so it may
not be appropriate in some cases (if the body or other threads do lots
of consing.)  Once the body exits, the MACPTR becomes invalid (and the
results of hanging onto that pointer from foreign code are undefined,
but probably unpleasant.)

Another approach (which has been somewhat broken; it didn't handle
DOUBLE-FLOAT vectors until recently, and has had other problems) is
to allocate IVECTORs in foreign memory; the GC would completely
ignore them (but you can use AREF, etc. on them just like "real"
ivectors) :

(multiple-value-bind (vector pointer) (CCL:MAKE-HEAP-IVECTOR 1 'double-float)
  (setf (aref vector 0) pi)
  (prog1 (%get-double-float pointer)
    (CCL:DISPOSE-HEAP-IVECTOR vector)))

In this example, I just made a 1-element vector of element-type DOUBLE-FLOAT,
and I deallocated ("disposed of") the vector soon after it was created.
It's probably more appropriate to use MAKE-HEAP-IVECTOR for long-lived
things (whose lifetime is well-defined) and WITH-POINTER-TO-IVECTOR for
things that just need to occasionally/incidentally be passed to foreign
code.

I don't know if either of these things is appropriate for your application;
you -might- find that it's easier to just malloc a pointer and use
%GET-DOUBLE-FLOAT/(SETF %GET-DOUBLE-FLOAT) to access/set its contents.

Note that - in both of these approaches - you're basically able to get
a foreign pointer to a lisp vector.  A lisp multidimensional array is
a very different kind of object from a C multidimensional array (and
I get a headache every time I try to remember whether C arrays are
row- or column-major.)  If V is the ivector that you want to pass to
C code and (LENGTH V) = 120 (for instance), you can certainly do:

(let* ((a (make-array (list 10 12)
                      :element-type 'double-float
                      :displaced-to v)))
  ;; Use A to access V's contents here
  ...)

On the C side, I don't remember whether

double a [10] [12];

or

double a [12] [10];

is closer to being equivalent.





More information about the Openmcl-devel mailing list