[comp.lang.c] Cryptic code == Optimized code ? YES : NO ;

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.