Вы находитесь на странице: 1из 10

6.1.

Introduction to optimization

Meaning and scope of optimization

In olden days when computers had less memory, developers would some times chose a slower
algorithm in order to ensure that the program would consume less memory. Later when
computers with higher memory became available, developers tried to minimize the amount of
time that a program consumed to perform some task by utilising the additional memory
available. Optimization generally focussed on improving just one or two aspects of performance:
execution time, memory usage, disk space, bandwidth, power consumption or some other
resource.

But in modern days, the goal of optimization is to make programs faster and at the same time, to
ensure that program occupies lesser memory in the computer's memory.

Need for program optimization

Just like software defects, the cost of optimizing a program increases exponentially with every
phase of SDLC, after the development phase, and even more, after the application is deployed
for use in the world.

Code optimization - best approach

The main focus and scope of the best approach tells you the various optimization techniques that
can be applied at the source code level.

High level optimization should be completed before low level optimization.

Choice of the best possible algorithm within the program, within functions, within the class,
should be done first, before applying other optimization principles.

A developer/designer should think about improving algorithms first before doing some code
optimization.

Double check if you use the correct collection types. Then have a look if you accidentally created
more objects than needed (object creation in loops is a typical reason for performance problems).

High level optimization is to be done in the source code, and low level optimization is done at
the compiled-code level. Low level optimization may be good on some platforms and bad on
other platforms.

6.2. Code optimization strategy


Code optimization - when to begin

Optimize the source code, around the time developer completes a preliminary developer version
of the program.

Ideally all developers should have a list of best approaches that result in optimized code, before
starting to code.

Detailed optimization should not be started before developer completes an initial version of the
code. Programmer should not let performance considerations affect the design of a piece of code.
Detailed performance should be considered only after the design of the code is complete.

Code optimization - where to begin

Identify the parts of the application or program executed more frequently or which consume
more execution time. 80 or 90 % of a program's execution time may be spent executing 20% or
10% of the code (Pareto principle).

So we should first optimize that 20% or 10% of the program identified accordingly.

Some classes or functions within classes may be used more frequently than others.

(Misuse of 'Ask a Doubt' Section will be dealt as per the Terms & Conditions of Campus Commune)

6.3. Standard techniques list


Common sub-expression/ Code grouping.

Unrolling loop.

Functional optimization

Avoid unused variables and parameters

Finding/Selection of effective algorithms.

Common sub-expression/ Code grouping.

Certain sub expressions may be common in several lines of code. In such a program when each
line of code gets executed, a common sub-expression gets executed and its same value gets
calculated, repetitively. This happens in many lines, even though it has the same value. In this
case, we should compute the common sub-expression only once and that variable, holding the
computed value of the common sub-expression should be used.

Certain parts of an expression may yield a constant value within certain scopes within the
program or function, such as within many blocks of a function, but may change only in few
blocks where re-computation is necessary. In such situations, based on the program context, we
may move the repetitive code into a constant in many scopes where it would be constant, and
substitute the original expression only in the few scopes (blocks etc) where it may be subject to
change. This code-grouping is an advanced form of common sub-expression.

You may also have situations in which certain temporary variables may be declared just once
outside loops and be re-assigned within the loop.

Non optimized common sub expression

function runWithoutIdentifyingCommonSubExpressions() {

j=0;

for( i=36000000;i>0;i--)

j += (i-sqrt(i)-sqrt(sqrt(i)))/sqrt(123456)+(i-sqrt(i)-sqrt(sqrt(i)))/sqrt(654321);

print(j);
}

Optimized common sub expression

