[cl-gd-devel] performance issue with CL-GD on SBCL, UFFI vs. CFFI

Anon Alex alexfs04 at gmail.com
Thu May 21 14:46:45 UTC 2009


Hello, I wanted to report a performance-related issue with CL-GD and
UFFI vs CFFI-UFFI-COMPAT

Here is a simple function for comparing two images:
As you can see, this loops over every pixel of both images, compares
the R,G,B components.

(defun compare-images (image1 image2)
  "Comapre two images -- uses API exported by CL-GD"
  (declare (optimize (debug 0) (speed 3)(safety 0)))
  ;(declare (optimize (debug 3) (speed 0)))
  (let ((err 0)
	(height (cl-gd:image-height image1))
	(width (cl-gd:image-width image1)))
    (declare (fixnum err height width))
    (format t "height1=~a, height2=~a" height width)
    (assert (and (= height (cl-gd:image-height image2))
		 (= width (cl-gd:image-width image2))))
    (loop for y fixnum from 0 below height do
	  (loop for x fixnum from 0 below width do
		(let ((img-color (cl-gd:get-pixel y x :image image2))
		      (target-color (cl-gd:get-pixel y x :image image1)))
		  (incf err (square (- (the fixnum(cl-gd:color-component :red
img-color :image image2))
				       (the fixnum(cl-gd:color-component :red target-color :image
image1)))))
		  (incf err (square (- (the fixnum (cl-gd:color-component :blue
img-color :image image2))
				       (the fixnum(cl-gd:color-component :blue target-color :image
image1)))))
		  (incf err (square (- (the fixnum(cl-gd:color-component :green
img-color :image image2))
				       (the fixnum(cl-gd:color-component :green target-color
:image image1))))))))
    err))


Here's another version, which uses non-exported functions to speed it
up a little bit (and for benchmarking, eliminate some of the the
API-level overhead)

(defun compare-images-raw (image1 image2)
  "Compare two images -- use internal functions to eliminate overhead
added by CL-GD API"
  (declare (optimize (debug 0) (speed 3)(safety 0)))
  ;(declare (optimize (debug 3) (speed 0)))
  (let ((err 0)
	(height (cl-gd:image-height image1))
	(width (cl-gd:image-width image1))
	(img1 (cl-gd::img image1))
	(img2 (cl-gd::img image2)))
    (declare (fixnum err height width))
    (format t "height1=~a, height2=~a" height width)
    (assert (and (= height (cl-gd:image-height image2))
		 (= width (cl-gd:image-width image2))))
    (loop for y fixnum from 0 below height do
	  (loop for x fixnum from 0 below width do
		(let ((img2-color (cl-gd:get-pixel y x :image image2))
		      (img1-color (cl-gd:get-pixel y x :image image1)))
		  (incf err (square (- (the fixnum(cl-gd::gd-image-get-red img2 img2-color))
				       (the fixnum(cl-gd::gd-image-get-red img1 img1-color)))))
		  (incf err (square (- (the fixnum(cl-gd::gd-image-get-green img2 img2-color))
				       (the fixnum(cl-gd::gd-image-get-green img1 img1-color)))))
		  (incf err (square (- (the fixnum(cl-gd::gd-image-get-blue img2 img2-color))
				       (the fixnum(cl-gd::gd-image-get-blue img1 img1-color))))))))
    err))



This is the result of running the above pair of functions on a 1024x768 image.

----
First, the timing with the standard CL-GD:
(SBCL 1.0.28, CL-GD 0.5.6, UFFI-1.6.1, 3GHz Intel Core2 Duo, Linux))

CL-GD uses UFFI by default on SBCL


EVO-LISA> (progn
  (defparameter *image1* (cl-gd:create-image-from-file "023.JPG"))
  (defparameter *image2* (cl-gd:create-image-from-file "023.JPG"))
  (time (compare-images *image1* *image2*)))

height1=1024, height2=768
Evaluation took:
  1.684 seconds of real time
  1.688105 seconds of total run time (1.688105 user, 0.000000 system)
  100.24% CPU
  5,061,067,326 processor cycles
  141,296 bytes consed

0


EVO-LISA> (progn
  (defparameter *image1* (cl-gd:create-image-from-file "023.JPG"))
  (defparameter *image2* (cl-gd:create-image-from-file "023.JPG"))
  (time (compare-images-raw *image1* *image2*)))

height1=1024, height2=768
Evaluation took:
  1.476 seconds of real time
  1.424088 seconds of total run time (1.420088 user, 0.004000 system)
  96.48% CPU
  4,437,702,108 processor cycles
  106,112 bytes consed

0

-----

After changing cl-gd.asd so that CFFI-UFFI-COMPAT is used for SBCL
instead of UFFI:

;;;;;;;;

EVO-LISA> (progn
  (defparameter *image1* (cl-gd:create-image-from-file "023.JPG"))
  (defparameter *image2* (cl-gd:create-image-from-file "023.JPG"))
  (time (compare-images *image1* *image2*)))

height1=1024, height2=768
Evaluation took:
  0.336 seconds of real time
  0.336021 seconds of total run time (0.336021 user, 0.000000 system)
  100.00% CPU
  1,010,111,976 processor cycles
  21,712 bytes consed

0
EVO-LISA> (progn
  (defparameter *image1* (cl-gd:create-image-from-file "023.JPG"))
  (defparameter *image2* (cl-gd:create-image-from-file "023.JPG"))
  (time (compare-images-raw *image1* *image2*)))

height1=1024, height2=768
Evaluation took:
  0.282 seconds of real time
  0.284018 seconds of total run time (0.284018 user, 0.000000 system)
  100.71% CPU
  845,516,097 processor cycles
  21,936 bytes consed

0

------------

Surprisingly,  there is a 5x difference between using CFFI and UFFI.

Pixel-level access to an image, as provided by
cl-gd:get-pixel, and cl-gd:color-component, is usually used in image
processing, where cl-gd:get-pixel, and cl-gd:color-component will be
called for many pixels on the image (usually the whole image), so the
speed of these accessors is critical for image processing, and
for some reason, CFFI (viat cffi-uffi-compat) offers 5x faster pixel
level access than UFFI.

I don't know much about either CFFI or UFFI, but
CFFI-UFFI-COMPAT is the default already for CLISP and OpenMCL.
Maybe it's worth considering making CFFI-UFFI-COMPAT the default for
SBCL as well?


Regards,
Alex Fukunaga




More information about the Cl-gd-devel mailing list