Firstly, I've never contributed something via git-send-email before. I hope I've gotten everything correct but apologies in advance if I've made any mistakes in the process. If either of these are accepted, I don't have write access to commit them.
I have a use case where we generate C++ at runtime and compile it with GCC, in an environment where memory is a significant constraint. We use a PCH to speed this up, but memory usage is still considerable, even for trivial translation units. I tracked this down to two main issues: - mapping the PCH saves little memory: the objects a translation unit writes to are scattered over the file, so most pages end up dirty - every translation unit redoes the template instantiations that the header's inline functions need, even if it uses nothing from the header As far as I can tell, it would be possible to prevent GCC from writing to the memory mapped from the PCH. However, in practice this would be a large piece of work: the writes come from many places, including template instantiation, gimplification, the symbol table, type flags and hash tables stored in the PCH. Patch 1 takes a pragmatic approach of laying out the PCH so that the objects that do get written are spread across as few pages as possible. Patch 2 addresses the second issue by adding -fpch-instantiate-templates, which does the template instantiations when the PCH is created (mirroring the option available in Clang). I think both are fairly simple, and together they significantly reduce the runtime and memory usage of translation units using a large PCH. To reproduce: curl -LO https://cburr.web.cern.ch/gcc-pch-reproducer/JIT.h.xz && unxz JIT.h.xz curl -LO https://cburr.web.cern.ch/gcc-pch-reproducer/functors92.cpp curl -LO https://cburr.web.cern.ch/gcc-pch-reproducer/peakmem.sh && chmod +x peakmem.sh FLAGS="-std=c++23 -O3 -march=x86-64-v3 -fPIC -w" g++ $FLAGS -fpch-instantiate-templates -x c++-header JIT.h -o JIT.h.gch touch empty.cpp ./peakmem.sh g++ $FLAGS --param=ggc-min-expand=10 -include JIT.h -c empty.cpp ./peakmem.sh g++ $FLAGS --param=ggc-min-expand=10 -include JIT.h -c functors92.cpp JIT.h is 10 MB of preprocessed C++23 (preprocessed with GCC 15, minimally edited to build with trunk), functors92.cpp 92 generated functions using it. I use the peakmem.sh script to more easily show the peak private memory of cc1plus. Peak private memory and wall time: empty TU 92 functions trunk 476 MB 12.5 s 728 MB 32.7 s patch 1 356 MB 7.8 s 606 MB 25.9 s patches 1+2 149 MB 1.9 s 395 MB 19.5 s With -fpch-instantiate-templates, building the PCH takes 30 s and 1761 MB instead of 21 s and 1363 MB, and it grows from 500 MB to 653 MB. The patches help other headers too. For nlohmann/json 3.12.0 (-std=c++17 -O2), with Clang 23 for comparison ("use" is a 15-line function that parses, modifies and dumps a JSON object): empty TU use GCC trunk, no PCH 182 MB 1.7 s 276 MB 6.0 s Clang 23, no PCH 89 MB 2.7 s 114 MB 5.3 s GCC trunk 75 MB 0.7 s 189 MB 5.2 s GCC patch 1 63 MB 0.6 s 175 MB 5.0 s Clang 23 50 MB 1.4 s 77 MB 3.7 s GCC patches 1+2, with option 11 MB 0.05 s 135 MB 4.4 s Clang 23, with option 8 MB 0.07 s 53 MB 2.8 s LibreOffice saw a significant speed-up from the Clang option, see the discussion that led to it being added: https://lists.llvm.org/pipermail/cfe-dev/2019-May/062371.html Clang can't compile JIT.h, since it was preprocessed with GCC, so to compare with Clang I used an older and larger version of the same header, compiled from its original sources: empty TU 92 functions GCC trunk 856 MB 31 s 1252 MB 84 s Clang 23 433 MB 17 s 713 MB 44 s GCC patches 1+2, with option 290 MB 7 s 781 MB 55 s Clang 23, with option 86 MB 3 s 396 MB 31 s For reference, Clang's PCH is also much smaller: older header nlohmann/json GCC 974 MB 92 MB Clang 23 178 MB 14 MB GCC, with option 1256 MB 119 MB Clang 23, with option 211 MB 19 MB Bootstrapped and regtested on x86_64-pc-linux-gnu (make -k check and check-c++-all), no regressions. Chris Burr (2): ggc: Lay out PCH objects in the order in which they are reached c++: Add -fpch-instantiate-templates gcc/c-family/c-pch.cc | 8 +++ gcc/c-family/c.opt | 4 ++ gcc/c-family/c.opt.urls | 3 + gcc/cp/decl2.cc | 5 ++ gcc/doc/invoke.texi | 31 +++++++++- gcc/ggc-common.cc | 61 +++++++++++++++++-- gcc/ggc.h | 3 + gcc/testsuite/g++.dg/pch/pch-instantiate-1.C | 7 +++ gcc/testsuite/g++.dg/pch/pch-instantiate-1.Hs | 56 +++++++++++++++++ gcc/testsuite/g++.dg/pch/pch-instantiate-2.C | 1 + gcc/testsuite/g++.dg/pch/pch-instantiate-2.Hs | 15 +++++ gcc/testsuite/g++.dg/pch/pch-instantiate-3.C | 11 ++++ gcc/testsuite/g++.dg/pch/pch-instantiate-3.Hs | 14 +++++ gcc/testsuite/g++.dg/pch/pch-instantiate-4.C | 11 ++++ gcc/testsuite/g++.dg/pch/pch-instantiate-4.Hs | 34 +++++++++++ gcc/testsuite/g++.dg/pch/pch-instantiate-5.C | 20 ++++++ gcc/testsuite/g++.dg/pch/pch-instantiate-5.Hs | 22 +++++++ .../g++.dg/template/pch-instantiate-1.C | 22 +++++++ .../g++.dg/template/pch-instantiate-2.C | 21 +++++++ .../g++.dg/template/pch-instantiate-3.C | 19 ++++++ .../g++.dg/template/pch-instantiate-4.C | 24 ++++++++ 21 files changed, 385 insertions(+), 7 deletions(-) create mode 100644 gcc/testsuite/g++.dg/pch/pch-instantiate-1.C create mode 100644 gcc/testsuite/g++.dg/pch/pch-instantiate-1.Hs create mode 100644 gcc/testsuite/g++.dg/pch/pch-instantiate-2.C create mode 100644 gcc/testsuite/g++.dg/pch/pch-instantiate-2.Hs create mode 100644 gcc/testsuite/g++.dg/pch/pch-instantiate-3.C create mode 100644 gcc/testsuite/g++.dg/pch/pch-instantiate-3.Hs create mode 100644 gcc/testsuite/g++.dg/pch/pch-instantiate-4.C create mode 100644 gcc/testsuite/g++.dg/pch/pch-instantiate-4.Hs create mode 100644 gcc/testsuite/g++.dg/pch/pch-instantiate-5.C create mode 100644 gcc/testsuite/g++.dg/pch/pch-instantiate-5.Hs create mode 100644 gcc/testsuite/g++.dg/template/pch-instantiate-1.C create mode 100644 gcc/testsuite/g++.dg/template/pch-instantiate-2.C create mode 100644 gcc/testsuite/g++.dg/template/pch-instantiate-3.C create mode 100644 gcc/testsuite/g++.dg/template/pch-instantiate-4.C -- 2.47.1
