In many situations the fact that C is weakly typed is an advantage to the programmer but any implicit conversions allowed in a piece of code take a certain amount of time and in some cases are not needed at all if the programmer is careful. Most unnecessary conversions occur in assignments, arithmetic expressions and parameter passing.
Consider the following code segment which simply computes the sum of a user input list of integers and their average value.
double average, sum = 0.0 ; short value, i ; ... for ( i=0; i < 1000; i ++ ) { scanf( “%d”, &value ) ; sum = sum + value ; } average = sum / 1000 ;
1. The conversion from value, of type short int, to the same type as sum, type double, occurs 1000 times in the for loop so the inherent inefficiency in that one line is repeated 1000 times which makes it substantial.
If we redefine the variable sum to be of type short we will eliminate these conversions completely. However as the range of values possible for a short are quite small we may encounter overflow problems so we might define sum to be of type long instead.
The conversion from short to long will now be implicit in the statement but it is more efficient to convert from short to long than it is from short to double.
2. Because of our modifications above the statement average = sum / 1000 ;now involves integer division which is not what we require here. ( Note however that an implicit conversion of 1000 from int to long occurs here which may be simply avoided as follows :- average = sum / 1000L ;with no time penalty whatsoever as it is carried out at compile time.) To remedy the situation we simply do the following :- average = sum / 1000.0;
3. The statement sum = sum + value ; also involves another source of inefficiency. The variable sum is loaded twice in the statement unnecessarily. If the shorthand operator += were used instead we will eliminate this. sum += value;
In general the lowest level arithmetic is more efficient especially in multiplication and division which are inherently expensive operations.
For Example :-
double d ; int i ; d = i * 2.0 ;
This operation requires that the variable i is converted to double and the multiplication used is then floating point multiplication.
If we instead write the statement as
d = i * 2 ;
we will have integer multiplication and the result is converted to double before being assigned to d. This is much more efficient than the previous and will give the same result ( as long as the multiplication does not overflow ).
Again very little will be saved in a single such operation but when one of many the saving may amount to something for example the expression
2.0 * j * k * l * m
where j, k, l and m are integers might involve four floating point multiplications rather than four integer multiplications when coded with efficiency in mind.
The use of the ‘to the power of ‘ function, pow() in C, is another common example of unnecessary arithmetic.
Computing the value of num2 or num3 for example should never be done in a program using the pow function especially if num is an integer. This is because there is an overhead in actually calling the pow function and returning a value from it and there is an overhead if a type conversion has to be made in passing the parameters to pow() or assigning the return value from the function. Instead straightforward multiplication should be used i.e.
num * num rather than pow( num, 2 ) ;
When large powers are involved it does make sense to use the pow function but again the situation should be evaluated on its own merit.
For example if we want to print a table of numn where n = 1 ... 99. If we do the following
double num ; int k ; for ( k = 1; k <100; k++ ) printf(“%lf to %d = %lf\n”, num, k, pow( num, k ));
we will end up with approximately n = 4950 multiplications plus 99 function calls. Whereas if we had used
double sum = num ; for ( k = 2; k <= 100; k++ ) { printf( "%lf to %d = %lf\n", num, k, sum ) ; sum *= num ; }
we will require just ( n -1 ) i.e. 98 multiplications in total.
C’s Bit-wise operators may also be used to improve efficiency in certain situations.
Computing the value of 2n can be done most efficiently using the left shift operator i.e.
1 << n
Determining whether a value is odd or even could be done using
if ( num % 2 ) printf( “odd” ) ; else printf( “even” ) ;
but it is more efficient to use
if ( num & 1 ) printf( “odd” ) ; else printf( “even” ) ;
All Rights Reserved. © 2024 BookOfNetwork