function runWithoutIdentifyingCommonSubExpressions() {

j=0;

commonValueInAllLoopIterations = 1/sqrt(123456)+1/sqrt(654321);

//Above expression with same value in all iterations taken outside to make loop faster.

for( i=36000000;i>0;i--)

j += (i-sqrt(i)-sqrt(sqrt(i)));

j=j*commonValueInAllLoopIterations

print(j);

In the above program the common sub expression is evaluated by multiplying the value of the
variable 'commonValueInAllLoopIterations' residing outside the for loop with value that is stored
in the variable 'j'.

Unrolling loops

Sometimes we use nested for-loops while the same task can be done with a single for-loop.

Sometimes we may be sure that a certain loop may get executed even number of times. In such a
situation, we may increment the loop counter by 2, and use an additional line or block of code
based on the next value of the current loop-index. For example, if we know that n= an even
number always, then the following code will reduce the number of loop iterations.

double x = 0;

for(int n=0 ; n <= 10000 ; n += 2)


{

x+=Math.sqrt(n);

fnXyzAddsModifiedResultToDynamicLiveFile(x);

x+=Math.sqrt(n+1);

fnXyzAddsModifiedResultToDynamicLiveFile(x);

Functional optimization

Programmers sometimes write code in a modular fashion which allows for several programmers
to work independently on separate concepts. These can be assembled at a later stage to create the
entire project. This concept forms the foundation of procedures or routines. Well-structured,
modular code is easier to write, debug, understand, and reuse.

When a method is invoked (called), a request is made to perform some action, such as setting a
value, printing statements, returning an answer, etc. The code to invoke the method contains the
name of the method to be executed and any needed data that the receiving method requires. The
required data for a method is specified in the method's parameter list.

Let us consider one example.

We need to calculate the salary of an employee. The salary has got three components- Basic, DA
and HRA. The DA is 10% of basic and HRA and 5% of Basic. The total salary would be the sum
of all three. Lets see one way of implementing this:

Before Modularizing

calculateTotalSalary()

basic=10000; //Assuming basic to be 10000

totalSalary=0;

da=(10000*0.1);
hra=(10000*0.05);

totalSalary=basic+da+hra;

print totalSalary;

This would give us the output, but it is a poor code from a reuse point of view. In case at a later
time da or hra calculation changes, we need to change it at different places.

This is where modularity comes to the picture. The same code can be modularized as follows:

After Modularizing

calculateHRA( basic)

hra=(basic*0.05);

return hra;

calculateDA( basic)

da=(basic*0.1);

return da;

calculateTotalSalary()

basic=10000; //Assuming basic to be 10000

totalSalary=0;
totalSalary=basic+calculateHRA(basic)+calculateDA( basic);

print totalSalary;

Now, when da or hra calculation changes, we just have to change it at the correct function. Also,
in case we have some other functionality that requires the da or the hra alone, they can just call
the corresponding function.

Avoid unused parameters and variables

Please consider the function given below. We will identify at least 3 points using which we can
further optimize the code snippet provided below, based on this principle.

calculateTotalSalary(double basicPay,double firstBasicPay)

double salary=100;

double salary=basicPay;

double totalSalary=0;

double da=(10000*0.1);

double hra=(10000*0.05);

double totalSalary=basic+da+hra;

print(totalSalary);

Here firstBasicPay is an input parameter to the function. But it is not used anywhere else in the
program. So while calling this function firstBasicPay should be compulsorily passed, even
though it is not required currently. This would lead to unnecessary memory allocation for the
parameter firstBasicPay.

Similarly salary is an unused variable initialized to the value 100.


Again, in the above program, the passed-in basicPay may be directly used without re-assigning
to basic.

So avoiding unused parameters and unused variables in writing a function leads to better
optimization technique in writing the function.

Finding/Selection of effective algorithms

Finding or selecting an effective algorithm for the computational task or the procedure in general
is a must before resorting to any other kind of optimization.

A good optimization is choosing a fast algorithm. A computational task can be performed in


several different ways with varying efficiency. For example, consider the following code snippet
whose intention is to obtain the sum of all integers from 1 to N:

Before Optimization

/**

* This function sumOfIntegers intention is to obtain the sum of all

* integers from 1 to N.

* Below code snippet calculates the sum by simply looping the value of N

* @parameter n - means the input value

*/

function sumOfIntegers(int n)

int i, sum = 0; // declare the variables i, sum

for (i = 1; i <= n; ++i) { // loop through till satisfies the condition i <= n

sum += i; // sum = sum + i


}

print ("sum : " + sum);

After Optimization

function sumOfIntegers(int n)

int sum = n * (1 + n) / 2;

print ("sum : " + sum);

In the above program 1/sqrt(123456) and 1/sqrt(654321) are sub expressions which are repeated
for every iteration. This can be modified by defining a common sub expression, in which the
above example can be modified.

6.4. Code optimization summary

Points of care

The time taken to undertake optimization should be carefully decided.

Optimizing existing code usually does not add new features.

Be sure to ensure that no new bugs are added during optimization.

Readability should not be lost after optimizing the unoptimized code.

Maintainability should not be impacted after optimizing the unoptimized code.

Be careful when optimizing large programs that already work without bugs.

If programmers follow the above principles, then programs will not be complicated by the
optimization and programmer will not be distracted by optimizing.

Code optimization summary


The optimization of code may begin at two steps in case of individual classes and functions.
While starting to code, certain basic principles of code optimization may be kept in mind, which
are based on common sense. Advanced optimization may begin, once the most frequently
executed areas of the programs and frequently used functions are identified.

For further reading you may refer the websites below.

http://channel9.msdn.com/Shows/Going+Deep/Russell-Hadley-The-Route-to-C-Code-
Optimization

It's nice to write clean code (code that looks good, is organized, is easy for others to understand
by reading it, etc). As developers we get to use great tools to implement algorithms in our
favorite languages. The act of composing a program is much like that of writing a story or, in
some cases, a poem But the underlying hardware isn't much interested in intelligent class
hierachies and easy-to-understand lines of programming language syntax. Processors do not
speak C++ or Java or C# or VB, etc.

The focus of this interview is mapping the (long and complicated)path to executable machine
code that the machine natively understands and acts upon, bringing your code to life. How does
this work, exactly?

Russell Hadley is a senior developer on the C++ team here at Microsoft and he spends his days
(and nights, ocassionally) writing code that takes the front-end compilation linear (flattened)
blob and turns it into highly optimized machine code patterns that the processor can execute in a
highly efficient manner.

This is a deep interview with lots of whiteboarding, but it is shallow enough so you won't drown
if you can't swim very well. Enjoy. This is another great conversation with one of the C++
experts who live in Building 41.