csp@gtenmc.UUCP (Charudutta S. Palkar) (09/11/90)
C is a very good language ie to say it good to good programmers and
bad to bad programmers.
I would like to discuss a few issues that I have noticed during programming
in C.
Note : sections of examples are logically equivalent
Example 1: Variable k is of type int.
a ) if ( a > b )
k = 7;
else
k = 5;
b ) k = a > b ? 7 : 5;
c ) k = ( a > b ) * 2 + 5;
Example 2: Variables a and b are of the same type either int , char , float.
a ) a = a + b;
b = a - b;
a = a - b;
b ) a = a - ( b = ( a = a + b ) - b );
c ) a -= b = ( a += b ) - b;
Example 3: Variables a and b are of the same type either int , char
a ) a = a ^ b;
b = a ^ b;
a = a ^ b;
b ) a = a ^ ( b = b ^ ( a = a ^ b ));
c ) a ^= b ^= a ^= b;
Example 4: Variables a and b are pointers to structures with
self referential pointers as fields.
typedef struct fool
{
struct fool *nxt , *prv;
char data;
} node , *ptr;
ptr a, b;
a ) b->prv = a;
b->nxt = a->nxt;
b->nxt->prv = b;
a->nxt = b;
b ) ( b->nxt = ( b->prv = a )->nxt )->prv = a->nxt = b;
The functions of examples 2 & 3 is to interchange values 2 variables
of numeric type. Example 4 is insertion of a node in a circularly
doublely linked list.
My questions are :
1 ) Will the code generated by a non-optimizing comiler be more
more optimised as a set of statements get combined into one
expression.
2 ) Will the same happen even with an optimizing compiler.
3 ) Should such kind of compaction be favoured for development.
Thanx In advance.
C S Palkar - csp@gtenmc.gtetele.com
csp@gtenmc.UUCP
PS : Any mistakes in the above code spotted , be pointed out and
other such examples I would like to know of.
" I only speak for myself "
K&R C > ANSI C.
stephen@estragon.uchicago.edu (Stephen P Spackman) (09/11/90)
In article <861@gtenmc.UUCP> csp@gtenmc.UUCP (Charudutta S. Palkar) writes: C is a very good language ie to say it good to good programmers and bad to bad programmers. Example 1: Variable k is of type int. a ) if ( a > b ) k = 7; else k = 5; b ) k = a > b ? 7 : 5; c ) k = ( a > b ) * 2 + 5; C is daft. Why do extra work? a and b differ in intent; if you are thinking? "Hmm. Next step, set k to the right value", use b; if you are thinking "Well, there are two cases, and k will have to be different in each case...", maybe you want a. In short: think about what will happen when you need to *modify* the code, and code accordingly. Example 2: Variables a and b are of the same type either int , char , float. a ) a = a + b; b = a - b; a = a - b; b ) a = a - ( b = ( a = a + b ) - b ); c ) a -= b = ( a += b ) - b; Use {int /* Or whatever */ tmp = a; a = b; b = tmp;} Moves are faster and clearer. Why do extra work? Example 3: Variables a and b are of the same type either int , char a ) a = a ^ b; b = a ^ b; a = a ^ b; b ) a = a ^ ( b = b ^ ( a = a ^ b )); c ) a ^= b ^= a ^= b; Never do bit arithmetic on signed types. And isn't this the same as (2)? Example 4: Variables a and b are pointers to structures with self referential pointers as fields. typedef struct fool { struct fool *nxt , *prv; char data; } node , *ptr; ptr a, b; a ) b->prv = a; b->nxt = a->nxt; b->nxt->prv = b; a->nxt = b; b ) ( b->nxt = ( b->prv = a )->nxt )->prv = a->nxt = b; (b) could mean almost anything. Use a. The functions of examples 2 & 3 is to interchange values 2 variables of numeric type. Example 4 is insertion of a node in a circularly doublely linked list. My questions are : 1 ) Will the code generated by a non-optimizing comiler be more more optimised as a set of statements get combined into one expression. No. Probably the reverse. 2 ) Will the same happen even with an optimizing compiler. No. It will make *no* difference, one hopes. 3 ) Should such kind of compaction be favoured for development. No. See comments above. In fact, the "obvious" versions are more obvious to the compiler, as well as to humans. My question is, did I just fall for a joke? Remember, people will spend longer maintaining your code than the computer will running it. And people are more valuable than computers. stephen p spackman stephen@estragon.uchicago.edu 312.702.3982 p.s. Have you considered writing crossword puzzles?
scjones@thor.UUCP (Larry Jones) (09/11/90)
In article <861@gtenmc.UUCP>, csp@gtenmc.UUCP (Charudutta S. Palkar) writes: > Example 1: Variable k is of type int. > a ) if ( a > b ) k = 7; > else k = 5; > b ) k = a > b ? 7 : 5; > c ) k = ( a > b ) * 2 + 5; As has already been pointed out, the difference between a and b is one of emphasis -- a emphasizes the test, b emphasizes the assignment. Which to use depends on which you think is more important. c is sufficiently crytpic that I wouldn't use it; although I understand it, whoever has to maintain the code might not. > Example 2: Variables a and b are of the same type either int , char , float. > a ) a = a + b; b = a - b; a = a - b; > b ) a = a - ( b = ( a = a + b ) - b ); > c ) a -= b = ( a += b ) - b; Both b and c and invalid as they contain multiple assignments to the same variable without a sequence point. The common swap code: tmp = a; a = b; b = a; is not only more obvious, but probably faster as well. > Example 3: Variables a and b are of the same type either int , char > a ) a = a ^ b; b = a ^ b; a = a ^ b; > b ) a = a ^ ( b = b ^ ( a = a ^ b )); > c ) a ^= b ^= a ^= b; Same comments as example 2. > Example 4: Variables a and b are pointers to structures with > self referential pointers as fields. > a ) b->prv = a; b->nxt = a->nxt; > b->nxt->prv = b; a->nxt = b; > b ) ( b->nxt = ( b->prv = a )->nxt )->prv = a->nxt = b; Again, example b is invalid because of multiple assignments to the same variable without a sequence point. > 1 ) Will the code generated by a non-optimizing comiler be > more optimised as a set of statements get combined into one > expression. > 2 ) Will the same happen even with an optimizing compiler. > 3 ) Should such kind of compaction be favoured for development. Some compilers do more optimization within a single expression than across expressions, for better compilers it doesn't make much difference. In either case, the speed improvement you get from such optimization is rarely significant. Is the increased difficulty of writing the code, verifying that it's correct, and maintaining it worth a miniscule performance gain? Probably not. Certainly not if you end up writing incorrect code like you did above. ---- Larry Jones UUCP: uunet!sdrc!thor!scjones SDRC scjones@thor.UUCP 2000 Eastman Dr. BIX: ltl Milford, OH 45150-2789 AT&T: (513) 576-2070 Your gender would be a lot more tolerable if it wasn't so darn cynical! -- Calvin
jb7m+@andrew.cmu.edu (Jon C. R. Bennett) (09/13/90)
scjones@thor.UUCP (Larry Jones) writes: > ... The common swap code: > tmp = a; a = b; b = a; > is not only more obvious, but probably faster as well. It IS faster. > > > Example 3: Variables a and b are of the same type either int , char > > a ) a = a ^ b; b = a ^ b; a = a ^ b; > > b ) a = a ^ ( b = b ^ ( a = a ^ b )); > > c ) a ^= b ^= a ^= b; > I have had it with this "lets swap using xor" stuff, on machines with lots of registers (like the MIPS R2000 I'm about to use as an example) you are far better of with "tmp = a; a = b; b = a;" then a = a ^ b; b = a ^b; a = a ^ b; below are two functions followed by the optimized assembly output int bar(a,b) int a,b; { int tmp; a ^= b; b ^= a; a ^= b; return a+b; } bar: [foo.c: 18] 0x10: 00852026 xor r4,r4,r5 [foo.c: 19] 0x14: 00a42826 xor r5,r5,r4 [foo.c: 20] 0x18: 00852026 xor r4,r4,r5 [foo.c: 22] 0x1c: 03e00008 jr r31 [foo.c: 22] 0x20: 00851021 addu r2,r4,r5 int foo(a,b) int a,b; { int tmp; tmp= a; a = b; b=tmp; return a+b; } foo: [foo.c: 6] 0x0: 00801821 move r3,r4 [foo.c: 7] 0x4: 00a02021 move r4,r5 [foo.c: 10] 0x8: 03e00008 jr r31 [foo.c: 10] 0xc: 00831021 addu r2,r4,r3 not only is the obvious code easier to read, it is faster, any good compiler (like GCC or the MIPS compiler) will seee that "tmp" becomes a dead variable and not bother to move it, generating faster code. Since the "xor swap" is harder to read, and slower, there is no good reason to not just swap the varibles in the obvious way as opposed to using the "clever" but worse (to read and to compile) xor hack. please stop jon bennett
dhesi%cirrusl@oliveb.ATC.olivetti.com (Rahul Dhesi) (09/27/90)
The real advantage of the exclusive OR method of exchanging two variables is that you can make it into a macro in C. It's a little harder to do that with the temp variable method because it is currently impossible to declare a variable in C without first knowing its type. The real question is whether the swap macro using exclusive OR is faster than a swap() function. I have seen a "typeof" keyword proposed that would fix this deficieny: #define swap(a,b) do { typeof(a) tmp; tmp = a; a = b; b = tmp; } until (0); -- Rahul Dhesi <dhesi%cirrusl@oliveb.ATC.olivetti.com> UUCP: oliveb!cirrusl!dhesi
otto@tukki.jyu.fi (Otto J. Makela) (09/28/90)
In article <2514@cirrusl.UUCP> dhesi%cirrusl@oliveb.ATC.olivetti.com (Rahul Dhesi) writes:
I have seen a "typeof" keyword proposed that would fix this deficieny:
#define swap(a,b) do { typeof(a) tmp; tmp = a; a = b; b = tmp; } until (0);
I don't believe this! I have the chance to correct Mr. Dhesi!
The whole idea of having the do {...} while(0) [sic!] around the macro
definition is to ensure rational behaviour of the macro (which to the
unaided eye looks a lot like a function call) if invoked inside an
if-then-else type structure, where adding an extra ';' could affect the
syntactic interpretatiton. Thus, you DON'T use a ';' at the end of the
macro.
Actually, typeof() is not only proposed, it is implemented in gcc.
--
/* * * Otto J. Makela <otto@jyu.fi> * * * * * * * * * * * * * * * * * * */
/* Phone: +358 41 613 847, BBS: +358 41 211 562 (CCITT, Bell 24/12/300) */
/* Mail: Kauppakatu 1 B 18, SF-40100 Jyvaskyla, Finland, EUROPE */
/* * * Computers Rule 01001111 01001011 * * * * * * * * * * * * * * * * */
jb7m+@andrew.cmu.edu (Jon C. R. Bennett) (09/28/90)
dhesi%cirrusl@oliveb.ATC.olivetti.com (Rahul Dhesi) writes: > The real question is whether the swap macro using exclusive OR is > faster than a swap() function. since we are concerned with what a good compiler will do with the code (if you are dealing with a bad compiler you have bigger problems) will produce, the swap function is almost certin to be faster, since it is so small that it will be inlined, and then since it is a null operation, eliminated, making the swap faster. jon an interesting aside, does anyone who is very familiar with GCC machine description files think that they could write a peephole optimization that spots a^=b^=a^=b and converts it to a swap insted.