[racket] Fast array access using Racket FFI

From: Petr Samarin (petrsamarin at gmail.com)
Date: Tue May 8 21:04:49 EDT 2012

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

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20120509/5696a0e8/attachment.html>

Posted on the users mailing list.