Which method is more efficient (C programming)
twm47099
Posts: 867
in Propeller 1
I've written a C program (using SimpleIDE) for reading the temperature and pressure from an MS5067 altimeter module (parallax 29124). I based it on the SPI code given in the manufacturer's application note AN520 (available from the parallax product page.)
In that code there are a number of formulas used to calculate the T and P values from the raw data and calibration constants obtained from the sensor.
The calculations are all floating point and application note uses the pow(x,y) function where x = 2 and y is various integers up to 23.
The equations are:
Since these are repeated a number of times to get an average value of T and P (I'm using 10 times in my code, but others have used more), I thought it would be useful to replace the pow function with something faster.
I decided to use the left shift function (1<< 17 for example). It was necessary to make each instance of the shift into a float to get the correct answer. This is my code:
If I was working with integers, I believe the shift method would be faster than pow, but since I have to convert the power of 2 into a float, is it more efficient?
(I also tried using the calculated values of pow(2,y) with a decimal point at the end of each, but I prefer to calculate them in the program (again not sure which is faster, particularly since it is repeated so many times). Would it be better to just declare them as constants (floating point) at the beginning of main(), or even #define them?
Thanks
Tom
In that code there are a number of formulas used to calculate the T and P values from the raw data and calibration constants obtained from the sensor.
The calculations are all floating point and application note uses the pow(x,y) function where x = 2 and y is various integers up to 23.
The equations are:
// calcualte 1st order pressure and temperature (MS5607 1st order algorithm) dT=D2-C[5]*pow(2,8); OFF=C[2]*pow(2,17)+dT*C[4]/pow(2,6); SENS=C[1]*pow(2,16)+dT*C[3]/pow(2,7); T=(2000+(dT*C[6])/pow(2,23))/100; P=(((D1*SENS)/pow(2,21)-OFF)/pow(2,15))/100;
Since these are repeated a number of times to get an average value of T and P (I'm using 10 times in my code, but others have used more), I thought it would be useful to replace the pow function with something faster.
I decided to use the left shift function (1<< 17 for example). It was necessary to make each instance of the shift into a float to get the correct answer. This is my code:
dT = D2 - (C[5] * (float)(1 << 8 )); OFF = C[2] * (float)(1 << 17) + (dT * C[4]) / (float)(1 << 6); SENS = C[1] * (float)(1 << 16) + (dT * C[3]) / (float)(1 << 7); T = (2000. + (dT * C[6]) / (float)(1 << 23)) / 100.; P = ((D1 * SENS / (float)(1 << 21) - OFF) / (float)(1 << 15)) / 100.;
If I was working with integers, I believe the shift method would be faster than pow, but since I have to convert the power of 2 into a float, is it more efficient?
(I also tried using the calculated values of pow(2,y) with a decimal point at the end of each, but I prefer to calculate them in the program (again not sure which is faster, particularly since it is repeated so many times). Would it be better to just declare them as constants (floating point) at the beginning of main(), or even #define them?
Thanks
Tom
Comments
If the arguments were variable, the shift then cast to float version would be significantly faster. Using pow() involves several steps internally, I think converting the inputs to their base-2 log values, multiplying, then converting back using exp. It's expensive. Casting to float, by comparison, is pretty cheap.
Thanks for the information.
Using the ldexp function could I just change the original equations to:
Can floating point variables (32bit on the prop) be used with ldexp or do they have to be doubles (64bit??)
Also when dividing by 2^n (for example in the second equation above) can I just use ldex(c[4],-6) ?
Thanks
Tom
-1 / 2 results in 0
-1 >> 2 results in -1
Now, there is a huge caveat to all of this: while in theory ldexpf(x, n) could be faster than x * (float)(1<<n) (because it has less work to do) in practice it might not be: the floating point multiplication routine is heavily optimized assembly code, whereas ldexpf is implemented as a C function. So if you really care about squeezing every bit of performance out then you probably should time both ways of doing it and compare the results.
In practice it may be more valuable to keep your code easy to read (which I think the pow version does), and leave the optimization to the compiler.