https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127610
Bug ID: 127610
Summary: 400.perlbench build 40% slower since
r17-4133-g230d950810f378 (inefficient LTO
partitioning?)
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: pheeck at gcc dot gnu.org
CC: pinskia at gcc dot gnu.org
Blocks: 26163
Target Milestone: ---
Host: aarch64-gnu-linux
Target: aarch64-gnu-linux
Description
-----------
Here's a graph showing this:
https://lnt.opensuse.org/db_default/v4/SPEC/graph?plot.0=588.10.8
Building the SPEC CPU 2006 benchmark 400.perlbench with
r17-4132-ga8c97564fbc39b and with r17-4133-g230d950810f378 with -Ofast
-march=armv8.2-a+crypto+fp16+rcpc+dotprod+ssbs -flto=64 on an Nvidia Grace
(Neoverse V2) machine (the march is replicating an Ampere Altra Neoverse N1
target, where I originally observed this):
rm -f *.o; time make -j$(nproc)
CC=$HOME/gcc/playground/inst-a8c97564fbc/bin/gcc
SPEC=$HOME/gcc/benchmarks/cpu2006/
real 0m3.867s
user 0m46.742s
sys 0m5.938s
rm -f *.o; time make -j$(nproc)
CC=$HOME/gcc/playground/inst-230d950810f378a16c47ab981e0b9cf88b700622/bin/gcc
SPEC=$HOME/gcc/benchmarks/cpu2006/
real 0m5.466s
user 0m47.196s
sys 0m5.988s
That's roughly a 40% slowdown.
The commit in question:
commit 230d950810f378a16c47ab981e0b9cf88b700622
Author: Andrea Pinski <[email protected]>
AuthorDate: Tue Aug 25 13:28:49 2026 -0700
Commit: Andrea Pinski <[email protected]>
CommitDate: Thu Sep 10 10:21:17 2026 -0700
phiopt/cselim: Support constants for cond_store_replacement_limited
[PR127052]
It's in LTO
-----------
Most of the time is spent in the LTO phase.
# rebuild all the .o files with a8c97564fbc, then:
time $HOME/gcc/playground/inst-a8c97564fbc/bin/gcc -Ofast
-march=armv8.2-a+crypto+fp16+rcpc+dotprod+ssbs -flto=64 -g -fpermissive
-std=gnu17 -DSPEC_CPU_LP64 -DSPEC_CPU_LINUX_X64 -std=gnu89
-Wl,-rpath,/home/fkastl/gcc/inst/lib64 av.o deb.o doio.o doop.o dump.o
globals.o gv.o hv.o locale.o mg.o numeric.o op.o pad.o perl.o perlapi.o
perlio.o perlmain.o perly.o pp.o pp_ctl.o pp_hot.o pp_pack.o pp_sort.o pp_sys.o
regcomp.o regexec.o run.o scope.o sv.o taint.o toke.o universal.o utf8.o util.o
xsutils.o Base64.o Cwd.o Dumper.o HiRes.o IO.o Peek.o attrs.o poll.o stdio.o
DynaLoader.o MD5.o Storable.o Parser.o specrand.o Hostname.o Opcode.o
-lm -o perlbench
real 0m3.340s
user 0m41.141s
sys 0m3.617s
# rebuild all the .o files with 230d950810f378a16c47ab981e0b9cf88b700622, then:
time $HOME/gcc/playground/inst-230d950810f378a16c47ab981e0b9cf88b700622/bin/gcc
-Ofast -march=armv8.2-a+crypto+fp16+rcpc+dotprod+ssbs -flto=64 -g -fpermissive
-std=gnu17 -DSPEC_CPU_LP64 -DSPEC_CPU_LINUX_X64 -std=gnu89
-Wl,-rpath,/home/fkastl/gcc/inst/lib64 av.o deb.o doio.o doop.o dump.o
globals.o gv.o hv.o locale.o mg.o numeric.o op.o pad.o perl.o perlapi.o
perlio.o perlmain.o perly.o pp.o pp_ctl.o pp_hot.o pp_pack.o pp_sort.o pp_sys.o
regcomp.o regexec.o run.o scope.o sv.o taint.o toke.o universal.o utf8.o util.o
xsutils.o Base64.o Cwd.o Dumper.o HiRes.o IO.o Peek.o attrs.o poll.o stdio.o
DynaLoader.o MD5.o Storable.o Parser.o specrand.o Hostname.o Opcode.o
-lm -o perlbench
real 0m4.924s
user 0m41.188s
sys 0m3.384s
It is the objects that matter
-----------------------------
Note that it is not enough to use the newer compiler for LTO. The objects have
to be built by the newer compiler. Actually, here's an experiment where I
tried to build the .o files with one compiler and LTO them with the other:
230d950810f378a16c47ab981e0b9cf88b700622 lto, a8c97564fbc .o ...
real 0m3.208s
user 0m40.500s
sys 0m3.677s
a8c97564fbc lto, 230d950810f378a16c47ab981e0b9cf88b700622 .o ...
real 0m5.032s
user 0m41.402s
sys 0m3.549s
Doesn't replicate with sequential LTO
-------------------------------------
# rebuild all the .o files with a8c97564fbc, then:
time $HOME/gcc/playground/inst-a8c97564fbc/bin/gcc -Ofast
-march=armv8.2-a+crypto+fp16+rcpc+dotprod+ssbs -flto -flto-partition=none -g
-fpermissive -std=gnu17 -DSPEC_CPU_LP64 -DSPEC_CPU_LINUX_X64 -std=gnu89
-Wl,-rpath,/home/fkastl/gcc/inst/lib64 av.o deb.o doio.o doop.o dump.o
globals.o gv.o hv.o locale.o mg.o numeric.o op.o pad.o perl.o perlapi.o
perlio.o perlmain.o perly.o pp.o pp_ctl.o pp_hot.o pp_pack.o pp_sort.o pp_sys.o
regcomp.o regexec.o run.o scope.o sv.o taint.o toke.o universal.o utf8.o util.o
xsutils.o Base64.o Cwd.o Dumper.o HiRes.o IO.o Peek.o attrs.o poll.o stdio.o
DynaLoader.o MD5.o Storable.o Parser.o specrand.o Hostname.o Opcode.o
-lm -o perlbench
real 0m54.082s
user 0m52.273s
sys 0m0.723s
# rebuild all the .o files with 230d950810f378a16c47ab981e0b9cf88b700622, then:
time $HOME/gcc/playground/inst-230d950810f378a16c47ab981e0b9cf88b700622/bin/gcc
-Ofast -march=armv8.2-a+crypto+fp16+rcpc+dotprod+ssbs -flto
-flto-partition=none -g -fpermissive -std=gnu17 -DSPEC_CPU_LP64
-DSPEC_CPU_LINUX_X64 -std=gnu89 -Wl,-rpath,/home/fkastl/gcc/inst/lib64
av.o deb.o doio.o doop.o dump.o globals.o gv.o hv.o locale.o mg.o numeric.o
op.o pad.o perl.o perlapi.o perlio.o perlmain.o perly.o pp.o pp_ctl.o pp_hot.o
pp_pack.o pp_sort.o pp_sys.o regcomp.o regexec.o run.o scope.o sv.o taint.o
toke.o universal.o utf8.o util.o xsutils.o Base64.o Cwd.o Dumper.o HiRes.o IO.o
Peek.o attrs.o poll.o stdio.o DynaLoader.o MD5.o Storable.o Parser.o specrand.o
Hostname.o Opcode.o -lm -o perlbench
real 0m52.778s
user 0m50.911s
sys 0m0.853s
So here actually the newer compiler seems to be faster.
A theory
--------
One theory could be that we're observing LTO having difficulties distributing
work across partitions evenly. Notice that the user time in the 'It's in LTO'
section doesn't change.
Maybe this behavior could be analyzed and LTO could be improved? I'm not sure
about this -- I don't have experience with LTO internals.
Referenced Bugs:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=26163
[Bug 26163] [meta-bug] missed optimization in SPEC (2026, 2k17, 2k and 2k6 and
95)