Here's something I've been working on lately. tl;dr: gretl has just
become substantially more efficient in respect of basic functions
applied to each element of a big matrix or series. If you'd like the
full story, read on.
By "basic functions" I mean those that take a single floating-point
value as argument and return a floating-point value, such as log,
exp, fabs, all the C-library trigonometric functions, and a bunch of
libgretl functions such as cnorm, dnorm, round, etc.
Suppose you want to apply such functions to a big matrix. You can of
course do that in hansl -- as in "matrix logM = log(M)" for a matrix
M -- but what's going on in the background? (The C library has no
"matrix" type and its log function applies strictly to single
numeric values, with the sole exception of complex numbers if we
consider those pairs as "not single").
We have to do the following (in pseudo-code):
for each element x = M(i,j) of original matrix M
apply function f to x and get result y
set element R(i,j) of result matrix R to y
end
The question is, how do we implement the "apply function f" part?
That is, how is "function f" identified? If it were identified by
name ("log", "sin" and so on) we'd have to look up the function by
name on each iteration of the loop. That's possible under POSIX but
not, in general, on MS Windows (or not without severe contortions),
and it would be horribly inefficient.
Up till now we've identified f using a fixed ID number, associated
with the name of the function via a look-up table. And the inner
"apply" bit included a loop across these IDs. So, for example, if
"log" mapped to ID 287 and "exp" to 288, that loop would include
lines resembling
if (f == 287) return log(x);
if (f == 288) return exp(x);
along with many other clauses of the same sort.
That's much better than look-up by name, but gretl still has to wade
through a bunch of "if (f == ID)" clauses to find what should be
done per element of the matrix (or entry in the series).
Solution: Instead of making "f" a symbolic ID of any sort that has
to be looked up, make it an actual pointer-to-function (the memory
address at which the function can be found). That's what we now do
for such functions.
In my experiments this reduces the time taken for scripts that are
heavy on calls to "basic functions", as defined above, by roughly 25
to 45 percent. Artificial example:
<hansl>
matrix m = muniform(50, 50)
set stopwatch
loop 5000 -q
res = log(m)
res = exp(m)
res = sqrt(m)
endloop
printf "elapsed: %gs\n", $stopwatch
</hansl>
In current git you can turn off the use of function pointers, to
compare with old-style behavior, by commenting out line 1340 of
lib/src/gensyntax.c (with comment "attach function pointer, if
available").
Allin