Hi Liam,
I recently realised that I completely forgot to submit the forms that I used to check where the problem lies with the slow FFT tests. So my apologies, and hereby some very simple forms (should be run in package :gsll). I quickly re-ran them to verify that the timings are still the same (with updated gsll and now using antik).
;;;; Check 1: Perform FFTs on 1000 different vectors, each with 1000 ;;;; random complex elements (consisting of a real and a complex ;;;; random element). Takes ~2 seconds on my old laptop. (time (progn (loop for i from 0 below 1000 do (forward-fourier-transform (make-urand-vector '(complex double-float) 1000))) nil))
;;;; Check 2: Same as 1, but without the FFTs. Takes ~1.8 seconds. (time (progn (loop for i from 0 below 1000 do (make-urand-vector '(complex double-float) 1000)) nil))
Here, it's clear that the time increases by an order of magnitude by just generating random vectors. 1000 FFTs take less time:
;;;; Check 3: Generate only 1 random vector, FFT 1000 times. Takes ~ ;;;; 0.14 seconds. (time (let ((rand (make-urand-vector '(complex double-float) 1000))) (loop for i from 0 below 1000 do (forward-fourier-transform rand)) nil))
So let's see where the problems lie in making the random vector:
;;;; Check 4: Same as 2, but without making the vector. Takes ~1 second. (time (progn (loop for i from 0 below 1000 do (loop for j from 0 below 1000 do (coerce (complex (urand) (urand)) '(complex double-float)))) nil))
;;;; Check 5: Same as 4, but without coercing. Takes ~0.6 seconds. (time (progn (loop for i from 0 below 1000 do (loop for j from 0 below 1000 do (complex (urand) (urand)))) nil))
You can see that half the time of Check 1 is just spent generating and coercing elements. Then ~40% is making and setting the vectors (non-optimized) and ~10% is the FFTs themselves.
I tried making an optimized version of make-urand-vector using declarations, which only sped up things by a little bit (certainly not an order of magnitude); we discussed this before on #lisp. However, since the transition to antik, I didn't manage to get this working in a reasonable amount of time. Regardless, the fact that it still contained the form:
(coerce (complex (urand) (urand)) '(complex double-float))
is the reason for the fact that even the optimized version is slow. Since the original GSL tests use random vectors a lot, this is a real problem. The second thing is that I suspect that setf-ing array elements is slower than in C as well. The GSL tests also use this a lot, since they initialize the vectors with known numbers, to verify that those elements were untouched when using the stride keyword.
It could be that optimized functions, using declarations and gsetf, will cause those parts of the tests to go faster. I will try that when I have some more time, and when I get it working.
By the way, here's the defun for the attempt at an optimized version of make-urand-vector for complex double-floats. I tried many different things (also without the let around) but couldn't get it to run: I always get a type-error.
(defun make-urand-complex-double-float-vector (dimension &key (stride 1) init-offset) "Make a complex double-float vector with random elements." (let ((vec (make-and-init-vector '(complex double-float) (* stride dimension) :init-offset init-offset))) (declare (type grid:vector-complex-double-float vec)) (loop for i from 0 below (* stride dimension) by stride do (let ((value (coerce (complex (urand) (urand)) '(complex double-float)))) (declare (type (complex double-float) value)) (grid:gsetf (grid:aref* vec i) value))) vec))
Regards, Sumant