Changes in inline behavior on O2 optimization level for GCC 10+

Alexey Makhmutov <a.makhmutov@postgrespro.ru>

From: Alexey Makhmutov <a.makhmutov@postgrespro.ru>
To: pgsql-hackers@postgresql.org
Date: 2025-07-27T21:48:49Z
Lists: pgsql-hackers

Attachments

GCC 10 introduced some changes to its handling of functions inline 
optimization. These changes are described in its changelist 
(https://gcc.gnu.org/gcc-10/changes.html) as following:
  > Inter-procedural optimization improvements:
  > ...
  > -finline-functions is now enabled at -O2 and was retuned for better 
code size versus runtime performance trade-offs. Inliner heuristics was 
also significantly sped up to avoid negative impact to -flto -O2 compile 
times.

Parameter 'inline-functions' was disabled by default in GCC 9 at O2 
optimization level, so compiler was considering only functions marked 
with 'inline' keyword as suitable for inline optimization. This behavior 
was changed in GCC 10: now this option is enabled at the O2 level, so 
even functions not explicitly marked as eligible for inline may be still 
optimized in such way. To compensate influence of this change on 
compiling time and resulting code size a set of threshold parameters 
were changed. In particular, following parameters were changed:
* 'early-inlining-insns' from 14 to 6 ("Specify growth that the early 
inliner can make. In effect it increases the amount of inlining for code 
having a large abstraction penalty.")
* 'max-inline-insns-single' from 200 to 70 ("This number sets the 
maximum number of instructions (counted in GCC’s internal 
representation) in a single function that the tree inliner considers for 
inlining. This only affects functions declared inline and methods 
implemented in a class declaration (C++).")
* 'max-inline-insns-auto' from 30 to 15 ("When you use 
-finline-functions, a lot of functions that would otherwise not be 
considered for inlining by the compiler are investigated. To those 
functions, a different (more restrictive) limit compared to functions 
declared inline can be applied (--param max-inline-insns-auto).")
* 'inline-min-speedup' from 15 to 30 ("When estimated performance 
improvement of caller + callee runtime exceeds this threshold (in 
percent), the function can be inlined regardless of the limit on --param 
max-inline-insns-single and --param max-inline-insns-auto.")

The notable change here is related to decrease of 
'max-inline-insns-single' parameter value. GCC 10+ now may decide to 
avoid optimizing functions with explicit 'inline' attribute if it is 
large enough to violate the new threshold. This mean that some functions 
which were inlined in GCC 9 / O2 builds will not be inlined in GCC 10+ / 
O2 builds. In particular, GCC 9 build of current master shows only 15 
instances of functions violating old threshold value (see attached 
'not-inlined-gcc-9.txt' file), while GCC 10 build has 174 cases for new 
threshold (see 'not-inlined-gcc-10.txt'). Note, that this applies only 
to O2 builds, while O3 level uses old threshold values.

The primary goal of this message is just to raise an awareness of this 
change and its possible influence on resulting builds.

Theoretically speaking, this change is not an issue by itself, as each 
compiler is in its right to adjust its optimization logic. Certain build 
pipeline could always have adjusted parameters if its maintainer is 
unsatisfied with default compiler heuristics. Inlining for functions 
marked with 'inline' is not guaranteed anyway (GCC 9 was already 
deciding to skip some of these functions as was shown above) and O2 
level is not the most aggressive optimization level in GCC as well. 
However, the GCC / O2 build is the most common configuration for Linux 
builds, so it's worth looking on whether code generated on this level is 
matching developers expectations. In particular, for some functions it 
may be beneficial to use 'pg_attribute_always_inline' instead of plain 
'inline' if function body is large enough to violate the new thresholds.

Thanks,
Alexey