[racket] Fast array access using Racket FFI

From: Eli Barzilay (eli at barzilay.org)
Date: Tue May 8 21:10:23 EDT 2012

The thing that I couldn't write properly in that format is that I
don't remember whether there was some way to get a Racket bytestring
object that wraps a block of external memory, which means that you can
use Racket functions to manipulate it.  Seems that you found it now...


A few minutes ago, Petr Samarin wrote:
> I tried the approach suggested by Eli on Stackoverflow, and it worked out! The
> idea is to use a bytestring. Since in this case the size of the array is
> known, (make-sized-byte-string cptr length) can be used:
> 
> (define data (make-sized-byte-string (IplImage-imageData img)
>                                      (* width height channels)))
> 
> This results in run times close to Racket's native vectors:
> 
> (time (let loop ([i (- (* 640 480 3) 1)])
>     (when (>= i 0)
>       ;; invert each pixel channel-wise
>       (bytes-set! data i (- 255 (bytes-ref data i)))
>       (loop (- i 1)))))
> -> cpu time: 18 real time: 18 gc time: 0
> 
> Thank you, Eli.
> 
> On May 8, 2012, at 3:34 PM, Petr Samarin wrote:
> 
>     Hello everyone,
>    
>     I am trying to write OpenCV FFI in Racket and arrived at a point where
>     arrays need to be manipulated efficiently. However, all my attempts to
>     access arrays by using Racket FFI resulted in very inefficient code. Is
>     there a way for fast access of C arrays using FFI?
>    
>     In Racket, this type of manipulation is reasonably fast, i.e.:
>    
>        (define a-vector (make-vector (* 640 480 3)))
>        (time (let loop ([i (- (* 640 480 3) 1)])
>            (when (>= i 0)
>              ;; invert each pixel channel-wise
>              (vector-set! a-vector i (- 255 (vector-ref a-vector i)))
>              (loop (- i 1)))))
>        ->  cpu time: 14 real time: 14 gc time: 0
>    
>     Now, in OpenCV, there is a struct called `IplImage` that looks like this:
>    
>        typedef struct _IplImage
>        {
>            int  imageSize;             /* sizeof(IplImage) */
>            ...
>            char *imageData;        /* Pointer to aligned image data.*/
>        }IplImage;
>     The struct is defined in Racket as follows:
>    
>        (define-cstruct _IplImage
>            ([imageSize _int]
>             ...
>             [imageData _pointer]))
>    
>     Now we load an image using `cvLoadImage` function as follows:
>    
>        (define img
>          (ptr-ref
>           (cvLoadImage "images/test-image.png" CV_LOAD_IMAGE_COLOR)
>           _IplImage))
>    
>     The pointer `imageData` can be accessed by: `(define data
>     (IplImage-imageData img)))`
>    
>     Now, we want to manipulate `data`, and the most efficient way I could come
>     up with was by using pointers:
>    
>        (time (let loop ([i (- (* width height channels) 1)]) ;; same 640 480 3
>            (when (>= i 0)
>              ;; invert each pixel channel-wise
>              (ptr-set! data _ubyte i (- 255 (ptr-ref data _ubyte i)))
>              (loop (- i 1)))))
>        -> cpu time: 114 real time: 113 gc time: 0
>    
>     This is very slow, compared to the speed of native Racket vectors.
>     I also tried other ways, such as `_array`, `_cvector` that don't even come
>     close to the speed of using pointers, except for writing a first-class
>     function in C that gets a function for running over the whole array. This
>     C function is compiled to a library and bound in Racket using FFI. Then,
>     Racket procedures can be passed to it and applied to all elements of the
>     array. The speed was the same as with pointers, but still not sufficient
>     to continue porting OpenCV library to Racket.
>    
>     Is there a better way to do this?
>    
>     Regards,
>     Petr
> 
> ____________________
>   Racket Users list:
>   http://lists.racket-lang.org/users

-- 
          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                    http://barzilay.org/                   Maze is Life!

Posted on the users mailing list.