I'm using GSL in C for handling complex numbers.
I have to use complex numbers like +-1, 0, +-i a lot of times (across different functions), something like 10^9 I think (even more maybe, don't know yet), so I need a very fast way to call them.
in gsl_complex_math.h they are defined like this:
#define GSL_COMPLEX_ONE (gsl_complex_rect(1.0,0.0))
where
gsl_complex_rect (double x, double y)
{ /* return z = x + i y */
gsl_complex z;
GSL_SET_COMPLEX (&z, x, y);
return z;
}
and
#define GSL_SET_COMPLEX(zp,x,y) do {(zp)->dat[0]=(x); (zp)->dat[1]=(y);} while(0)
That looks like an awful lot of code and temporary variables declaration for my purposes, but I have exactly zero experience in evaluating code efficiency.
What if I declare a global variable in a header like global.h like this:
#if defined MAIN_PROGRAM
#define EXTERN
#else
#define EXTERN extern
#endif
EXTERN const gsl_complex C_U = {.dat[0] = 1., .dat[1] = 0.}
1) Should I expect an increase in performance? 2) Is the code sufficiently clean? Any traps I'm getting into? 3) Is there a better way?
Premature optimisation is a BAD THING TO DO. This minor change is easy enough to make after you have a working implementation which you can benchmark (with compiler optimisations enabled!)
a) Using global constants for common values is (in moderation) a clean way to code. However, if the library itself already provides such constants (or in this case, constant-looking functions), it's generally cleaner to use those (new programmers entering the project will immediately see what's going on, without needing to follow your custom definitions)
b) Potential gotchas of global constants are:
For objects this small, with such a low overhead of construction, there's not really a better way than the two options you're already aware of. For larger objects which take more effort to construct, a factory pattern with caching may become appropriate, but not here.
To elaborate a little on (1):
The code the library is using will be optimised more than you'd think;
do...while
is a common pattern in macro definitions, and will be removed entirely by any decent compiler. It's simply a way to ensure multiple commands will still play nicely when used inside a brace-less if
, among other placesThat means the total cost after optimisation would be memory space on the stack and 2 assignments (and with a good compiler I wouldn't be surprised if the 2 assignments could be optimised to a single assignment containing both values). With lower-levels of optimisation, it may also involve a function call.
So the final comparison looks like this: