GCC gprof statistics

Ishikawa ishikawa@yk.rim.or.jp
Tue Jul 1 02:01:00 GMT 2003


Zack Weinberg wrote:
> 
> Ishikawa <ishikawa@yk.rim.or.jp> writes:
> 
> > Once I get the latest CVS files in place,
> > I will produce
> >  - --print-search-dirs variants as suggested by Zack,
> >  - create a few experimental compilation speedup patches(2-3 %),
> >    which I believe will be helpful since for_each_rtx is rather small
> >    and called so many times in cc1 run,
> >  - and finally, will begin working on diagnostics.c format
> >    specifier thing again as suggested by Zack.
> >    (The last one will need people's input since I am not
> >     sure how the file is organized and I just read
> >    the description of portable VA_OPEN(), VA_CLOSE() macros, etc...
> 
> We're very glad to have you join the team.
> 
> It would be a good idea to start the ball rolling on copyright
> assignment paperwork now.  Please follow the instructions in the
> attached document.
> 

I sent it out.

> The good news is you can forget about VA_OPEN/VA_CLOSE; GCC 3.4 will
> require a C89 bootstrap compiler, so the normal <stdarg.h> primitives
> can be used instead.
> 
Sounds good!

Now here is the minor optimization to speed up the compilation by 2-3 %.

GCC was compiled using
make BOOT_CFLAGS="-g -pg -O2".

I found that CVS is already slightly faster than gcc 3.3. July 1st. 2003

For compiling a source of 8.5 K lines,

real	0m8.994s
user	0m8.810s   <-- gcc 3.3 used to take approximately 9.3 seconds. 
sys	0m0.070s


Here is what I did. Quote from the for_each_rtx.h attached below.

    Idea is to define a specialized for_each_rtx_xxxxx function
    that uses a given function (the riginal second paramter f to 
    for_each_rtx().)

     And instead of calling the generic for_each_rtx(),
     use the specialized version.

     For each call to for_each_rtx thus replaced, we have one fewer 
     argument to pass in the specialized version. 
     (gprof analysis indicates that
      for_each_rtx is called  so many times that this matters!)

After doing this,
the execution time has become (3 runs to make sure
the effect is real).


	real	0m8.720s
	user	0m8.520s
	sys	0m0.090s

	real	0m8.655s
	user	0m8.550s
	sys	0m0.050s

	real	0m8.697s
	user	0m8.520s
	sys	0m0.070s


	0.2-0.3 sec. 2-3 % speed up?

Since the modification introduces no logical change,
this is a safe optimization.

Here is the patch.

ishikawa@duron$ diff -c cse.c.save cse.c 
*** cse.c.save	Tue Jul  1 10:29:47 2003
--- cse.c	Tue Jul  1 10:34:04 2003
***************
*** 44,49 ****
--- 44,51 ----
  #include "target.h"
  #include "params.h"
  
+ #include "for_each_rtx.h"
+ 
  /* The basic idea of common subexpression elimination is to go
     through the code, keeping a record of expressions that would
     have the same value at the current scan point, and replacing
***************
*** 710,716 ****
  
  /* Subroutine of approx_reg_cost; called through for_each_rtx.  */
  
! static int
  approx_reg_cost_1 (rtx *xp, void *data)
  {
    rtx x = *xp;
--- 712,720 ----
  
  /* Subroutine of approx_reg_cost; called through for_each_rtx.  */
  
! static
! inline
! int
  approx_reg_cost_1 (rtx *xp, void *data)
  {
    rtx x = *xp;
***************
*** 736,741 ****
--- 740,748 ----
    return 0;
  }
  
+ DEFINE_FOR_EACH_RTX_ITERATOR(approx_reg_cost_1)
+ 
+ 
  /* Return an estimate of the cost of the registers used in an rtx.
     This is mostly the number of different REG expressions in the rtx;
     however for some exceptions like fixed registers we use a cost of
***************
*** 746,752 ****
  {
    int cost = 0;
  
!   if (for_each_rtx (&x, approx_reg_cost_1, (void *) &cost))
      return MAX_COST;
  
    return cost;
--- 753,759 ----
  {
    int cost = 0;
  
!   if (CALL_FOR_EACH_RTX_ITERATOR (&x, approx_reg_cost_1, (void *) &cost))
      return MAX_COST;
  
    return cost;
***************
*** 1769,1775 ****
    rtx exp;
  };
  
! static int
  check_dependence (rtx *x, void *data)
  {
    struct check_dependence_data *d = (struct check_dependence_data *) data;
--- 1776,1784 ----
    rtx exp;
  };
  
! static 
! inline
! int
  check_dependence (rtx *x, void *data)
  {
    struct check_dependence_data *d = (struct check_dependence_data *) data;
***************
*** 1778,1783 ****
--- 1787,1795 ----
    else
      return 0;
  }
+ 
+ DEFINE_FOR_EACH_RTX_ITERATOR(check_dependence)
+ 
  

  /* Remove from the hash table, or mark as invalid, all expressions whose
     values could be altered by storing in X.  X is a register, a subreg, or
***************
*** 1914,1920 ****
  		    p->canon_exp = canon_rtx (p->exp);
  		  d.exp = x;
  		  d.mode = full_mode;
! 		  if (for_each_rtx (&p->canon_exp, check_dependence, &d))
  		    remove_from_table (p, i);
  		}
  	    }
--- 1926,1932 ----
  		    p->canon_exp = canon_rtx (p->exp);
  		  d.exp = x;
  		  d.mode = full_mode;
! 		  if (CALL_FOR_EACH_RTX_ITERATOR (&p->canon_exp, check_dependence, &d))
  		    remove_from_table (p, i);
  		}
  	    }
***************
*** 7209,7215 ****
  	  /* If we haven't already found an insn where we added a LABEL_REF,
  	     check this one.  */
  	  if (GET_CODE (insn) == INSN && ! recorded_label_ref
! 	      && for_each_rtx (&PATTERN (insn), check_for_label_ref,
  			       (void *) insn))
  	    recorded_label_ref = 1;
  	}
--- 7221,7227 ----
  	  /* If we haven't already found an insn where we added a LABEL_REF,
  	     check this one.  */
  	  if (GET_CODE (insn) == INSN && ! recorded_label_ref
! 	      && CALL_FOR_EACH_RTX_ITERATOR (&PATTERN (insn), check_for_label_ref,
  			       (void *) insn))
  	    recorded_label_ref = 1;
  	}
