https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126603

            Bug ID: 126603
           Summary: Compile time hog in cse reciprocals
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Keywords: compile-time-hog
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: ktkachov at gcc dot gnu.org
  Target Milestone: ---

/* Compile-time hog: the occurrence-tree build in the "gimple CSE reciprocals"
   pass is quadratic in the number of sibling blocks that divide by the same
   SSA name.

     gcc -O1 -freciprocal-math -ftime-report -S t.c
   Compile with -DLOG4N=5 (1024 arms), -DLOG4N=6 (4096) or the default
   -DLOG4N=7 (16384) to see the scaling.

   Observed, reading the "gimple CSE reciprocals" line of -ftime-report:
     LOG4N=5   0.01s   5% of a  0.26s compile
     LOG4N=6   0.22s  17% of a  1.33s compile
     LOG4N=7   3.97s  36% of a 10.94s compile
   At LOG4N=7 it is the second-largest single entry in the report, behind RTL
   CSE.  A separate series with N = 1000/2000/4000/8000/16000 distinct case
   arms shows 0.01 / 0.05 / 0.21 / 0.89 / 3.86s at -O2, i.e. exactly 4x per
   doubling of N.  Expected: linear, or at least not the fastest-growing term
   in the compile.

   Mechanism.  register_division_in (tree-ssa-math-opts.cc:307) calls insert_bb
   (:243) once per basic block that divides by DEF.  insert_bb walks the entire
   sibling list at the current dominator level and calls
   nearest_common_dominator for every element before appending.  When the N
   division blocks are all immediate siblings under one dominator, as in a flat
   switch, the k-th insertion walks k-1 siblings, so the build costs O(N^2)
   nearest_common_dominator calls.  There is no cap and no early exit.  The
   tree is also built before the profitability test at :831
   (count + square_recip_count >= threshold), so unprofitable functions pay for
   it too.

   Attribution.  perf on the LOG4N=7 compile names
   nearest_common_dominator (1.80% of all cycles) and insert_bb (0.36%) and no
   other recip symbol.  Control experiment: replacing "1.0 / x" by
   "1.0 / (x + <arm index>)", so that each divisor is a distinct SSA name with
   a one-element occurrence list, makes the "gimple CSE reciprocals" line drop
   below the -ftime-report threshold entirely at N=8000, where the shared
   divisor costs 1.05s. */

#ifndef LOG4N
#define LOG4N 7
#endif

#define D1(i)   case i: p[i] = 1.0 / x; break;
#define D4(i)   D1(i) D1(i+1) D1(i+2) D1(i+3)
#define D16(i)  D4(i) D4(i+4) D4(i+8) D4(i+12)
#define D64(i)  D16(i) D16(i+16) D16(i+32) D16(i+48)
#define D256(i) D64(i) D64(i+64) D64(i+128) D64(i+192)
#define D1K(i)  D256(i) D256(i+256) D256(i+512) D256(i+768)
#define D4K(i)  D1K(i) D1K(i+1024) D1K(i+2048) D1K(i+3072)
#define D16K(i) D4K(i) D4K(i+4096) D4K(i+8192) D4K(i+12288)

#if LOG4N == 5
#define BODY D1K(0)
#elif LOG4N == 6
#define BODY D4K(0)
#else
#define BODY D16K(0)
#endif

double f (double x, int n, double *p)
{
  switch (n)
    {
      BODY
      default: break;
    }
  return x;
}

Reply via email to