Skip to content

[opt](build) Cut three more waves of hot include edges in the BE header graph - #66672

Open
morningman wants to merge 11 commits into
apache:masterfrom
morningman:be-build-opt-1-include-cuts
Open

[opt](build) Cut three more waves of hot include edges in the BE header graph#66672
morningman wants to merge 11 commits into
apache:masterfrom
morningman:be-build-opt-1-include-cuts

Conversation

@morningman

@morningman morningman commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Split out of #66510, which carries the whole
BE build-time batch. This PR continues the header-closure surgery merged as
#66400 with three more waves, and is independent of the rest of that batch —
it can be reviewed and merged on its own.

What problem does this PR solve?

Related PR: #66400, #66510

Problem Summary:

Three waves of include-edge surgery in the BE header graph, each in the same
"seed first, then cut" shape as #66400: one purely additive commit that outlines
the inline bodies forcing the instantiation and seeds the direct includes the cut
will expose, then one commit that deletes the edges and pins them with new
build-support/check-header-deps.py rules.

Wave 1 — three hot edges (Prepare cutting three hot edges + Cut three hot include edges)

edge cut why it cost so much how it is made removable
core/pod_array.hruntime/thread_context.h dead include left over from the PODArray memory-tracking experiment (#50549); the tracking logic has since moved into Allocator and pod_array.h names no thread_context symbol just delete it — 203 TUs stop seeing thread_context.h and 195 of them stop seeing exec_env.h (1.37 MB/TU differential payload)
core/column/column.hexec/sort/hybrid_sorter.h a coreexec layering violation reaching 808 TUs HybridSorter only appears in virtual signatures, so a forward declaration covers it; the BE_TEST-only get_permutation_default body (it constructs a HybridSorter by value) moves to column.cpp
core/wide_integer_impl.hboost/multiprecision (unconditional) 4.27 MB of preprocessed closure in 1169 TUs on every platform without an 80-bit long double the from-double members stay inline (and constexpr) only where LDBL_MANT_DIG == 64; elsewhere they compile once in a new core/wide_integer_from_double.cpp with explicit instantiations for integer<128|256, signed|unsigned>. They were never constexpr on those platforms, so no constant evaluation is lost

Preprocessed closure of column.h: 16.72 MB → 10.15 MB (-39%).

Wave 2 — two instantiation amplifiers: RLE and FMT_COMPILE

Both parquet decoder.h trees included util/rle_encoding.h only because inline
BaseDictDecoder bodies made ~530 TUs instantiate the
RleBatchDecoder<uint32_t>GetLiteralValuesUnpackBatchUnpackValues
chain — measured at 0.43 CPU s per TU, 231 CPU s total — on top of reparsing
the 2038-line rle/bit-stream/bit-packing family each time. The eight per-page /
per-batch methods plus the ctor and dtor move out of line, so the
unique_ptr<RleBatchDecoder<uint32_t>> member only needs the complete type in
decoder.cpp. Everything moved is dispatched through the vtable at every call
site already, so no generated call changes; the real decode TUs keep including
rle_encoding.h directly and inline the chain exactly as before, and the
per-value-hot LevelDecoder::get_next path is deliberately untouched.

core/uint24.h and core/value/large_int_value.h pushed their FMT_COMPILE
formatter instantiations (the "{:04d}-{:02d}-{:02d}" date formatter is the
single biggest fmt instantiation in the tree, plus the int128 formatters) into
~1150 TUs, 53.5 CPU s. Those bodies move to .cpp files and both headers
drop <fmt/compile.h> / <fmt/format.h>. They return std::string and are
allocation-dominated, so the now-outlined call is noise.

Wave 3 — the DataVariants amplifier behind dependency.h

exec/pipeline/dependency.h and exec/pipeline/rec_cte_shared_state.h hold
non-template in-class inline bodies — SharedState constructors, destructors,
close paths and three std::visit dispatches — that name the full
AggregatedDataVariants / JoinDataVariants / SetDataVariants /
DistinctDataVariants surface. Such bodies are semantically analyzed when the
header is parsed
, not when they are called, so every one of the ~128 TUs that
transitively include dependency.h instantiated that whole surface at a flat
~0.85 CPU s per TU (~130 CPU s) — and 105 of those TUs never touch a variant.

All of it is per-query setup/teardown, so it moves to dependency.cpp and a new
rec_cte_shared_state.cpp. With the bodies gone the headers can drop:

  • exec/common/agg_utils.h, set_utils.h, distinct_agg_utils.h — forward declarations suffice;
  • exec/common/join_utils.h → the new light exec/common/join_op_utils.h (JoinOpVariants and the AsofIndexGroup family split out of join_utils.h, which re-exports them; dependency.h holds these by value, and the new header depends only on thrift enums, pdqsort and std containers — not on the hash tables);
  • exec/operator/join/process_hash_table_probe.h — dead include;
  • util/brpc_closure.h — dead include, and the sole route carrying query_context.h, thread_context.h and service/brpc.h into ~100 pipeline TUs (1.36 MB/TU);
  • <concurrentqueue.h> — dead include (152 KB third-party single header);
  • util/brpc_client_cache.h from rec_cte_shared_state.h — the rpc send bodies live in the .cpp now.

BucketedAggSharedState::init_instances also becomes a non-template taking
std::function: non-dependent constructs in a member-template body are checked
at definition time, so the old inline template forced the destructor of
unique_ptr<BucketedAggDataVariants> on every includer despite never being
called there.

One CI-critical commit rides along

Opt wide_integer_from_double.cpp out of the PCH must ship in this PR, not as
a follow-up. Upstream's clang toolchain defaults to ENABLE_PCH=ON
(be/CMakeLists.txt), and pch.h transitively includes wide_integer_impl.h,
whose include guard is then already consumed when the impl TU is compiled — the
explicit instantiations would find no definition. It is the only file in the tree
with this interaction, but splitting the two commits apart would leave a state
that fails the clang CI lane.

Benefit

Compile-time only; no runtime behavior change.

Head-to-head on exactly this PR's content

Cold, cache-free BE builds of this PR's merge base (c29075a7e10) and its head
(03129023ee5), run back-to-back on the same machine with the repo's own
--compile-bench harness — dedicated always-cold build dirs, ccache disabled,
-j10, ENABLE_PCH=ON, and both runs started from the same cooled state
(load1 2.1 vs 2.3) so neither side pays for the other's heat.

metric before after delta
build phase wall 14m50s 14m14s -36.3 s (-4.1%)
Σ TU wall time (parallelism-independent) 2h19m 2h13m -6 min (-4.3%)
Σ TU cpu (user+sys) 2h18m 2h12m -6 min
effective parallelism 9.4× 9.4× identical ⇒ clean attribution
per-file wall 197 improved / 12 regressed / 3 new

The improvements land exactly on the predicted targets — pipeline_fragment_context.cpp
-6.0 s, rec_cte_anchor_sink_operator.cpp -5.0 s, operator.cpp -3.7 s,
data_queue.cpp -3.0 s (all W3 dependency.h consumers), and
byte_array_dict_decoder.cpp -2.5 s (the W2 decoder edge).

Of the 12 regressions, dependency.cpp +2.6 s is by design: that is the TU the
SharedState bodies were moved into, so it pays once for what ~128 TUs stop paying.
The rest sit in the noise floor of a -j10 run, and there is direct evidence for
that floor: gensrc/build/gen_cpp/cloud.pb.cc, a generated protobuf TU this PR
cannot touch, moved +2.3 s between the two runs.

Why no end-to-end total-wall number is quoted: the two trees differed outside
the build phase — the baseline tree skipped the contrib submodule step while the PR
tree re-fetched it (+58.1 s), and its gensrc was already generated (-5.6 s). Those
phases are not compilation, so only the build-phase and Σ-TU figures above are
attributable to the change.

One caveat worth stating: these numbers are with ENABLE_PCH=ON. A PCH already
amortizes exactly the kind of shared headers this PR is cutting, so it masks part of
the win — and the upstream compile lane builds without a PCH (its command line
carries no -include-pch). The effect there should be larger, not smaller.

Per-wave numbers from the development branch

wave effect measurement
W1 -24.3 s wall, be/src CPU -3.6% same-codebase A/B on the batch branch
W2 -125 s / -6.9% (30m24s → 28m19s) same-codebase A/B, back-to-back
W3 ~130 CPU s of parse-time instantiation removed (~0.85 CPU s × ~128 TUs), plus 1.36 MB/TU × ~100 TUs of dead brpc payload P3.5 research traces (per-TU CPU, not end-to-end wall)

These were taken on the batch branch at -j5/-j6 against an older base, so they
do not add up to the head-to-head figure above; they are included because they
attribute the win to each wave separately.

Risk and verification

  • Header sweeps. Every wave was validated with a full -fsyntax-only sweep over the natural (no-PCH) include closure: W1 1358 TUs with only the 4 known pre-existing failures, W2 1364/1364 clean, W3 0 failing of 1365.
  • BE unit tests. The first sweeps covered be/src only, so a full ninja -k 0 over 2422 targets was run to reach the 1024 be/test TUs; it surfaced 8 failing TUs in 4 families, all repaired in Repair the BE UT build after the include-edge cuts. Nothing there restores a cut edge and no production header gains an include. Two of those repairs fix a latent defect that predates this PR: BaseDictDecoder's defaulted-in-class constructor odr-uses ~unique_ptr<RleBatchDecoder<uint32_t>> in every TU constructing a derived decoder, so the header's own claim that the complete type is only needed in decoder.cpp did not hold — in both trees, though only the format/ one had a test reaching it.
  • Rebased onto current master (c29075a7e10) and rebuilt from scratch in a clean worktree, macOS/arm64 + clang 20, ENABLE_PCH=ON (upstream's clang default, so the PCH opt-out above is exercised): 8554/8554 ninja edges, zero failures, doris_be links. compile_commands.json confirms wide_integer_from_double.cpp is the one first-party TU compiled without the PCH.
  • BE unit tests rebuilt on the rebased tree: all 9602 objects compile, zero failures, and the final link resolves every symbol (0 undefined). Two pre-existing macOS-only obstacles were hit on the way and are called out under Proactive disclosure.
  • Natural-closure sweep (syntax_sweep.py --no-pch, the gate added in [opt](build) Add build-timing and header-closure sweep tooling #66616): 0 failing TUs of 1380. This is the gate that matters for a PR that cuts include edges — a normal build with ENABLE_PCH=ON cannot see a missing include that the PCH happens to supply, which is precisely how the <ranges> breakage in the last commit reached CI before it reached me.
  • Runtime cost of outlining. Everything moved out of line is either per-query setup/teardown (SharedState ctor/dtor/close), or a per-page/per-batch method already reached through a vtable. No per-value or per-row hot path was outlined.
  • Guard rules. check-header-deps.py gains rules pinning pod_array.h !-> thread_context.h, column.h !-> exec/sort/, and both decoder.h headers !-> util/rle_encoding.h (19 rules total, all passing).

Proactive disclosure

  • One commit here is unrelated to include edges: Make hierarchical_data_iterator_test compile on macOS arm64. std::min(*rows, ROWS - current_ordinal) cannot deduce _Tp where size_t is unsigned long and ordinal_t (uint64_t) is unsigned long long, which is the case on macOS/arm64 but not on Linux — so CI is green while be/test does not build on macOS at all. It arrived with [feature](variant) Support ColumnVariantV2 segment reads and writes #66204. It is fixed here because this branch is verified on macOS and the break blocks that verification; happy to split it out if a reviewer prefers.
  • The macOS Debug UT link has outgrown the Mach-O format, independently of this PR: section __debug_names's file offset exceeds 4GB. Master's own test growth is what crossed the line — the same worktree layout linked fine on 2026-08-10 at 7.6 GB of test debug info, and master is now at 8.0 GB across 10 more test TUs, while this PR adds one #include to each of 5 test files. Omitting the debug map (-Wl,-S) links the binary cleanly with zero undefined symbols, which is how the link was verified here. Worth someone's attention as a separate issue.
  • Cross-platform is the blind spot. All measurements and all sweeps ran on macOS/arm64 + clang 20. Nothing here is platform-specific by construction, but the Linux gcc/clang lanes are covered only by upstream CI, not by any local gate — please give those two lanes a look.
  • Two cuts have no scanner rule. The boost/multiprecision edge is preprocessor-gated and check-header-deps.py is preprocessor-blind (it would flag the impl TU's gated include); the macro structure is self-guarding instead — breaking it fails the impl TU's build. The fmt cuts are likewise only noted in a comment, since the scanner follows quoted project includes.
  • Three test files carry clang-format off/on around their includes. asof_join_test and the two fix_length_dict_decoder tests are include-order-sensitive: the supplying include has to come before the header under test (ADL cannot reach the global pdqsort from std::vector's iterators; a unique_ptr<RleBatchDecoder<uint32_t>> dereference depends on no template parameter, so it binds where the template is parsed). Without the marker the formatter sorts the include back and breaks the build. The cost was deliberately kept in the tests rather than paid by a production header.
  • DISALLOW_COPY_AND_ASSIGN in storage/olap_define.h loses its trailing semicolon. butil/macros.h defines the same macro without one and wins under #ifndef in TUs that see butil first, so the two expansions have to stay call-site compatible. All 45 call sites in the tree already write the ;.

Release note

None

Check List (For Author)

  • Test

    • No need to test or manual test. Explain why:
      • This is a refactor/code format and no logic has been changed.
      • Previous test can cover this change. (full BE UT build + link, and the -fsyntax-only sweeps above)
  • Behavior changed:

    • No.
  • Does this need documentation?

    • No.

morningman and others added 9 commits August 11, 2026 22:57
Pure-additive / behavior-preserving preparation so that three include edges
can be cut next: core/pod_array.h -> runtime/thread_context.h (dead include),
core/column/column.h -> exec/sort/hybrid_sorter.h (core->exec layering
violation reaching 808 TUs) and wide_integer_impl.h's unconditional
boost/multiprecision include (4.27MB of preprocessed closure in 1169 TUs on
platforms without an 80-bit long double). No include edge is removed here:

- core/wide_integer_from_double.cpp (new): compiles the from-double
  conversion once and explicitly instantiates it for
  integer<128|256, signed|unsigned>; harmless while the header still defines
  the members inline
- column.h: forward-declare HybridSorter; move the BE_TEST-only
  get_permutation_default body to column.cpp (it constructs HybridSorter by
  value); the production get_permutation default keeps its inline throw body
- column_decimal.h: move the permutation<U> helper body to column_decimal.cpp
  (its only caller, get_permutation, is already defined there); member
  templates are instantiated by that call, not by the class-level explicit
  instantiations
- column_const.h: include <span> directly (rode in via hybrid_sorter.h)
- storage/olap_define.h: drop the trailing semicolon inside
  DISALLOW_COPY_AND_ASSIGN so the call sites' `;` completes the expansion:
  butil/macros.h defines the same macro without one and wins under #ifndef
  in TUs that happen to see butil first, so the two definitions must stay
  call-site compatible
- format_v2/native/native_reader.h: include runtime/runtime_profile.h
  (RuntimeProfile::Counter members need the complete type; it rode in via
  pod_array -> thread_context)
- bm25_similarity.cpp: drop the dead `using namespace inverted_index` (the
  only declaration of that namespace lives in exec_env.h and rode the same
  edge)
- inverted_index_searcher.h, index_file_writer.h, ann_index_writer.h,
  query/query.h: wrap their bare <CLucene.h> includes in the -Wconversion
  suppression (inverted_index_common_impl.h pattern); whether CLucene's
  first expansion lands inside a suppressed region depends on include order

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit c2bf234)
…nteger

- core/pod_array.h no longer includes runtime/thread_context.h: dead include
  left over from the PODArray memory-tracking experiment (apache#50549); the
  tracking logic has since moved into Allocator and pod_array.h references
  no thread_context symbol. 203 TUs stop seeing thread_context.h and 195 of
  them stop seeing exec_env.h (differential payload 1.37MB per TU).
- core/column/column.h no longer includes exec/sort/hybrid_sorter.h
  (core -> exec layering violation): HybridSorter only appears in virtual
  signatures, which the forward declaration covers; pdqsort/timsort leave
  808 TUs.
- wide_integer_impl.h defines the from-double members (set_multiplier /
  wide_integer_from_builtin(double)) inline only where the 80-bit long
  double exists (LDBL_MANT_DIG == 64 — unchanged and still constexpr there)
  or inside the dedicated impl TU core/wide_integer_from_double.cpp; every
  other TU sees declarations only and drops boost/multiprecision +
  boost/math from its closure (4.27MB preprocessed x 1169 TUs; the members
  were never constexpr on these platforms, so no constant evaluation is
  lost).
- check-header-deps.py: two new rules (pod_array.h !-> thread_context.h,
  column.h !-> exec/sort/). No formal rule for the boost edge: the scanner
  is preprocessor-blind and would flag the impl TU's gated include; the
  macro structure is self-guarding (breaking it fails the impl TU build).

Preprocessed closure of column.h: 16.72MB -> 10.15MB (-39%).
Syntax sweep: 1358 TUs; failures identical to the 4 known pre-existing
merge artifacts (stale gensrc x3, apache#66242 template-only call site), zero new.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 13fca3d)
…iers

Pure-additive / behavior-preserving preparation so that two kinds of
template-instantiation edges can be cut next: both parquet decoder.h
headers' util/rle_encoding.h include (their inline BaseDictDecoder bodies
force ~530 TUs to instantiate the RleBatchDecoder<uint32_t> ->
GetLiteralValues -> UnpackBatch -> UnpackValues chain at ~0.43 CPU s per
TU, 231 CPU s total) and the FMT_COMPILE formatter instantiations that
uint24.h / large_int_value.h push into ~1150 TUs (53.5 CPU s). No include
edge is removed here:

- format/parquet/decoder.h: move set_data, skip_values and the defaulted
  ~BaseDictDecoder out of line into decoder.cpp; everything moved is a
  per-page/per-batch virtual call that is dispatched through the vtable at
  every call site already, so outlining changes no generated call. With the
  dtor out of line the unique_ptr<RleBatchDecoder<uint32_t>> member no
  longer needs the complete type in the header; forward-declare it
- format_v2/parquet/reader/native/decoder.h: same treatment for set_data,
  decode_dictionary_indices, decode_selected_dictionary_indices,
  _decode_fragmented_selection, _decode_and_validate_skipped,
  _decode_dictionary_values and the dtor; the pure orchestrator bodies
  (skip_values, decode_dictionary_values, decode_selected_dictionary_values)
  stay inline as they only call the now-declared members
- decoder.cpp (both trees): include util/rle_encoding.h and
  common/cast_set.h directly (cast_set previously rode in through
  rle_encoding.h)
- core/uint24.h: move to_string (the FMT_COMPILE("{:04d}-{:02d}-{:02d}")
  date formatter, the single biggest fmt instantiation in the codebase)
  into a new core/uint24.cpp; a std::string-returning formatter is
  allocation-dominated, so the outlined call is noise
- core/value/large_int_value.h: move to_buffer/to_string x2 (the
  FMT_COMPILE int128 formatters) into large_int_value.cpp, which now
  includes fmt/compile.h + fmt/format.h directly instead of riding its own
  header

The RleBatchDecoder methods stay fully inline in rle_encoding.h for the
real decode TUs; the per-value-hot LevelDecoder::get_next path is
deliberately untouched.

(cherry picked from commit df9ec8f)
The one-line deletions the previous commit prepared, plus the guard rules
that lock them in:

- format/parquet/decoder.h and format_v2/parquet/reader/native/decoder.h
  drop util/rle_encoding.h (RleBatchDecoder<uint32_t> is forward-declared;
  the complete type is only needed by decoder.cpp). New trace run
  20260805_131321 measured the old edges at 531 TUs x 435 ms = 231 CPU s
  of RleBatchDecoder/BitPacking/BatchedBitReader instantiation, plus the
  2038-line rle_encoding/bit_stream_utils/bit_packing family reparsed in
  each of those TUs. The real decode TUs keep including rle_encoding.h
  directly and inline the chain exactly as before
- core/uint24.h drops <fmt/compile.h> and core/value/large_int_value.h
  drops <fmt/compile.h> + <fmt/format.h>: their formatter bodies moved to
  the matching .cpp files, which ends the FMT_COMPILE date/int128 formatter
  instantiation (53.5 CPU s over ~1150 TUs) in every includer
- check-header-deps.py: two new rules pin both decoder.h headers away from
  util/rle_encoding.h (19 rules total, all passing); the fmt cuts are noted
  in a comment because the scanner only follows quoted project includes

Full -fsyntax-only sweep: 1364/1364 TUs clean with the cuts applied (the
single failure during arbitration was large_int_value.cpp riding its own
header for fmt, fixed in the preparation commit).

(cherry picked from commit 624d039)
The four cut-edge commits on this branch were validated with a -fsyntax-only
sweep over be/src only, so the 1024 be/test TUs were never compiled against the
slimmed graph. A full `ninja -k 0` run (2422 targets) surfaced 8 failing TUs in
4 families. Nothing here restores a cut edge, and no production header gains an
include; the RLE fix in fact tightens the invariant decoder.h already claimed.

- core/wide_integer_from_double.cpp: -Werror,-Wunused-macros. Where the 80-bit
  long double exists, wide_integer_impl.h takes its `#if (LDBL_MANT_DIG == 64)`
  branch and never evaluates the `#elif defined(...)` that reads
  DORIS_WIDE_INTEGER_FROM_DOUBLE_IMPL_TU, so the definition looked unused. The
  existing instantiation guard now reads the macro itself. `defined(...)` must
  stay the left operand -- the preprocessor short-circuits `&&`, so a false left
  term skips it and the warning returns.
- format/parquet/decoder.h and format_v2/parquet/reader/native/decoder.h: outline
  BaseDictDecoder's default ctor next to the dtor that was already outlined. A
  defaulted-in-class ctor is defined in every TU that constructs a derived
  decoder and it odr-uses ~unique_ptr<RleBatchDecoder<uint32_t>>, so the header's
  own comment -- that the complete type is only needed in decoder.cpp -- did not
  hold. Both trees had the defect; only the format/ one had a test reaching it.
- block_test / column_variant_v2_test: declare the RuntimeProfile and
  HybridSorter they use directly. They had been riding
  pod_array.h -> thread_context.h and column.h -> exec/sort/hybrid_sorter.h.
- asof_join_test and the two fix_length_dict_decoder tests: include-order-
  sensitive, so the cost stays in the tests rather than in a production header.
  AsofIndexGroup::sort_and_finalize calls the global pdqsort, which ADL cannot
  reach from std::vector's iterators; FixLengthDictDecoder::_decode_values
  dereferences a unique_ptr<RleBatchDecoder<uint32_t>> that depends on no
  template parameter. Both bind where the template is parsed, not where it is
  instantiated, so the supplying include has to come first. The two decoder tests
  carry clang-format off/on because the formatter would otherwise sort the
  include back after the header under test and break the build again.

Verified: `sh run-be-ut.sh` links test/doris_be_test with 0 failures; all 10
files pass clang-format --dry-run --Werror.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3BVeKrPS3jQ2bagcuGHBQ
(cherry picked from commit f8b01ae)
Pure-additive / behavior-preserving preparation so that the hash-table
variant machinery (exec/common/{agg,join,set,distinct_agg}_utils.h) can be
cut out of exec/pipeline/dependency.h and rec_cte_shared_state.h next.
Those two headers hold non-template in-class inline bodies (SharedState
constructors/destructors/close paths, three std::visit dispatches) that
reference the full variant types, and such bodies are semantically
analyzed when the header is parsed: every one of the ~128 TUs that
transitively include dependency.h instantiates the whole
AggregatedDataVariants/JoinDataVariants/SetDataVariants surface at a flat
~0.85 CPU s per TU (~130 CPU s total), whether it uses them or not. No
include edge is removed here:

- dependency.h: move out of line into dependency.cpp everything whose body
  touches a variant -- AggSharedState ctor/dtor/_close_with_serialized_key/
  _close_without_key, BucketedAggSharedState PerInstanceData ctor, dtor,
  _close and _close_one_agg_data, both HashJoinSharedState ctors, and a
  new user-declared SetSharedState ctor/dtor pair that takes over the
  make_unique<SetDataVariants>() default member initializer. All of it is
  per-query setup/teardown, so outlining costs nothing at runtime
- dependency.h: forward-declare the variant structs, spell the members as
  plain std::unique_ptr/std::vector<size_t> instead of the utils-provided
  aliases, redeclare AggregateDataPtr, and directly include the light
  dependencies that used to ride in through the utils chain (core/arena.h,
  exprs/vexpr_fwd.h, util/stopwatch.hpp, cast_set/exception/
  factory_creator, <list>/<queue>/<set>)
- exec/common/join_op_utils.h (new): JoinOpVariants, is_asof_join*, and
  the AsofIndexGroup/AsofIndexVariant family move here out of
  join_utils.h, which re-exports it; dependency.h holds these by value,
  and this light header depends only on thrift enums, pdqsort and std
  containers -- not on the hash tables
- rec_cte_shared_state.h: same treatment for the fourth amplifier; the
  emplace_block std::visit over DistinctDataVariants plus the brpc-heavy
  build_basic_param/send_data_to_targets bodies move to a new
  rec_cte_shared_state.cpp (CMake GLOB picks it up), with user-declared
  ctor/dtor so unique_ptr<DistinctDataVariants> works forward-declared
- seed direct includes ahead of the cut: agg_utils.h into the three agg
  operator headers whose inline code walks method_variant, join_utils.h
  into join_build_sink_operator.h, distinct_agg_utils.h into
  rec_cte_source_operator.cpp, and the utils/template_helpers includes
  into dependency.cpp

Verified with a full -fsyntax-only sweep: 0 failing TU(s) of 1365.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit e39c032)
…ncy.h

With the SharedState bodies out of line (previous commit), drop the edges
that made every pipeline TU pay for the hash-table variant machinery and
two dead payload includes. Measured on the P3.5 research traces: a flat
~0.85 CPU s of parse-time template instantiation per TU across the ~128
TUs reached by dependency.h (105 of which never touch a variant), plus
1.36 MB/TU of preprocessed brpc/query_context payload riding the dead
brpc_closure.h edge into ~100 TUs.

Edges cut from exec/pipeline/dependency.h:
- exec/common/agg_utils.h, set_utils.h: forward declarations suffice now
- exec/common/join_utils.h -> exec/common/join_op_utils.h: the by-value
  JoinOpVariants/AsofIndexVariant members need complete types, so keep
  only the light join-op header split out in the previous commit
- exec/operator/join/process_hash_table_probe.h: dead include (no symbol
  of it is referenced here); the probe machinery belongs to the join TUs
- util/brpc_closure.h: dead include; it was the only route that carried
  runtime/query_context.h, runtime/thread_context.h and service/brpc.h
  into ~100 pipeline TUs
- <concurrentqueue.h>: dead include (152 KB third-party single header);
  the moodycamel users include it themselves
Also make BucketedAggSharedState::init_instances a non-template taking
std::function (defined in dependency.cpp): non-dependent constructs in a
member-template body are checked at definition time, so the old inline
template still forced the destructor of
unique_ptr<BucketedAggDataVariants> on every includer despite never
being called there. Once-per-query cold path.

exec/pipeline/rec_cte_shared_state.h (fourth amplifier, same recipe):
- cut exec/common/distinct_agg_utils.h (DistinctDataVariants is only
  touched by rec_cte_shared_state.cpp now) and util/brpc_client_cache.h
  (the rpc send bodies live in the .cpp)

Seeds arbitrated by two full -fsyntax-only sweeps (16 true positives):
- process_hash_table_probe_impl.h includes join_utils.h (the
  INSTANTIATION_FOR macro names SerializedHashTableContext and friends,
  fixing all 12 *_join_impl.cpp TUs); process_hash_table_probe.h becomes
  self-contained (runtime_profile.h for its Counter members)
- hashjoin_probe_operator.h includes process_hash_table_probe.h (the
  by-value std::variant<ProcessHashTableProbe<...>> needs the definition)
- direct utils/hash_map_util includes for the TUs that dereference the
  variants: hashjoin_probe_operator.cpp, hashjoin_build_sink.cpp, the
  three set operator cpps, the three aggregation source cpps
- materialization_opertor.h includes service/brpc.h for its
  brpc::Controller member; local_exchanger.h includes concurrentqueue.h
  for its ConcurrentQueue members

Guards: check-header-deps.py gains five rules (24 total, all passing):
dependency.h and rec_cte_shared_state.h must not reach
exec/common/hash_table/ (phmap_fwd_decl.h excepted), dependency.h must
not regain process_hash_table_probe.h / brpc_closure.h, and
rec_cte_shared_state.h must not regain brpc_client_cache.h; the
concurrentqueue.h ban is recorded as a comment (third-party angle
include, outside the scanner's reach).

Verified: full -fsyntax-only sweep 0 failing of 1365; header layering
24/24.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 82d8ce7)
The PCH already pulls in wide_integer_impl.h (via storage/olap_common.h) in
declaration-only mode, so its pre-spent include guard would leave the
from-double explicit instantiations in this TU without definitions. Skip the
PCH for this single TU so DORIS_WIDE_INTEGER_FROM_DOUBLE_IMPL_TU takes effect.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit ae1f7b1)
`std::min(*rows, ROWS - _state->current_ordinal)` fails template argument
deduction wherever size_t and ordinal_t are distinct types: on macOS/arm64
size_t is `unsigned long` while uint64_t -- and hence ordinal_t -- is
`unsigned long long`, so _Tp is deduced conflictingly and none of the four
std::min overloads match. On Linux the two spell the same type, which is why
CI never saw it.

Name the template argument explicitly at the three call sites. Both types are
64-bit unsigned, so nothing is narrowed.

The break arrived with apache#66204 and has nothing to do with the include-edge cuts
on this branch, but it stops be/test from building on macOS at all -- which is
where this branch is verified -- so it is fixed here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gdfkk7RqgD5e3Uv7bTM3NV
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

1 similar comment
@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@morningman
morningman requested a review from luwei16 as a code owner August 12, 2026 00:16
@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

…ply it

`column.h -> exec/sort/hybrid_sorter.h` was carrying `gfx/timsort.hpp`, whose
line 37 is `#include <ranges>`. Four files consumed `<ranges>` through that
chain without ever asking for it, and the previous commit's cut takes it away.

Why the local build did not see this: it runs with `ENABLE_PCH=ON`, and
`cmake_pch.hxx` supplies `<ranges>` to every TU regardless of that TU's own
includes, so the debt stays invisible. The upstream compile lane builds without
a PCH, where nothing covers for it. Compiling the same file on macOS with the
PCH genuinely stripped reproduces the identical error, so this is not a
libc++/libstdc++ difference -- it is the PCH masking a missing include, exactly
the failure mode `syntax_sweep.py --no-pch` exists to expose.

- storage/tablet/base_tablet.cpp: `std::views::transform` / `std::views::filter`
  -- this is the one the CI compile lane caught.
- cloud/cloud_meta_mgr.{h,cpp}: three declarations and three definitions
  constrained on `std::ranges::range auto&&`. The Cloud target is compiled
  after Storage, so the build stopped before reaching it; it would have failed
  next.
- storage/compaction/compaction.cpp: `std::ranges::reverse_view`. This one still
  compiles, because simdjson.h happens to remain in its closure and also pulls
  `<ranges>` -- included here so it stops depending on that accident.

Found by diffing each consumer's include closure before and after the cut and
matching it against the symbols the removed providers supply, rather than by
waiting for another CI round trip.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gdfkk7RqgD5e3Uv7bTM3NV
@morningman
morningman force-pushed the be-build-opt-1-include-cuts branch from 06351a4 to 0312902 Compare August 12, 2026 00:22
@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

…::agg_data

GCC rejects the forward-declared `unique_ptr<DistinctDataVariants>` member that
the previous commits introduced, in every TU that merely includes
rec_cte_shared_state.h:

    unique_ptr.h:91:23: error: invalid application of 'sizeof' to incomplete
                        type 'doris::DistinctDataVariants'
    required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr()'
    rec_cte_shared_state.h:42:54: required from here

Column 54 is the `nullptr` of `agg_data = nullptr`. A default member
initializer is part of the class definition, so GCC instantiates the member's
destructor while completing the class -- which needs `DistinctDataVariants` to
be complete -- even though the constructor and destructor of RecCTESharedState
are both defined out of line in rec_cte_shared_state.cpp, where it is complete.
Clang defers that instantiation to the point the constructor is actually
defined, which is why the clang lanes and the macOS build were green while the
gcc lane failed on rec_cte_anchor_sink_operator.cpp and rec_cte_sink_operator.cpp.

`= nullptr` was redundant to begin with: `unique_ptr`'s default constructor
already leaves it null, and `RecCTESharedState::RecCTESharedState() = default;`
in the .cpp performs that initialization where the type is complete. So dropping
the initializer changes no behavior and removes the only instantiation point
that required completeness in the header.

Swept the rest of the tree for the same shape: the only other
`unique_ptr<DistinctDataVariants>` with an initializer is in
distinct_streaming_aggregation_operator.h, which includes distinct_agg_utils.h
directly and therefore has the complete type.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gdfkk7RqgD5e3Uv7bTM3NV
@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

1 similar comment
@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28728 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 5adf59ef9ecf14250f9f9435258a91709eb5c648, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17569	3979	3947	3947
q2	2005	337	203	203
q3	10284	1419	794	794
q4	4688	475	340	340
q5	7546	845	553	553
q6	202	173	138	138
q7	733	792	599	599
q8	10141	1339	1331	1331
q9	5427	4059	4026	4026
q10	6790	1622	1358	1358
q11	497	358	329	329
q12	741	563	462	462
q13	18090	3392	2787	2787
q14	263	255	240	240
q15	q16	744	735	661	661
q17	1040	972	979	972
q18	6595	5661	5535	5535
q19	1210	1276	1122	1122
q20	793	681	576	576
q21	5685	2666	2454	2454
q22	433	355	301	301
Total cold run time: 101476 ms
Total hot run time: 28728 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4328	4244	4212	4212
q2	284	314	210	210
q3	4517	4947	4361	4361
q4	2257	2374	1461	1461
q5	4226	4087	4158	4087
q6	235	180	127	127
q7	1688	1592	1412	1412
q8	2815	2103	2120	2103
q9	7362	7198	7306	7198
q10	4331	4301	3870	3870
q11	585	393	370	370
q12	703	721	513	513
q13	3297	3726	2993	2993
q14	295	304	286	286
q15	q16	680	745	629	629
q17	1329	1273	1316	1273
q18	12200	11086	11806	11086
q19	1184	1152	1170	1152
q20	2255	2227	1927	1927
q21	5916	5180	4983	4983
q22	511	449	396	396
Total cold run time: 60998 ms
Total hot run time: 54649 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 158781 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 5adf59ef9ecf14250f9f9435258a91709eb5c648, data reload: false

query5	4321	590	454	454
query6	467	221	203	203
query7	4850	571	343	343
query8	332	165	145	145
query9	8771	4096	4061	4061
query10	503	374	308	308
query11	5846	2193	1994	1994
query12	148	100	97	97
query13	1250	592	422	422
query14	6051	4320	4021	4021
query14_1	3863	3863	3828	3828
query15	204	198	175	175
query16	969	470	483	470
query17	898	667	540	540
query18	2419	444	328	328
query19	201	186	143	143
query20	101	98	102	98
query21	233	155	137	137
query22	13075	13029	12813	12813
query23	15835	14872	14664	14664
query23_1	14749	14655	14701	14655
query24	7510	1700	1276	1276
query24_1	1277	1263	1236	1236
query25	560	423	351	351
query26	1334	364	210	210
query27	2580	578	367	367
query28	4533	2026	2026	2026
query29	1017	597	481	481
query30	327	260	223	223
query31	1196	1108	1046	1046
query32	104	61	61	61
query33	528	303	235	235
query34	1153	1131	671	671
query35	756	754	646	646
query36	768	769	709	709
query37	160	114	89	89
query38	1842	1791	1664	1664
query39	826	821	800	800
query39_1	785	798	774	774
query40	242	169	139	139
query41	68	63	63	63
query42	96	93	93	93
query43	318	324	278	278
query44	1456	764	767	764
query45	185	174	167	167
query46	1037	1167	684	684
query47	1543	1516	1449	1449
query48	407	393	290	290
query49	577	409	293	293
query50	1059	445	330	330
query51	10513	10684	10721	10684
query52	93	89	79	79
query53	252	275	191	191
query54	279	230	212	212
query55	74	73	67	67
query56	350	297	301	297
query57	1014	997	917	917
query58	282	264	251	251
query59	1575	1594	1406	1406
query60	317	269	262	262
query61	150	142	155	142
query62	394	319	265	265
query63	234	191	201	191
query64	2848	1016	896	896
query65	3915	3802	3800	3800
query66	1840	481	387	387
query67	20040	20100	19935	19935
query68	3082	1520	956	956
query69	426	317	297	297
query70	931	794	773	773
query71	378	398	301	301
query72	2989	2590	2277	2277
query73	871	771	435	435
query74	4670	4477	4294	4294
query75	2371	2345	2000	2000
query76	2297	1136	766	766
query77	347	361	277	277
query78	11067	11147	10650	10650
query79	1407	1118	782	782
query80	667	596	463	463
query81	453	325	284	284
query82	622	175	138	138
query83	417	322	298	298
query84	326	170	131	131
query85	916	628	515	515
query86	327	230	230	230
query87	1994	1967	1845	1845
query88	3730	2833	2789	2789
query89	395	315	294	294
query90	1868	204	198	198
query91	201	195	163	163
query92	61	61	53	53
query93	1575	1476	1061	1061
query94	529	346	327	327
query95	793	529	490	490
query96	1060	801	373	373
query97	2501	2466	2365	2365
query98	195	185	180	180
query99	761	754	610	610
Total cold run time: 243976 ms
Total hot run time: 158781 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.11 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 5adf59ef9ecf14250f9f9435258a91709eb5c648, data reload: false

query1	0.00	0.00	0.00
query2	0.10	0.05	0.06
query3	0.26	0.14	0.13
query4	1.60	0.14	0.14
query5	0.25	0.24	0.24
query6	1.16	0.86	0.85
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.39	0.34	0.33
query10	0.57	0.59	0.56
query11	0.19	0.14	0.14
query12	0.18	0.15	0.14
query13	0.48	0.47	0.47
query14	0.98	0.99	1.00
query15	0.64	0.60	0.60
query16	0.31	0.35	0.33
query17	1.11	1.06	1.07
query18	0.21	0.21	0.20
query19	2.03	1.95	1.94
query20	0.02	0.02	0.01
query21	15.43	0.22	0.15
query22	4.83	0.06	0.06
query23	16.10	0.31	0.12
query24	3.03	0.43	0.33
query25	0.11	0.05	0.04
query26	0.76	0.22	0.16
query27	0.04	0.04	0.03
query28	3.48	0.78	0.35
query29	12.47	4.07	3.22
query30	0.28	0.16	0.15
query31	2.77	0.57	0.32
query32	3.23	0.60	0.48
query33	3.19	3.20	3.20
query34	15.64	3.94	3.27
query35	3.23	3.21	3.23
query36	0.55	0.44	0.43
query37	0.09	0.07	0.06
query38	0.05	0.04	0.04
query39	0.03	0.03	0.03
query40	0.18	0.15	0.14
query41	0.08	0.04	0.03
query42	0.04	0.03	0.03
query43	0.04	0.04	0.03
Total cold run time: 96.23 s
Total hot run time: 24.11 s

@github-actions github-actions Bot added the approved Indicates a PR has been approved by one committer. label Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

PR approved by at least one committer and no changes requested.

@github-actions

Copy link
Copy Markdown
Contributor

PR approved by anyone and no changes requested.

@morningman

Copy link
Copy Markdown
Contributor Author

run vault_p0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by one committer. reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants