From: richard@iesd.auc.dk (Richard Flamsholt S0rensen)
Newsgroups: comp.lang.c
Subject: Optimizing code (was Re: printf() questions)
Date: 10 Apr 92 14:50:48 GMT
Distribution: comp
Organization: Mathematics and Computer Science, University of Aalborg


>>>>> On 5 Apr 92 12:35:50 GMT, jcoxhead@acorn.co.uk (Jonathan Coxhead) said:

JC> pf>   1) I have have a character array (for a string): name[NAME_SIZE].

  Just an aside: make that NAME_SIZE + 1 to accomodate for the \0.

JC> pf> NAME_SIZE is #defined because I need to be able to change it easily,
JC> pf> and it appears all over the program. I want to print this string out,
JC> pf> and, so that things will line up nicely, I want it printed in a field
JC> pf> of length NAME_SIZE. (i.e. printf("%-25s",name) ... except with
JC> pf> NAME_SIZE where 25 is)

JC>    Two answers for this have appeared so far, neither optimal.

  Now, what is "optimal" ? Optimal in readability?  Speed?  Size of
sourcecode or executable?  I assume you mean "speed", since you propose...

JC>       #define str_(s) #s
JC>       #define str(s) str_(s)
JC>       printf ("%-" str (NAME_SIZE) "s", name);

JC> That's faster than reading the macro out of the argument list.

  This is definitely harder to read than the "*"-notation and even
requires a double-macro preprocessor trick so it's far from optimal to
read and understand.
  Now, if it substantially speeds up a bottleneck, that *could* be ok,
but *is* it really faster?  It did not seem obvious to me, that printf
could read a string literal and convert it into a number (the
field-width) any faster than having it look at a "*" and know where to
find that number on the argument stack.

  I know it is against all good c.l.c netiquette, but I decided to
test this instead of throwing some "I think"-arguments into a letter
and rush it off, which has become the de facto way of replying  >;-)
  Here's the test program:

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#include <stdio.h>

#define str_(s)		#s
#define str(s)		str_(s)
#define NAME_SIZE	25

int
main()
{
  char *name = "test string";

#ifdef CONCAT
  printf ("[%-" str (NAME_SIZE) "s]\n", name);
#else
  printf ("[%-*s]\n", NAME_SIZE, name);
#endif
  return 0;
}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

This was compiled to assembler code using gcc -S with and without
CONCAT defined.  A result of a

	diff strnorm strconcat

gave this output:

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
6c6
< 	.ascii "[%-*s]\12\0"
---
> 	.ascii "[%-25s]\12\0"
14,16c14,16
< 	sethi %hi(LC0),%o3
< 	or %lo(LC0),%o3,%o3
< 	st %o3,[%fp-20]
---
> 	sethi %hi(LC0),%o2
> 	or %lo(LC0),%o2,%o2
> 	st %o2,[%fp-20]
19,20c19
< 	mov 25,%o1
< 	ld [%fp-20],%o2
---
> 	ld [%fp-20],%o1
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  -where the four last lines shows that the only difference (apart
from the difference in format strings) is the single mov instruction,
that puts the literal 25 on the stack before calling printf.

  So far, the "*"-based code may therefore seem marginally larger and
more time-consuming, but we have not yet considered printf().  Not
having the code for printf() at hand, my investigation stopped here,
but I'll bet that printf's handling of "%-*" vs "*-25" can be
expressed in (very ;-) pseudo code as

	read and treat %-
	recognize a '*'
	now we know next arg is an int, we know where it is on the arg
	  stack -- so just mov it into width register

versus

	read and treat %-
	next char is a digit; read until no more digits while converting
	  the digits into a number to place in width register [there's
	  two digits here, meaning probably several a-digit?-comparisons,
	  additions, multiplies by 10 and branches in a loop].


  Frankly, I think the overhead in the latter is much, much more
severe than that of the single, extra mov instruction in the former
and that the optimization Jonathan suggests is therefore both harder
to grasp and produces slower code.

  This just goes to show how careful you should be before trying to
optimize something, by reasoning that it "obviously is much faster
this way..." (I know it, I've made optimizations myself which turned
out to slow things down  :-)


  ciao, Richard
--
Richard Flamsholt
richard@iesd.auc.dk
Xref: sparky comp.lang.c:7290 comp.programming:760