***************
*** 7326,7332 ****
  /* Called via for_each_rtx to see if an insn is using a LABEL_REF for which
     there isn't a REG_LABEL note.  Return one if so.  DATA is the insn.  */
  
! static int
  check_for_label_ref (rtx *rtl, void *data)
  {
    rtx insn = (rtx) data;
--- 7338,7346 ----
  /* Called via for_each_rtx to see if an insn is using a LABEL_REF for which
     there isn't a REG_LABEL note.  Return one if so.  DATA is the insn.  */
  
! static 
! inline
! int
  check_for_label_ref (rtx *rtl, void *data)
  {
    rtx insn = (rtx) data;
***************
*** 7341,7346 ****
--- 7355,7363 ----
  	  && INSN_UID (XEXP (*rtl, 0)) != 0
  	  && ! find_reg_note (insn, REG_LABEL, XEXP (*rtl, 0)));
  }
+ 
+ DEFINE_FOR_EACH_RTX_ITERATOR(check_for_label_ref)
+ 
  

  /* Count the number of times registers are used (not set) in X.
     COUNTS is an array in which we accumulate the count, INCR is how much
ishikawa@duron$ 

--- for_each_rtx.h

diff -c /dev/null for_each_rtx.h
*** /dev/null	Thu Jan  1 09:00:00 1970
--- for_each_rtx.h	Tue Jul  1 10:39:11 2003
***************
*** 0 ****
--- 1,122 ----
+ /***
+     for_each_rtx optimization.
+ 
+     Idea is to define a specialized for_each_rtx_xxxxx function
+     that uses a given function (the riginal second paramter f to 
+     for_each_rtx().)
+ 
+      And instead of calling the generic for_each_rtx(),
+      use the specialized version.
+ 
+      For each call to for_each_rtx thus replaced, we have one fewer 
+      argument to pass in the specialized version. 
+      (gprof analysis indicates that
+       for_each_rtx is called  so many times that this matters!)
+ 
+     Also, by inlining the called functions (adding inline)
+     before the definition of specialized for_each_rtx_func, the compiler
+     can have ample opportunities to optimize. (After a second look,
+     this is unlikely. But at least we can probably save the call/return
+     overhead.)
+     
+     Now instead of hard-coding the specialized function,
+     I decided to write a macro to write the specialized
+     definition for given function.
+ 
+     This makes it easy to propagate any change (optimization)
+     in for_each_rtx to the definitions. We only need to rewrite the
+     macro once. 
+ 
+     Is such optimizatin of for_each_rtx itself likely?
+     By looking at the current for_each_rtx and comment from
+     Andrew Pinski posted to gcc maling list, I think we can handle
+     some special cases of length=2 or 1 rtl tree, etc..
+     (for that matter, is GCC clever enough to figure that
+     XVECLEN(*x,i,j) is a constant in the nested loop?
+ 
+ 
+ DEFINE_FOR_EACH_RTX_ITERATOR(foobar) 
+ defines
+ int
+ for_each_rtx_foobar(x, data);
+      rtx *x;
+      void *data;  
+ 
+ CALL_FOR_EACH_RTX_ITERATOR(x, func, z)
+      is changed into
+      for_each_rtx_func(x, z)
+ 
+ ***/
+ 
+ #define DEFINE_FOR_EACH_RTX_ITERATOR(func) \
+ int \
+ for_each_rtx_##func (x, /*f, */ data) \
+      rtx *x;			      \
+      /***rtx_function f;***/	      \
+      void *data;		      \
+ { \
+   int result; \
+   int length; \
+   const char *format; \
+   int i;		\
+ 		\
+   /* Call F on X.  */		\
+   result = func (x, data);		\
+   if (result == -1)		\
+     /* Do not traverse sub-expressions.  */		\
+     return 0;		\
+   else if (result != 0)		\
+     /* Stop the traversal.  */		\
+     return result;		\
+ 		\
+   if (*x == NULL_RTX)		\
+     /* There are no sub-expressions.  */		\
+     return 0;		\
+ 		\
+   length = GET_RTX_LENGTH (GET_CODE (*x));		\
+   format = GET_RTX_FORMAT (GET_CODE (*x));		\
+ 		\
+   for (i = 0; i < length; ++i)		\
+     {		\
+       switch (format[i])		\
+ 	{		\
+ 	case 'e':		\
+ 	  result = for_each_rtx_##func (&XEXP (*x, i), /*f,*/ data);		\
+ 	  if (result != 0)		\
+ 	    return result;		\
+ 	  break;		\
+ 		\
+ 	case 'V':		\
+ 	case 'E':		\
+ 	  if (XVEC (*x, i) != 0)		\
+ 	    {		\
+ 	      int j;		\
+ 	      for (j = 0; j < XVECLEN (*x, i); ++j)		\
+ 		{		\
+ 		  result = for_each_rtx_##func (&XVECEXP (*x, i, j), /*f,*/ data);		\
+ 		  if (result != 0)		\
+ 		    return result;		\
+ 		}		\
+ 	    }		\
+ 	  break;		\
+ 		\
+ 	default:		\
+ 	  /* Nothing to do.  */		\
+ 	  break;		\
+ 	}		\
+ 		\
+     }		\
+ 		\
+   return 0;		\
+ }
+ 
+ #define CALL_FOR_EACH_RTX_ITERATOR(x, func, d) for_each_rtx_##func ((x),(d))
+ 
+ 
+ 
+ /* Usage Example
+     DEFINE_FOR_EACH_RTX_ITERATOR(gazonk)
+ 
+  CALL_FOR_EACH_RTX_ITERATOR(a, gazonk, c)
+ */
+ 
ishikawa@duron$ 


