Posted by: {authorName}

Seems that the there's not much we as software programmers can do with the statements...

But did you know that double-digit percentage optimisation results can be achieved with a few simple steps?

Having optimally-running software is quite a bit of challenge. The main difficulty in achieving maximum speed lays in small implementation details, that, if not treated properly on time, build up and often are difficult to track down.

In a nutshell, writing a fast program means coding the same logic in fewer steps required for the computer to perform.

The following are several simple hints for programmers related with the well-known statements.

Program codes seldom follow a unidirectional path if statements are probably the most used statements of all. The syntax of the statement is:

if <condition holds true> then <do something with THEN (TRUE) caluse>
otherwise <do something with ELSE (FALSE) clause>


Firstly, some sign legends:
  • == - EQUALS TO
  • != - NOT EQUALS TO
  • && - AND clause
  • || - OR clause
  • ! - NOT (NEGATION) clause

Usually, the condition of the if statement is comprised of multiple atomic (unbreakable into simpler pieces) conditions with optional NOT negation clause that are joint together with AND or OR clauses.

Example:
1. (a == b) && (c != d)
2. (a != b) && (c != d)
are both valid if conditions. Let's analyse the number of steps needed to gain a result.

(a == b) && (c != d) - in human language this means that to perform THEN clause a and b must be the same and c and d must be different.
This condition is equivalent to (a == b) && !(c == d). So
  1. Check if a is equal to b
  2. Check if c is equal to d
  3. Negate the result of 2.
  4. Output TRUE if 1. and 3. are TRUE
All in all 4 steps.

(a != b) && (c != d) - in human language this means that to perform THEN clause a and b must be different and so must c and d.
This condition is equivalent to !(a == b) && !(c == d). So
  1. Check if a is equal to b
  2. Negate the result of 1.
  3. Check if c is equal to d
  4. Negate the result of 3.
  5. Output TRUE if 2. and 4. are TRUE
All in all 5 steps.

Disjunctive Normal Forms (DNF) and Conjunctive Normal Forms (CNF) are methods to transform a condition into an equivalent condition (which may result to better performance in computer terms).

DNFs contain atomic conditions that are related to each other with OR clauses.
CNFs contain atomic conditions that are related to each other with AND clauses.

Following are statements that we will use to figure out how to optimise conditions of if statements.

1.
DNFs result to TRUE value if at least one of its conditions is TRUE,
CNFs result to TRUE value if and only if all of its conditions are TRUE.
Likewise,
CNFs result to FALSE value if at least one of its conditions is FALSE,
DNFs result to FALSE value if and only if all of its conditions are FALSE.

2.
DNFs can be converted into CNFs and vice versa using the following rules. I will use <==> sign to mark equivalency.
  • A || B <==> !(!A && !B)
  • A && B <==> !(!A || !B)


Having the two statements above we can deduct that the (a != b) && (c != d) condition can be performed faster by performing less steps. Really,
  • (a != b) && (c != d) <==> !(a == b) && !(c == d) ,
  • which by the DNF to CNF interconversion rule <==> !( !(!(a == b)) || !(!(c == d)) ).
  • ! applied twice neutralises itself thus we have !( (a == b) || (c == d) ).

Analysing the result we have the following steps
  1. Check if a is equal to b
  2. Check if c is equal to d
  3. Take TRUE if either 1. or 2. or both are TRUE
  4. Take the negated result of 3.

So instead of 5 steps we managed to do the same thing in 4 steps. Taking a close look at both versions
!(a == b) && !(c == d) and !( (a == b) || (c == d) )
we can clearly see that the optimum one has less NEGATION clause. So the verdict is:
  1. NEGATION clauses can be trimmed
  2. Excessive NEGATION clauses can slow down the code.

Modern programming languages are going one extra step to optimise the code further by making good use of the 1. statement about DNFs and CNFs:
  • For DNFs if the code encounters a TRUE atomic clause it disregards the rest of the clauses as they will not influence the final result,
  • For CNFs if the code encounters a FALSE atomic clause it disregards the rest of the clauses as they will not influence the final result.
So, approaching wisely here, we can in less than the 4 steps achieve the same results as above:
  • For DNFs put the likely TRUE clauses before the rest of the clauses,
  • For CNFs put the likely FALSE clauses before the rest of the clauses.

Let's consider !( (a == b) || (c == d) ) again.
  1. Check if a is equal to b
  2. If 1. is TRUE negate 1. and take the result as an answer (2 STEPS ONLY), otherwise If 1. is FALSE Check if c is equal to d
  3. Take TRUE if either 1. or 2. or both are TRUE
  4. Take the negated result of 3.

Considering that 2. has 50/50 chance to be TRUE or FALSE we finally have that on average the whole statement can be performed in ( 2 + 4 ) / 2 = 3 steps.

So we gained approximately 40% performance improvement in obtaining a result for the condition (a != b) && (c != d).

Additionally, if an
if (condition) then else
can be transformed into an equivalent
if (condition) then
form then the code will avoid an unnecessary calculation which is also an optimisation measure.

Further to come,
1. Does unary operators have underwater reefs.
2. Memory usage versus quick execution, an example.

Comments

blog comments powered by Disqus