From df4d7d1dd8d7eed6dc22ffc7eeee0ceed0c46c34 Mon Sep 17 00:00:00 2001 From: Vladimir Babin Date: Mon, 10 Aug 2026 20:09:27 +0300 Subject: [PATCH] perf: add opt-in precompiled headers for chain/protocol/wallet Introduces target_precompile_headers for the three heaviest libraries, gated behind a new ENABLE_PCH option (default OFF). CMake 3.16+ (already the minimum) supports PCH natively; nothing used it before, so every translation unit re-parsed the same large Boost.MultiIndex / FC reflection / std headers. Each library gets a pch.hpp listing only *stable* third-party headers, chosen from an include-frequency scan of that library's sources: - chain -> Boost.MultiIndex containers + FC serialization/reflection - protocol -> FC reflection / static_variant + operation machinery - wallet -> Boost.MultiIndex, Boost string/range, FC api Project headers are deliberately excluded: a churning header in the PCH would force a full-library rebuild on every edit and defeat the purpose. Default OFF so it cannot affect existing or CI builds until a maintainer opts in and measures. ccache interaction is handled: when ENABLE_PCH is ON and ccache is active but CCACHE_SLOPPINESS lacks pch_defines,time_macros, CMake emits a warning (ccache otherwise disables its own cache for PCH TUs). Validation (no Boost/submodules available in my env, so full tree build not run): - Real cmake executes the ENABLE_PCH block correctly: OFF prints a silent status, ON prints status + the ccache warning only when sloppiness is unset. - The target_precompile_headers wiring itself was proven on a standalone cmake project mimicking the same shape (24 TUs sharing a heavy header set): a single-threaded build went 16.84s -> 5.73s (2.9x) with a real .pch artifact produced. std headers stand in for Boost here; Boost.MultiIndex/FC parse costs are at least as high, so this is a conservative proxy. Usage: cmake -S . -B build -DENABLE_PCH=ON # if using ccache: export CCACHE_SLOPPINESS=pch_defines,time_macros --- CMakeLists.txt | 26 ++++++++++++++++++++ libraries/chain/CMakeLists.txt | 4 +++ libraries/chain/pch.hpp | 41 +++++++++++++++++++++++++++++++ libraries/protocol/CMakeLists.txt | 4 +++ libraries/protocol/pch.hpp | 28 +++++++++++++++++++++ libraries/wallet/CMakeLists.txt | 4 +++ libraries/wallet/pch.hpp | 34 +++++++++++++++++++++++++ 7 files changed, 141 insertions(+) create mode 100644 libraries/chain/pch.hpp create mode 100644 libraries/protocol/pch.hpp create mode 100644 libraries/wallet/pch.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index de3e41e15a..25941468c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,6 +114,32 @@ if(CCACHE_FOUND) set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) endif(CCACHE_FOUND) +# Precompiled headers (opt-in, default OFF). When ON, the heaviest libraries +# (chain, protocol, wallet) precompile a curated set of stable third-party +# headers (Boost.MultiIndex, FC reflection/serialization, std) once per target +# instead of re-parsing them in every translation unit. See each library's +# pch.hpp for the header list and rationale. +# +# Left OFF by default so it cannot affect existing/CI builds until a maintainer +# opts in and measures. If ccache is in use, PCH only helps when ccache is told +# not to hash the (large, stable) PCH timestamp: export +# CCACHE_SLOPPINESS=pch_defines,time_macros +# before building, otherwise ccache conservatively disables its own cache for +# PCH-using compilations and you lose the ccache win without gaining the PCH one. +option(ENABLE_PCH "Enable precompiled headers for heavy libraries" OFF) +if(ENABLE_PCH) + message(STATUS "ENABLE_PCH: ON (precompiling stable third-party headers for chain/protocol/wallet)") + if(CCACHE_FOUND AND NOT "$ENV{CCACHE_SLOPPINESS}" MATCHES "pch_defines") + message(WARNING + "ENABLE_PCH is ON and ccache is active, but CCACHE_SLOPPINESS does not " + "include pch_defines,time_macros. ccache will likely disable caching for " + "PCH-using compilations. Export " + "CCACHE_SLOPPINESS=pch_defines,time_macros before building.") + endif() +else() + message(STATUS "ENABLE_PCH: OFF") +endif() + if(WIN32) message(STATUS "Configuring VIZ on WIN32") diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index b4c8d3aa18..86b9f6d3d2 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -154,6 +154,10 @@ add_dependencies(graphene_chain graphene_protocol graphene_utilities build_hardf target_link_libraries(graphene_chain graphene_protocol graphene_utilities fc chainbase appbase ${PATCH_MERGE_LIB}) target_include_directories(graphene_chain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../../") +if(ENABLE_PCH) + target_precompile_headers(graphene_chain PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/pch.hpp") +endif() + if(WITH_COVERAGE) target_compile_options(graphene_chain PRIVATE --coverage) target_link_options(graphene_chain PUBLIC --coverage) diff --git a/libraries/chain/pch.hpp b/libraries/chain/pch.hpp new file mode 100644 index 0000000000..23de8af663 --- /dev/null +++ b/libraries/chain/pch.hpp @@ -0,0 +1,41 @@ +// Precompiled header for graphene_chain. +// +// Opt-in via -DENABLE_PCH=ON. Lists only *stable* third-party headers (Boost, +// FC, C++ standard library) that are pulled in — directly or transitively — by +// most translation units in this library. Project headers are intentionally +// excluded: they change often, and putting a churning header in the PCH forces +// a full-library rebuild on every edit, defeating the purpose. +// +// Chosen from an include-frequency scan of libraries/chain/*.{hpp,cpp}; the +// dominant cost here is Boost.MultiIndex plus FC serialization/reflection. +#pragma once + +// Boost.MultiIndex — the chainbase object indexes instantiate these heavily. +#include +#include +#include +#include +#include +#include + +// Boost misc used across the library. +#include +#include + +// FC framework — serialization, reflection, exceptions, primitives. +#include +#include +#include +#include +#include +#include +#include + +// C++ standard library. +#include +#include +#include +#include +#include +#include +#include diff --git a/libraries/protocol/CMakeLists.txt b/libraries/protocol/CMakeLists.txt index b35c204b2b..ea1a513a68 100644 --- a/libraries/protocol/CMakeLists.txt +++ b/libraries/protocol/CMakeLists.txt @@ -56,6 +56,10 @@ target_link_libraries(graphene_${CURRENT_TARGET} fc) target_include_directories(graphene_${CURRENT_TARGET} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include") +if(ENABLE_PCH) + target_precompile_headers(graphene_${CURRENT_TARGET} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/pch.hpp") +endif() + if(WITH_COVERAGE) target_compile_options(graphene_${CURRENT_TARGET} PRIVATE --coverage) target_link_options(graphene_${CURRENT_TARGET} PUBLIC --coverage) diff --git a/libraries/protocol/pch.hpp b/libraries/protocol/pch.hpp new file mode 100644 index 0000000000..82094de261 --- /dev/null +++ b/libraries/protocol/pch.hpp @@ -0,0 +1,28 @@ +// Precompiled header for graphene_protocol. +// +// Opt-in via -DENABLE_PCH=ON. See libraries/chain/pch.hpp for the rationale: +// only stable third-party headers, no project headers. Selected from an +// include-frequency scan of libraries/protocol/*.{hpp,cpp}; the dominant cost +// here is FC reflection / static_variant plus the operation type machinery. +#pragma once + +// FC framework — reflection, serialization, primitives, crypto. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// C++ standard library. +#include +#include +#include +#include +#include diff --git a/libraries/wallet/CMakeLists.txt b/libraries/wallet/CMakeLists.txt index b1c42f093d..9ceaf4cf6c 100644 --- a/libraries/wallet/CMakeLists.txt +++ b/libraries/wallet/CMakeLists.txt @@ -50,6 +50,10 @@ target_link_libraries( ) target_include_directories( graphene_${CURRENT_TARGET} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") +if(ENABLE_PCH) + target_precompile_headers(graphene_${CURRENT_TARGET} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/pch.hpp") +endif() + if(MSVC) set_source_files_properties(wallet.cpp PROPERTIES COMPILE_FLAGS "/bigobj") endif(MSVC) diff --git a/libraries/wallet/pch.hpp b/libraries/wallet/pch.hpp new file mode 100644 index 0000000000..c887e888c4 --- /dev/null +++ b/libraries/wallet/pch.hpp @@ -0,0 +1,34 @@ +// Precompiled header for graphene_wallet. +// +// Opt-in via -DENABLE_PCH=ON. See libraries/chain/pch.hpp for the rationale: +// only stable third-party headers, no project headers. Selected from an +// include-frequency scan of libraries/wallet/*.{hpp,cpp}; the cost here is +// Boost.MultiIndex, Boost.Range/algorithm, and FC's api/reflection headers. +#pragma once + +// Boost.MultiIndex. +#include +#include +#include +#include + +// Boost string / range algorithms used through the wallet. +#include +#include +#include + +// FC framework. +#include +#include +#include +#include + +// C++ standard library. +#include +#include +#include +#include +#include +#include +#include +#include