[GSLL-devel] [Gsll-devel] Efficient access to externally generated double-float arrays?
Liam Healy
lhealy at common-lisp.net
Mon Nov 8 03:59:16 UTC 2010
OK, this turned out to be a lot harder than I thought. I have done
three things:
1) I have defined gref* and (setf gref*) methods specific to each of
the foreign-array types that call cffi:mem-aref. This gives about 20x
speedup because I am now passing the literal type to cffi:mem-aref, so
it can work that fact in at compile time.
2) There is now a compiler macro to turn a grid:gref into a grid:gref*
if there's only one index. This gives about 2x speed up when gref is
used.
3) There is another compiler macro that turns a grid:gref* into a
cffi:mem-aref directly if the foreign array is declared. This gives
about a 400x speedup overall, similar to your "hardwired" result. It
is a bit slower because I'm not able to precompute the pointer, it has
to be recomputed each time on the gref* call.
On the last point, there is a caveat. I tried to make it work when
the foreign array has been declared with a standard (declare ...)
form. This has a chance of working on SBCL because of its support for
the CLtL2 function variable-information, which was removed from CL
before it was sent to ANSI standardization. However, it did not work
for me; I will continue to try to get this working. In the meantime,
the only way to do a declaration to take advantage of 3 is with a 'the
form, e.g. (grid:gref (the vector-double-float zvector) i). This is
kind of annoying, but it is portable, and allows you to avoid going to
lower level functions (i.e., cffi:mem-aref).
So for example see my rewrite of your function (in
foreign-array/tests/fast-array-access.lisp)
(defun gref-access (dim)
"Given an integer dim, this constructs a function that, when supplied with a
N-dimensional vector Z and some output vector (-> pointer?), yields the
corresponding forces"
(let ((temp-values (make-array 2 :element-type 'double-float
:initial-element 0.0d0)))
(lambda (zvector output)
(declare (fixnum dim)
(optimize (speed 3) (safety 0) (debug 0))
(type vector-double-float zvector)) ;;; <--- this is useless,
but ought not to be!
(do ((i 0 (1+ i))) ((= i dim)) (declare (fixnum i))
(setf (aref temp-values 0) 0.0d0)
(do ((m 0 (1+ m))) ((> m i)) (declare (fixnum m))
(do ((n i (1+ n))) ((= n dim)) (declare (fixnum n))
(setf (aref temp-values 1) 0.0d0)
(do ((k m (1+ k))) ((> k n)) (declare (fixnum k))
(incf (aref temp-values 1) (grid:gref (the vector-double-float
zvector) k))) ; This declaration does the work!
(incf (aref temp-values 0) (expt (aref temp-values 1) -2))))
(setf (grid:gref output i)
(- (grid:gref (the vector-double-float zvector) i) ; This one does too!
(aref temp-values 0)))))))
is now (almost) as fast as your cffi-access.
There is still a bunch of stuff to be done --- the optimizations only
work for vectors, not higher dimensional arrays, and I haven't defined
a compiler macro for setf yet on 3). But it's a start; try it in your
problem and let me know how it performs.
Liam
More information about the gsll-devel
mailing list