Fast array access using Racket FFI

无人久伴 提交于 2020-02-03 04:58:12

问题


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?


回答1:


I tried the approach suggested by Eli 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.




回答2:


It would probably be better to set the whole thing using a bytestring (via _bytes), but that's a very rough guess. It would be much better to ask this question on the mailing list...



来源:https://stackoverflow.com/questions/10491047/fast-array-access-using-racket-ffi

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!