PS: I didn't change all the use of for_each_rtx using the
macro above. Only the three major usages.

After the above modification, the gprof output looks like this.

Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls   s/call   s/call  name    
  3.85      0.18     0.18   206388     0.00     0.00  cse_insn
  2.36      0.29     0.11   164391     0.00     0.00  for_each_rtx
  2.14      0.39     0.10   100239     0.00     0.00  validate_value_data
  1.93      0.48     0.09  1793431     0.00     0.00  get_cse_reg_info
  1.93      0.57     0.09  1350273     0.00     0.00  mark_set_1
  1.93      0.66     0.09  1172862     0.00     0.00  ggc_alloc
  1.71      0.74     0.08   456357     0.00     0.00  for_each_rtx_approx_reg_cost_1
  1.50      0.81     0.07   387397     0.00     0.00  fold_rtx
  1.28      0.87     0.06  1969116     0.00     0.00  find_reg_note
  1.28      0.93     0.06   180810     0.00     0.00  constrain_operands
  1.28      0.99     0.06   156249     0.00     0.00  copy_rtx_if_shared
  1.28      1.05     0.06   101632     0.00     0.00  for_each_rtx_check_dependence
  1.28      1.11     0.06     1158     0.00     0.00  init_alias_analysis
  1.07      1.16     0.05   360696     0.00     0.00  invalidate
  1.07      1.21     0.05   328996     0.00     0.00  extract_insn
  1.07      1.26     0.05   178282     0.00     0.00  for_each_rtx_check_for_label_ref
  1.07      1.31     0.05     1928     0.00     0.00  rtl_verify_flow_info_1
  0.86      1.35     0.04  1247401     0.00     0.00  ix86_find_base_term
  0.86      1.39     0.04  1029410     0.00     0.00  rtx_equal_p
  0.86      1.43     0.04   627713     0.00     0.00  record_set

  ... rest omitted ...


-- 
int main(void){int j=2003;/*(c)2003 cishikawa. */
char t[] ="<CI> @abcdefghijklmnopqrstuvwxyz.,\n\"";
char *i ="g>qtCIuqivb,gCwe\np@.ietCIuqi\"tqkvv is>dnamz";
while(*i)((j+=strchr(t,*i++)-(int)t),(j%=sizeof t-1),
(putchar(t[j])));return 0;}/* under GPL */



More information about the Gcc mailing list