22 jop oct 08

1
SEPTEMBER 2008 | LINUX FOR YOU | www.openITis.com 106 S.G. GANESH The Joy of Programming C /C++ allows only integer types for use in case statements. Why can’t we use floating point numbers? Because C designers thought that it is not a good idea: checking the exact equality in floating point is not portable [ref: C99 rationale]. How about string literals? It is allowed in many languages that evolved from C, such as C#, which is a useful feature. Since switch is for integral types, a compiler can translate it to efficient code, as we will now see. Which of the two is better: a switch statement or cascading if-else statements? Well, a switch expresses the programmer’s intentions more clearly than an if-else cascade. Also, you might be surprised to know that a switch is, in general, more efficient than an equivalent if-else statement sequence! Why? The if-else statement is flexible: it can have different conditions for each ‘if’ statement; also each condition can have (different) variables for comparison in the conditional expression. However, a switch statement is limited: it can have only one condition and the matching of the case statements to the condition expression is always an equality comparison; the case statements are always constant values (and not variables). Because of these reasons, the compiler can do a better job and generate efficient code. How? A sequence of if-else statements is typically translated as a sequence of labels and jump statements (gotos). For a switch statement, a compiler generates an internal table to find the matches at runtime. Depending on the constants in the case statements, the table can be a look-up or range table. If the constants are unrelated, the comparison is usually done at the beginning and the jump is made to the specific entry in the table (i.e., a look-up table). If the constants are related and within a range (e.g., ‘0’ to ‘9’), the jump can be made for each range of values (i.e., a range table). For example, a Java compiler internally compiles the switch statements into either lookupswitch or tableswitch bytecodes. So the switch is typically more efficient than if-else statements (unless the compiler is very smart, which is unlikely). The efficiency of switch statements is often exploited in different techniques and we’ll now look at an unusual case. A source of nasty bugs in C-based languages is that the case statements in the switch statement are fall- through. The ‘fall-through’ nature of switch is exploited in a technique called as Duff’s device [Tom Duff, ‘netnews’, May 1984]. The following function which copies count number of bytes pointed by from to to: send(short *to, short *from, int count){ do *to = *from++; while(--count>0); } // this program fails if count is equal to zero. and this version, compiled in a VAX C compiler, ran very slow. The reason is that the compiler translates do-while as a pair of two gotos and labels (one for each true and false case); for every condition check, a goto is executed, which makes it slow. So Tom Duff proposed another, faster version: send(short *to, short *from, int count){ register n=(count+7)/8; // get number of times to execute do...while loop switch(count%8){ // go to the remaining mod value case 0: do{ *to = *from++; case 7: *to = *from++; case 6: *to = *from++; case 5: *to = *from++; case 4: *to = *from++; case 3: *to = *from++; case 2: *to = *from++; case 1: *to = *from++; }while(--n>0); // this loop is executed n times } } // this program fails if count is equal to zero. The idea is to find out the number of times the loop is to be executed in n and call switch to copy for modulus value. The do-while loop just ignores the case statements since they are just labels. This technique exploits the fact that case statements do not break automatically. This version ran faster than the do-while loop version (one goto for one statement) because this version has less gotos (only one goto for 8 statements) when the compiler translates it. Even though this technique clearly exploits the fall through nature of C switch statements, it is (fortunately) not widely used; it is good to be aware of this technique, but don’t use it! The switch statement appears mundane; what can be special or interesting about it? In this issue, we’ll explore the switch statement—you may realise that you’ve underestimated its value! Duff’s Device and Some Interesting Aspects of Switch S.G. Ganesh is a research engineer in Siemens (Corporate Technology). His latest book is “60 Tips on Object Oriented Programming”, published by Tata McGraw-Hill. You can reach him at [email protected]

Upload: ganesh-sg

Post on 27-Dec-2014

224 views

Category:

Technology


2 download

DESCRIPTION

 

TRANSCRIPT

Page 1: 22 Jop Oct 08

September 2008 | L INUX For YoU | www.openItis.com106

S.G. GaneSh

The Joy ofProgramming

C/C++ allows only integer types for use in case statements. Why can’t we use floating point numbers? Because C designers thought that it is not a good idea:

checking the exact equality in floating point is not portable [ref: C99 rationale]. How about string literals? It is allowed in many languages that evolved from C, such as C#, which is a useful feature. Since switch is for integral types, a compiler can translate it to efficient code, as we will now see.

Which of the two is better: a switch statement or cascading if-else statements? Well, a switch expresses the programmer’s intentions more clearly than an if-else cascade. Also, you might be surprised to know that a switch is, in general, more efficient than an equivalent if-else statement sequence! Why?

The if-else statement is flexible: it can have different conditions for each ‘if’ statement; also each condition can have (different) variables for comparison in the conditional expression. However, a switch statement is limited: it can have only one condition and the matching of the case statements to the condition expression is always an equality comparison; the case statements are always constant values (and not variables). Because of these reasons, the compiler can do a better job and generate efficient code. How?

A sequence of if-else statements is typically translated as a sequence of labels and jump statements (gotos). For a switch statement, a compiler generates an internal table to find the matches at runtime. Depending on the constants in the case statements, the table can be a look-up or range table. If the constants are unrelated, the comparison is usually done at the beginning and the jump is made to the specific entry in the table (i.e., a look-up table). If the constants are related and within a range (e.g., ‘0’ to ‘9’), the jump can be made for each range of values (i.e., a range table). For example, a Java compiler internally compiles the switch statements into either lookupswitch or tableswitch bytecodes. So the switch is typically more efficient than if-else statements (unless the compiler is very smart, which is unlikely). The efficiency of switch statements is often exploited in different techniques and we’ll now look at an unusual case.

A source of nasty bugs in C-based languages is that the case statements in the switch statement are fall-through. The ‘fall-through’ nature of switch is exploited in a technique called as Duff’s device [Tom Duff, ‘netnews’, May 1984]. The following function which copies count number of bytes pointed by from to to:

send(short *to, short *from, int count){

do

*to = *from++;

while(--count>0);

} // this program fails if count is equal to zero.

and this version, compiled in a VAX C compiler, ran very slow. The reason is that the compiler translates do-while as a pair of two gotos and labels (one for each true and false case); for every condition check, a goto is executed, which makes it slow. So Tom Duff proposed another, faster version: send(short *to, short *from, int count){

register n=(count+7)/8;

// get number of times to execute do...while loop

switch(count%8){

// go to the remaining mod value

case 0: do{ *to = *from++;

case 7: *to = *from++;

case 6: *to = *from++;

case 5: *to = *from++;

case 4: *to = *from++;

case 3: *to = *from++;

case 2: *to = *from++;

case 1: *to = *from++;

}while(--n>0);

// this loop is executed n times

}

}

// this program fails if count is equal to zero.

The idea is to find out the number of times the loop is to be executed in n and call switch to copy for modulus value. The do-while loop just ignores the case statements since they are just labels. This technique exploits the fact that case statements do not break automatically. This version ran faster than the do-while loop version (one goto for one statement) because this version has less gotos (only one goto for 8 statements) when the compiler translates it.

Even though this technique clearly exploits the fall through nature of C switch statements, it is (fortunately) not widely used; it is good to be aware of this technique, but don’t use it!

The switch statement appears mundane; what can be special or interesting about it? In this issue, we’ll explore the switch statement—you may realise that you’ve underestimated its value!

Duff’s Device and Some Interesting Aspects of Switch

S.G. Ganesh is a research engineer in Siemens (Corporate Technology). His latest book is “60 Tips on Object Oriented Programming”, published by Tata McGraw-Hill. You can reach him at [email protected]