This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 7b00434015c [opt](build) Add ENABLE_UNITY_BUILD and pilot unity builds
on three glue targets (#66712)
7b00434015c is described below
commit 7b00434015ca962f2d5c1964b402062c82018c45
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Fri Aug 14 13:54:17 2026 +0800
[opt](build) Add ENABLE_UNITY_BUILD and pilot unity builds on three glue
targets (#66712)
> Part of the BE build-time optimization series tracked in #66715.
>
> Split out of **https://github.com/apache/doris/pull/66510**, which
carries the whole
> BE build-time batch. After the header-closure surgery in **#66400**
and **#66672**,
> this PR opens the second line of that batch: CMake unity builds. It
adds the
> infrastructure switch and converts the three lowest-risk glue segments
as a pilot;
> the heavier targets (Exec, Exprs, and the rest) follow in separate PRs
once this
> one has proven the mechanism cross-platform.
### What problem does this PR solve?
Related PR: #66510, #66672
Problem Summary:
Most of the BE's cold-build time is not spent compiling our code — it is
spent
**re-parsing the same shared header closure once per small `.cpp`**. For
the glue
directories this ratio is extreme: the 51 InformationSchema scanners
cost ~124 CPU s
(with PCH) of which almost everything is the closure re-parse; the ~58
http handlers
sum to ~5.6 min of slot time for a few thousand lines of handler logic.
CMake's built-in `UNITY_BUILD` (CMake ≥ 3.16; we require 3.19.2)
concatenates groups
of `.cpp` files into jumbo TUs, so a closure is parsed **once per
batch** instead of
once per file. This PR:
1. **Adds the switch** — `option(ENABLE_UNITY_BUILD ON)` in
`be/CMakeLists.txt`,
plumbed through `build.sh` and `run-be-ut.sh` exactly like `ENABLE_PCH`,
overridable from the environment / `custom_env.sh`. Every per-target
`UNITY_BUILD` property is set unconditionally from it -- including an
explicit
OFF, so the switch also wins over CMake's own `CMAKE_UNITY_BUILD` in a
reused
cache. Turn it OFF for per-file diagnostics, per-file tooling
(clang-tidy,
coverage), or the finest-grained incremental rebuilds.
2. **Pilots unity on three glue segments** (and only there — everything
else is
explicitly opted out per file, not by omission):
- **InformationSchema**: all 51 scanner TUs → 1 unity TU
(`UNITY_BUILD_BATCH_SIZE 0`).
- **Service, scoped to `http/`**: ~58 handler TUs → 1 unity TU. The
non-http
service sources (service entry points, arrow_flight) are heterogeneous
heavy TUs
that gain nothing from merging and stay individual.
- **Storage, scoped to `index/`**: ~111 TUs sharing one CLucene-heavy
closure →
4 unity TUs of ≤ 32 sources (batch size bounds jumbo-TU size and
memory).
3. **Makes the merged TUs legal C++** with two hygiene commits that
stand on their
own even without unity:
- 27 http action files each carried a private copy of the same
file-scope
constants (`HEADER_JSON` ×16, `TABLET_ID` ×7, `SCHEMA_HASH` ×4, …). They
move to
one shared header as C++17 inline variables, values unchanged.
- `prefix_query` declared `get_prefix_terms(IndexReader*)` relying on a
header-level `CL_NS_USE(index)` using-directive. Inside
`doris::segment_v2` that
unqualified name silently flips to `doris::segment_v2::IndexReader` as
soon as
any sibling source brings that type into scope — a landmine with or
without
unity. The declaration now spells out `lucene::index::IndexReader`.
4. **Fixes a latent bug unity exposed**: `schema_scanner_helper.h`
opened with
`#ifndef _SCHEMA_SCANNER_HELPER_H_` but never defined the macro (and one
include
sat outside the guard), so the guard never worked. Harmless while every
TU
included it exactly once; breaks immediately under unity. Now `#pragma
once`.
5. **Keeps the compile-bench tooling honest under unity** — with the
default ON,
`build.sh --compile-bench` produces a build directory whose sources are
jumbo TUs,
which two of the already-merged tools silently mis-read: `cut_impact.py`
dropped
every unity dependency block (and with it all 211 pilot sources) from
its TU set,
and `report.py` filed each batch's time under the build directory
instead of the
module it came from. `cut_impact.py` now refuses a unity build directory
with the
rebuild instruction — its analysis needs per-source closures, and
splitting a
batch's union back over its members would over-estimate instead of
under-report —
`report.py` attributes a unity TU to its target's directory, and
`rebuild_radius.py` says when its per-header counts are batch-granular.
`build-support/tests/test-compile-bench-unity.sh` pins all three
behaviours.
Files whose file-scope macros must not leak into unity siblings stay
individual via
`SKIP_UNITY_BUILD_INCLUSION`: `http_parser.cpp` (CR/LF),
`be_thread_stack_action.cpp`
(`UNW_LOCAL_ONLY`), and four index files (`CL_MAX_PATH` and friends,
`IS_CHINESE_CHAR`, `APPLY_FOR_PRIMITITYPE`). A future file whose
file-scope symbols
clash inside a unity TU can opt out the same way.
### Measured results
All numbers from the development branch this series is split from (which
also
carried the #66672 include cuts), macOS arm64 + clang 20, `-j14`,
`ENABLE_PCH=ON`, cold builds, back-to-back A/B:
| metric | before | after |
|---|---|---|
| build phase wall | 11m38s | **10m16s (-82.4 s / -11.8%)** |
| Σ per-TU CPU | 147.9 min | **134.8 min (-8.9%)** |
| TU count | 8384 | 8180 |
| failures | 0 | 0 |
Per-segment slot time (sum of compile time the segment occupies across
slots):
| segment | before | after | ratio |
|---|---|---|---|
| information_schema | 221.7 s | 17.0 s | **13×** |
| service/http | 273.6 s | 26.3 s | **10.4×** |
| storage/index | 264.2 s | 120.1 s | **2.2×** (remainder includes the
four macro SKIPs) |
Why unity rather than more PCH: a serial cold micro-benchmark of the
InformationSchema target alone measured **9.9× with PCH** (124 s → 12.5
s) and
**14.6× without PCH** (206 s → 14.1 s) — and unity *without* PCH still
beats
individual TUs *with* PCH by 8.8×. Unity removes the repeated parse
instead of
amortizing it, and the two compose.
Side effects on artifacts: `libInformationSchema.a` 334 MB → 29 MB,
`libStorage.a`
1492 MB → 1318 MB (linkonce_odr instantiations dedup inside each unity
TU). The
largest unity TU peaks at 2.1 GB RSS — *below* the largest existing
individual TU in
the tree (3.9 GB), so `-jN` memory envelopes are unchanged.
### Risk and verification
- **Unity changes TU grouping only; no code changes ride along** beyond
the two
hygiene commits described above (constant dedup with identical values,
one
qualified name, one include guard).
- **Archive symbol parity** was checked per target on the development
branch:
InformationSchema keeps all 2303 external defined symbols (plus 5 weak
`unique_ptr<SchemaXxxScanner>` instantiations that dedup), Service keeps
all 4054,
Storage keeps all external symbols with 8 weak linkonce_odr template
instantiations deduping away — which is the point of unity, not a loss.
- **This exact branch, rebased onto current master, full BE build from
scratch**
(macOS arm64, clang 20, `ENABLE_PCH=ON`, `ENABLE_UNITY_BUILD=ON`):
**8349/8349 ninja edges, zero failures, `doris_be` links.** The six
expected unity
TUs (InformationSchema ×1, Service http ×1, storage/index ×4) all
compile.
This includes `schema_tso_status_scanner.cpp`, added upstream after the
pilot was
measured — it lands inside the InformationSchema unity TU via the
existing
`GLOB_RECURSE` with zero CMakeLists edits, which is the intended
maintenance story.
- **The OFF path is verified on the same tree**: reconfiguring with
`ENABLE_UNITY_BUILD=OFF` removes every `unity_*.cxx` entry from
`compile_commands.json` and flips **exactly 219 ninja edges** — the
three targets'
per-file objects plus their archives and the final link, nothing else.
All of them
compile per-file with zero failures and `doris_be` links again. The
blast radius
of the switch is precisely the three pilot targets.
- **`OFF` also wins over a native-unity cache**: configuring with
`-DENABLE_UNITY_BUILD=OFF -DCMAKE_UNITY_BUILD=ON` produced 28 unity TUs
across the
three pilot targets before the gate fix and **0** after, while the
ordinary
`-DENABLE_UNITY_BUILD=ON` configure still produces exactly the 6
advertised
batches (InformationSchema ×1, Service ×1, Storage ×4).
- **These three segments have been building as unity TUs on the
development branch
since 2026-08-08**, through repeated full-tree builds and the BE UT
builds that
verified #66672 (the UT binaries link against these same target
libraries).
### Proactive disclosure
- **Cross-platform is the blind spot, and the default is deliberately ON
so this
PR's own CI closes it.** Every local build and measurement above is
macOS arm64 +
clang 20. Nothing here is platform-specific by construction, but with
the default
ON, the Linux compile lanes and every regression pipeline in this PR's
CI run
against unity builds — that is the validation. Please give the Linux gcc
lane in
particular a look. If some environment trips over unity after merge, the
escape hatches are, in order: per-user `ENABLE_UNITY_BUILD=OFF` (env or
`custom_env.sh`), per-file `SKIP_UNITY_BUILD_INCLUSION`, or a one-line
default
flip — the infrastructure stays either way.
- **The incremental-rebuild trade-off is real**: touching one `.cpp`
inside a unity
batch recompiles the whole batch. For the glue chosen here a batch
compiles in
~16–30 s, comparable to single mid-weight TUs elsewhere in the tree; and
`ENABLE_UNITY_BUILD=OFF` restores per-file granularity for workflows
that need it.
This is also why the pilot targets are glue directories and not the
hot-edit paths.
- **contrib (openblas/clucene) was evaluated and deliberately left
alone**: the
f2c-generated LAPACK sources and the snowball stemmers define clashing
file-scope
statics (`static c__1` and friends) — structurally un-unifiable without
rewriting
generated code — and contrib compiles once and rarely changes.
- **The rest of Storage and Service is opted out per file, on purpose.**
Merging
heavy heterogeneous TUs earns nothing (the closure parse is not the
dominant cost
there) and risks monster TUs. Follow-up PRs extend unity to Exec, Exprs
and the
remaining targets with the same SKIP discipline; on the development
branch the
full rollout takes the same tree from 10m16s to **6m18s**.
---
be/CMakeLists.txt | 15 +++
be/src/information_schema/CMakeLists.txt | 8 ++
be/src/information_schema/schema_scanner_helper.h | 4 +-
be/src/service/CMakeLists.txt | 14 +++
.../http/action/action_constants.h} | 43 +++-----
.../service/http/action/batch_download_action.cpp | 2 +-
.../http/action/check_encryption_action.cpp | 2 +-
.../http/action/check_tablet_segment_action.cpp | 3 +-
be/src/service/http/action/checksum_action.cpp | 3 +-
be/src/service/http/action/compaction_action.cpp | 7 +-
.../http/action/compaction_score_action.cpp | 2 +-
be/src/service/http/action/config_action.cpp | 2 +-
.../service/http/action/delete_bitmap_action.cpp | 7 +-
be/src/service/http/action/download_action.cpp | 2 +-
be/src/service/http/action/file_cache_action.cpp | 4 +-
be/src/service/http/action/health_action.cpp | 3 +-
be/src/service/http/action/http_stream.cpp | 3 +-
be/src/service/http/action/jeprofile_actions.cpp | 2 +-
be/src/service/http/action/load_channel_action.cpp | 3 +-
be/src/service/http/action/load_stream_action.cpp | 3 +-
be/src/service/http/action/meta_action.cpp | 3 +-
be/src/service/http/action/pad_rowset_action.cpp | 2 +-
be/src/service/http/action/peer_cache_action.cpp | 3 +-
.../service/http/action/reload_tablet_action.cpp | 5 +-
.../service/http/action/restore_tablet_action.cpp | 4 +-
.../http/action/show_nested_index_file_action.cpp | 3 +-
be/src/service/http/action/snapshot_action.cpp | 4 +-
be/src/service/http/action/stream_load.cpp | 2 +-
.../http/action/tablet_migration_action.cpp | 2 +-
.../http/action/tablets_distribution_action.cpp | 3 +-
be/src/service/http/action/tablets_info_action.cpp | 3 +-
be/src/service/http/action/version_action.cpp | 3 +-
be/src/storage/CMakeLists.txt | 17 +++
.../storage/index/inverted/query/prefix_query.cpp | 4 +-
be/src/storage/index/inverted/query/prefix_query.h | 5 +-
build-support/compile-bench/cut_impact.py | 23 +++-
build-support/compile-bench/rebuild_radius.py | 9 +-
build-support/compile-bench/report.py | 23 +++-
build-support/tests/test-compile-bench-unity.sh | 116 +++++++++++++++++++++
build.sh | 2 +
run-be-ut.sh | 2 +
41 files changed, 277 insertions(+), 93 deletions(-)
diff --git a/be/CMakeLists.txt b/be/CMakeLists.txt
index 4bbd22dd774..0741d0a17dd 100644
--- a/be/CMakeLists.txt
+++ b/be/CMakeLists.txt
@@ -80,6 +80,20 @@ add_definitions(-DGLOG_CUSTOM_PREFIX_SUPPORT)
option(GLIBC_COMPATIBILITY "Enable compatibility with older glibc libraries."
ON)
option(USE_LIBCPP "Use libc++" OFF)
option(USE_JEMALLOC "Use jemalloc" ON)
+# Merge groups of .cpp files into jumbo translation units for much faster full
+# builds. Turn OFF for precise per-file diagnostics, per-file tooling
+# (clang-tidy/coverage), or the finest-grained incremental rebuilds.
+option(ENABLE_UNITY_BUILD "Enable CMake unity builds for BE targets" ON)
+# Normalize to a strict ON/OFF. Every pilot target sets its UNITY_BUILD
property
+# unconditionally from this value: OFF must be an explicit OFF, otherwise a
+# cache that also carries CMake's own CMAKE_UNITY_BUILD=ON leaves the property
+# ON (it initializes UNITY_BUILD at add_library time) and the escape hatch does
+# nothing.
+if (ENABLE_UNITY_BUILD)
+ set(DORIS_UNITY_BUILD ON)
+else()
+ set(DORIS_UNITY_BUILD OFF)
+endif()
if (OS_MACOSX)
set(GLIBC_COMPATIBILITY OFF)
set(USE_LIBCPP ON)
@@ -103,6 +117,7 @@ message(STATUS "USE_LIBCPP is ${USE_LIBCPP}")
message(STATUS "USE_JEMALLOC is ${USE_JEMALLOC}")
message(STATUS "USE_UNWIND is ${USE_UNWIND}")
message(STATUS "ENABLE_PCH is ${ENABLE_PCH}")
+message(STATUS "ENABLE_UNITY_BUILD is ${ENABLE_UNITY_BUILD}")
message(STATUS "USE_AVX2 is ${USE_AVX2}")
# set CMAKE_BUILD_TYPE
diff --git a/be/src/information_schema/CMakeLists.txt
b/be/src/information_schema/CMakeLists.txt
index f06b42180dc..a77f44d7963 100644
--- a/be/src/information_schema/CMakeLists.txt
+++ b/be/src/information_schema/CMakeLists.txt
@@ -25,3 +25,11 @@ file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS *.cpp)
add_library(InformationSchema STATIC ${SRC_FILES})
pch_reuse(InformationSchema)
+
+# Unity build: these schema scanners are homogeneous glue code whose per-TU
+# cost is dominated by re-parsing the same header closure once per file.
+# Batch size 0 merges all sources into a single unity TU (~10x faster than
+# compiling them individually). A new file whose file-scope symbols clash
+# inside the unity TU can opt out via SKIP_UNITY_BUILD_INCLUSION.
+set_target_properties(InformationSchema PROPERTIES UNITY_BUILD
${DORIS_UNITY_BUILD}
+ UNITY_BUILD_BATCH_SIZE 0)
diff --git a/be/src/information_schema/schema_scanner_helper.h
b/be/src/information_schema/schema_scanner_helper.h
index fbe23adae5f..decee9d804e 100644
--- a/be/src/information_schema/schema_scanner_helper.h
+++ b/be/src/information_schema/schema_scanner_helper.h
@@ -15,8 +15,7 @@
// specific language governing permissions and limitations
// under the License.
-#include <cstdint>
-#ifndef _SCHEMA_SCANNER_HELPER_H_
+#pragma once
#include <stdint.h>
@@ -45,4 +44,3 @@ public:
};
} // namespace doris
-#endif
diff --git a/be/src/service/CMakeLists.txt b/be/src/service/CMakeLists.txt
index 938a3711c43..28985a2e031 100644
--- a/be/src/service/CMakeLists.txt
+++ b/be/src/service/CMakeLists.txt
@@ -36,6 +36,20 @@ add_library(Service STATIC ${SRC_FILES})
pch_reuse(Service)
+# Unity build scoped to the http glue: ~60 small handlers re-parsing the same
+# header closure once per file. Non-http sources (service entry points,
+# arrow_flight) are heterogeneous heavy TUs that gain nothing from merging.
+# http_parser.cpp and be_thread_stack_action.cpp stay individual because their
+# file-scope macros (CR/LF, UNW_LOCAL_ONLY) must not leak into unity siblings.
+set(SERVICE_UNITY_SKIP ${SRC_FILES})
+list(FILTER SERVICE_UNITY_SKIP EXCLUDE REGEX ".*/service/http/.*")
+list(APPEND SERVICE_UNITY_SKIP
+ ${CMAKE_CURRENT_SOURCE_DIR}/http/http_parser.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/http/action/be_thread_stack_action.cpp)
+set_source_files_properties(${SERVICE_UNITY_SKIP} PROPERTIES
SKIP_UNITY_BUILD_INCLUSION ON)
+set_target_properties(Service PROPERTIES UNITY_BUILD ${DORIS_UNITY_BUILD}
+ UNITY_BUILD_BATCH_SIZE 0)
+
if (${MAKE_TEST} STREQUAL "OFF" AND ${BUILD_BENCHMARK} STREQUAL "OFF")
add_executable(doris_be
doris_main.cpp
diff --git a/be/src/storage/index/inverted/query/prefix_query.h
b/be/src/service/http/action/action_constants.h
similarity index 52%
copy from be/src/storage/index/inverted/query/prefix_query.h
copy to be/src/service/http/action/action_constants.h
index 1f146cc235d..e3cc8161573 100644
--- a/be/src/storage/index/inverted/query/prefix_query.h
+++ b/be/src/service/http/action/action_constants.h
@@ -17,29 +17,20 @@
#pragma once
-#include "storage/index/inverted/query/query.h"
-
-CL_NS_USE(index)
-
-namespace doris::segment_v2 {
-
-class PrefixQuery : public Query {
-public:
- PrefixQuery(SearcherPtr searcher, IndexQueryContextPtr context);
- ~PrefixQuery() override = default;
-
- void add(const InvertedIndexQueryInfo& query_info) override;
- void search(roaring::Roaring& roaring) override;
-
- void get_prefix_terms(IndexReader* reader, const std::wstring& field_name,
- const std::string& prefix, std::vector<std::string>&
prefix_terms,
- int32_t max_expansions = 50);
-
-private:
- SearcherPtr _searcher;
- IndexQueryContextPtr _context;
-
- UnionTermIterPtr _lead1;
-};
-
-} // namespace doris::segment_v2
\ No newline at end of file
+#include <cstddef>
+#include <string>
+
+namespace doris {
+
+// Constants shared by the http action handlers. Each of these used to be
+// copy-pasted as a file-scope constant in many action .cpp files; single
+// definitions let those sources live together in one unity TU.
+inline const std::string HEADER_JSON = "application/json";
+inline const std::string TABLET_ID = "tablet_id";
+inline const std::string SCHEMA_HASH = "schema_hash";
+inline const std::string OP = "op";
+inline const std::string PATH = "path";
+inline const std::string TOKEN_PARAMETER = "token";
+inline constexpr size_t MEBIBYTE = 1024 * 1024;
+
+} // namespace doris
diff --git a/be/src/service/http/action/batch_download_action.cpp
b/be/src/service/http/action/batch_download_action.cpp
index 4ccd7ec9671..ecbc84b2eea 100644
--- a/be/src/service/http/action/batch_download_action.cpp
+++ b/be/src/service/http/action/batch_download_action.cpp
@@ -29,6 +29,7 @@
#include "common/status.h"
#include "io/fs/local_file_system.h"
#include "runtime/exec_env.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_method.h"
#include "service/http/http_request.h"
@@ -40,7 +41,6 @@ namespace {
const std::string CHECK_PARAMETER = "check";
const std::string LIST_PARAMETER = "list";
const std::string DIR_PARAMETER = "dir";
-const std::string TOKEN_PARAMETER = "token";
} // namespace
BatchDownloadAction::BatchDownloadAction(
diff --git a/be/src/service/http/action/check_encryption_action.cpp
b/be/src/service/http/action/check_encryption_action.cpp
index 9db83df00ce..e8aa9691bc5 100644
--- a/be/src/service/http/action/check_encryption_action.cpp
+++ b/be/src/service/http/action/check_encryption_action.cpp
@@ -36,6 +36,7 @@
#include "io/fs/file_system.h"
#include "io/fs/path.h"
#include "runtime/exec_env.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_headers.h"
#include "service/http/http_status.h"
@@ -44,7 +45,6 @@
namespace doris {
-const std::string TABLET_ID = "tablet_id";
const std::string GET_FOOTER = "get_footer";
CheckEncryptionAction::CheckEncryptionAction(ExecEnv* exec_env,
TPrivilegeHier::type hier,
diff --git a/be/src/service/http/action/check_tablet_segment_action.cpp
b/be/src/service/http/action/check_tablet_segment_action.cpp
index e86a7adc074..96ecebcad66 100644
--- a/be/src/service/http/action/check_tablet_segment_action.cpp
+++ b/be/src/service/http/action/check_tablet_segment_action.cpp
@@ -25,6 +25,7 @@
#include <string>
#include "service/backend_options.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_headers.h"
#include "service/http/http_request.h"
@@ -35,8 +36,6 @@
namespace doris {
-const static std::string HEADER_JSON = "application/json";
-
CheckTabletSegmentAction::CheckTabletSegmentAction(ExecEnv* exec_env,
StorageEngine& engine,
TPrivilegeHier::type hier,
TPrivilegeType::type type)
diff --git a/be/src/service/http/action/checksum_action.cpp
b/be/src/service/http/action/checksum_action.cpp
index bd8e36c9c9c..1aa24385ad7 100644
--- a/be/src/service/http/action/checksum_action.cpp
+++ b/be/src/service/http/action/checksum_action.cpp
@@ -24,6 +24,7 @@
#include "boost/lexical_cast.hpp"
#include "common/logging.h"
#include "common/status.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_request.h"
#include "service/http/http_status.h"
@@ -32,11 +33,9 @@
namespace doris {
-const std::string TABLET_ID = "tablet_id";
// do not use name "VERSION",
// or will be conflict with "VERSION" in thrift/config.h
const std::string TABLET_VERSION = "version";
-const std::string SCHEMA_HASH = "schema_hash";
ChecksumAction::ChecksumAction(ExecEnv* exec_env, StorageEngine& engine,
TPrivilegeHier::type hier,
TPrivilegeType::type type)
diff --git a/be/src/service/http/action/compaction_action.cpp
b/be/src/service/http/action/compaction_action.cpp
index 68b77808390..88c9bb1d169 100644
--- a/be/src/service/http/action/compaction_action.cpp
+++ b/be/src/service/http/action/compaction_action.cpp
@@ -33,6 +33,7 @@
#include "common/metrics/doris_metrics.h"
#include "common/status.h"
#include "service/backend_options.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_headers.h"
#include "service/http/http_request.h"
@@ -50,12 +51,6 @@
namespace doris {
using namespace ErrorCode;
-namespace {
-
-constexpr std::string_view HEADER_JSON = "application/json";
-
-} // namespace
-
CompactionAction::CompactionAction(CompactionActionType ctype, ExecEnv*
exec_env,
StorageEngine& engine, TPrivilegeHier::type
hier,
TPrivilegeType::type ptype)
diff --git a/be/src/service/http/action/compaction_score_action.cpp
b/be/src/service/http/action/compaction_score_action.cpp
index 7db935e1381..04febc7e1a4 100644
--- a/be/src/service/http/action/compaction_score_action.cpp
+++ b/be/src/service/http/action/compaction_score_action.cpp
@@ -42,6 +42,7 @@
#include "cloud/cloud_tablet_mgr.h"
#include "cloud/config.h"
#include "common/status.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_handler_with_auth.h"
#include "service/http/http_headers.h"
@@ -58,7 +59,6 @@ const std::string SYNC_META = "sync_meta";
const std::string COMPACTION_SCORE = "compaction_score";
constexpr size_t DEFAULT_TOP_N = std::numeric_limits<size_t>::max();
constexpr bool DEFAULT_SYNC_META = false;
-constexpr std::string_view TABLET_ID = "tablet_id";
template <typename T>
concept CompactionScoreAccessble = requires(T t) {
diff --git a/be/src/service/http/action/config_action.cpp
b/be/src/service/http/action/config_action.cpp
index 7fbf6666aa9..11f5aa00709 100644
--- a/be/src/service/http/action/config_action.cpp
+++ b/be/src/service/http/action/config_action.cpp
@@ -35,6 +35,7 @@
#include "common/config.h"
#include "common/logging.h"
#include "common/status.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_headers.h"
#include "service/http/http_request.h"
@@ -42,7 +43,6 @@
namespace doris {
-const static std::string HEADER_JSON = "application/json";
const static std::string PERSIST_PARAM = "persist";
const std::string CONF_ITEM = "conf_item";
diff --git a/be/src/service/http/action/delete_bitmap_action.cpp
b/be/src/service/http/action/delete_bitmap_action.cpp
index 58113471832..bd8b0603741 100644
--- a/be/src/service/http/action/delete_bitmap_action.cpp
+++ b/be/src/service/http/action/delete_bitmap_action.cpp
@@ -41,6 +41,7 @@
#include "common/logging.h"
#include "common/metrics/doris_metrics.h"
#include "common/status.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_headers.h"
#include "service/http/http_request.h"
@@ -52,12 +53,6 @@
namespace doris {
using namespace ErrorCode;
-namespace {
-
-constexpr std::string_view HEADER_JSON = "application/json";
-
-} // namespace
-
DeleteBitmapAction::DeleteBitmapAction(DeleteBitmapActionType ctype, ExecEnv*
exec_env,
BaseStorageEngine& engine,
TPrivilegeHier::type hier,
TPrivilegeType::type ptype)
diff --git a/be/src/service/http/action/download_action.cpp
b/be/src/service/http/action/download_action.cpp
index 683947901a5..74061d09bb0 100644
--- a/be/src/service/http/action/download_action.cpp
+++ b/be/src/service/http/action/download_action.cpp
@@ -26,6 +26,7 @@
#include "common/status.h"
#include "io/fs/local_file_system.h"
#include "runtime/exec_env.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_request.h"
#include "service/http/utils.h"
@@ -33,7 +34,6 @@
namespace doris {
namespace {
const std::string FILE_PARAMETER = "file";
-const std::string TOKEN_PARAMETER = "token";
const std::string CHANNEL_PARAMETER = "channel";
const std::string CHANNEL_INGEST_BINLOG_TYPE = "ingest_binlog";
const std::string ACQUIRE_MD5_PARAMETER = "acquire_md5";
diff --git a/be/src/service/http/action/file_cache_action.cpp
b/be/src/service/http/action/file_cache_action.cpp
index d291bc8df1d..2403f970139 100644
--- a/be/src/service/http/action/file_cache_action.cpp
+++ b/be/src/service/http/action/file_cache_action.cpp
@@ -32,6 +32,7 @@
#include "io/cache/block_file_cache_factory.h"
#include "io/cache/file_cache_common.h"
#include "io/cache/fs_file_cache_storage.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_headers.h"
#include "service/http/http_request.h"
@@ -42,10 +43,7 @@
namespace doris {
-constexpr static std::string_view HEADER_JSON = "application/json";
-constexpr static std::string_view OP = "op";
constexpr static std::string_view SYNC = "sync";
-constexpr static std::string_view PATH = "path";
constexpr static std::string_view CLEAR = "clear";
constexpr static std::string_view RESET = "reset";
constexpr static std::string_view HASH = "hash";
diff --git a/be/src/service/http/action/health_action.cpp
b/be/src/service/http/action/health_action.cpp
index 93ee2f46d63..27618940f4b 100644
--- a/be/src/service/http/action/health_action.cpp
+++ b/be/src/service/http/action/health_action.cpp
@@ -21,6 +21,7 @@
#include <string>
#include "runtime/exec_env.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_headers.h"
#include "service/http/http_request.h"
@@ -28,8 +29,6 @@
namespace doris {
-const static std::string HEADER_JSON = "application/json";
-
void HealthAction::handle(HttpRequest* req) {
std::string status;
std::string msg;
diff --git a/be/src/service/http/action/http_stream.cpp
b/be/src/service/http/action/http_stream.cpp
index ce33b3d8ef6..c374b21de7d 100644
--- a/be/src/service/http/action/http_stream.cpp
+++ b/be/src/service/http/action/http_stream.cpp
@@ -49,6 +49,7 @@
#include "runtime/cluster_info.h"
#include "runtime/exec_env.h"
#include "runtime/fragment_mgr.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_common.h"
#include "service/http/http_headers.h"
@@ -68,8 +69,6 @@ using namespace ErrorCode;
namespace {
-constexpr size_t MEBIBYTE = 1024 * 1024;
-
bool is_compressed_file_scan(const TPipelineFragmentParams& params) {
if (!params.__isset.file_scan_params) {
return false;
diff --git a/be/src/service/http/action/jeprofile_actions.cpp
b/be/src/service/http/action/jeprofile_actions.cpp
index e8c36b00cf1..d7702c8fb63 100644
--- a/be/src/service/http/action/jeprofile_actions.cpp
+++ b/be/src/service/http/action/jeprofile_actions.cpp
@@ -24,6 +24,7 @@
#include "agent/utils.h"
#include "runtime/memory/heap_profiler.h"
+#include "service/http/action/action_constants.h"
#include "service/http/ev_http_server.h"
#include "service/http/http_channel.h"
#include "service/http/http_handler.h"
@@ -33,7 +34,6 @@
namespace doris {
-const static std::string HEADER_JSON = "application/json";
const static std::string START_HEAP_PROFILE_NOTICE =
"`curl http://be_host:be_webport/jeheap/active/true` to start heap
profiler, note that "
"`JEMALLOC_CONF` in `be/conf/be.conf` must contain `prof:true`, will
only track and sample "
diff --git a/be/src/service/http/action/load_channel_action.cpp
b/be/src/service/http/action/load_channel_action.cpp
index 2a2e8c3714d..44f0e0aaf83 100644
--- a/be/src/service/http/action/load_channel_action.cpp
+++ b/be/src/service/http/action/load_channel_action.cpp
@@ -28,6 +28,7 @@
#include "load/channel/load_channel_mgr.h"
#include "runtime/exec_env.h"
#include "service/backend_options.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_headers.h"
#include "service/http/http_request.h"
@@ -38,8 +39,6 @@
namespace doris {
-const static std::string HEADER_JSON = "application/json";
-
void LoadChannelAction::handle(HttpRequest* req) {
req->add_output_header(HttpHeaders::CONTENT_TYPE, HEADER_JSON.c_str());
HttpChannel::send_reply(req, HttpStatus::OK,
_get_load_channels().ToString());
diff --git a/be/src/service/http/action/load_stream_action.cpp
b/be/src/service/http/action/load_stream_action.cpp
index 364deb7413f..f3300a82c22 100644
--- a/be/src/service/http/action/load_stream_action.cpp
+++ b/be/src/service/http/action/load_stream_action.cpp
@@ -28,6 +28,7 @@
#include "load/channel/load_stream_mgr.h"
#include "runtime/exec_env.h"
#include "service/backend_options.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_headers.h"
#include "service/http/http_request.h"
@@ -38,8 +39,6 @@
namespace doris {
-const static std::string HEADER_JSON = "application/json";
-
void LoadStreamAction::handle(HttpRequest* req) {
req->add_output_header(HttpHeaders::CONTENT_TYPE, HEADER_JSON.c_str());
HttpChannel::send_reply(req, HttpStatus::OK,
_get_load_streams().ToString());
diff --git a/be/src/service/http/action/meta_action.cpp
b/be/src/service/http/action/meta_action.cpp
index 4dec64d0532..b41dce36336 100644
--- a/be/src/service/http/action/meta_action.cpp
+++ b/be/src/service/http/action/meta_action.cpp
@@ -29,6 +29,7 @@
#include "cloud/config.h"
#include "common/logging.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_headers.h"
#include "service/http/http_request.h"
@@ -42,8 +43,6 @@
namespace doris {
-const static std::string HEADER_JSON = "application/json";
-const static std::string OP = "op";
const static std::string DATA_SIZE = "data_size";
const static std::string HEADER = "header";
diff --git a/be/src/service/http/action/pad_rowset_action.cpp
b/be/src/service/http/action/pad_rowset_action.cpp
index 017371af586..84ffd046de6 100644
--- a/be/src/service/http/action/pad_rowset_action.cpp
+++ b/be/src/service/http/action/pad_rowset_action.cpp
@@ -27,6 +27,7 @@
#include <string>
#include <vector>
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_request.h"
#include "service/http/http_status.h"
@@ -42,7 +43,6 @@
namespace doris {
namespace {
-const std::string TABLET_ID = "tablet_id";
const std::string START_VERSION = "start_version";
const std::string END_VERSION = "end_version";
diff --git a/be/src/service/http/action/peer_cache_action.cpp
b/be/src/service/http/action/peer_cache_action.cpp
index 29271c9aa55..12992802eb0 100644
--- a/be/src/service/http/action/peer_cache_action.cpp
+++ b/be/src/service/http/action/peer_cache_action.cpp
@@ -25,6 +25,7 @@
#include "cloud/cloud_storage_engine.h"
#include "cloud/cloud_warm_up_manager.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_headers.h"
#include "service/http/http_request.h"
@@ -32,8 +33,6 @@
namespace doris {
-static constexpr std::string_view HEADER_JSON = "application/json";
-
// Serialize a single TabletPeerCandidates into a rapidjson object
static void tablet_peer_to_json(int64_t tablet_id, const TabletPeerCandidates&
tpc,
rapidjson::Writer<rapidjson::StringBuffer>&
writer) {
diff --git a/be/src/service/http/action/reload_tablet_action.cpp
b/be/src/service/http/action/reload_tablet_action.cpp
index 9932e84efcf..cf15e50aa34 100644
--- a/be/src/service/http/action/reload_tablet_action.cpp
+++ b/be/src/service/http/action/reload_tablet_action.cpp
@@ -27,6 +27,7 @@
#include "common/logging.h"
#include "common/status.h"
#include "runtime/exec_env.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_request.h"
#include "service/http/http_status.h"
@@ -34,10 +35,6 @@
namespace doris {
-const std::string PATH = "path";
-const std::string TABLET_ID = "tablet_id";
-const std::string SCHEMA_HASH = "schema_hash";
-
ReloadTabletAction::ReloadTabletAction(ExecEnv* exec_env, StorageEngine&
engine,
TPrivilegeHier::type hier,
TPrivilegeType::type type)
: HttpHandlerWithAuth(exec_env, hier, type), _engine(engine) {}
diff --git a/be/src/service/http/action/restore_tablet_action.cpp
b/be/src/service/http/action/restore_tablet_action.cpp
index beeba3449ac..6036d254a63 100644
--- a/be/src/service/http/action/restore_tablet_action.cpp
+++ b/be/src/service/http/action/restore_tablet_action.cpp
@@ -35,6 +35,7 @@
#include "io/fs/local_file_system.h"
#include "io/fs/path.h"
#include "runtime/exec_env.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_request.h"
#include "service/http/http_status.h"
@@ -49,9 +50,6 @@ using std::filesystem::path;
namespace doris {
-const std::string TABLET_ID = "tablet_id";
-const std::string SCHEMA_HASH = "schema_hash";
-
RestoreTabletAction::RestoreTabletAction(ExecEnv* exec_env, StorageEngine&
engine,
TPrivilegeHier::type hier,
TPrivilegeType::type type)
: HttpHandlerWithAuth(exec_env, hier, type), _engine(engine) {}
diff --git a/be/src/service/http/action/show_nested_index_file_action.cpp
b/be/src/service/http/action/show_nested_index_file_action.cpp
index 84450da5bc0..1e7f737107c 100644
--- a/be/src/service/http/action/show_nested_index_file_action.cpp
+++ b/be/src/service/http/action/show_nested_index_file_action.cpp
@@ -23,6 +23,7 @@
#include <string>
#include "common/status.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_headers.h"
#include "service/http/http_request.h"
@@ -34,8 +35,6 @@
namespace doris {
using namespace ErrorCode;
-const static std::string HEADER_JSON = "application/json";
-
ShowNestedIndexFileAction::ShowNestedIndexFileAction(ExecEnv* exec_env,
TPrivilegeHier::type hier,
TPrivilegeType::type
ptype)
: HttpHandlerWithAuth(exec_env, hier, ptype) {}
diff --git a/be/src/service/http/action/snapshot_action.cpp
b/be/src/service/http/action/snapshot_action.cpp
index abd714928ba..82536c83aa1 100644
--- a/be/src/service/http/action/snapshot_action.cpp
+++ b/be/src/service/http/action/snapshot_action.cpp
@@ -26,6 +26,7 @@
#include "common/logging.h"
#include "common/status.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_request.h"
#include "service/http/http_status.h"
@@ -34,9 +35,6 @@
namespace doris {
-const std::string TABLET_ID = "tablet_id";
-const std::string SCHEMA_HASH = "schema_hash";
-
SnapshotAction::SnapshotAction(ExecEnv* exec_env, StorageEngine& engine,
TPrivilegeHier::type hier,
TPrivilegeType::type type)
: HttpHandlerWithAuth(exec_env, hier, type), _engine(engine) {}
diff --git a/be/src/service/http/action/stream_load.cpp
b/be/src/service/http/action/stream_load.cpp
index 81d1fe9ccef..8f47edaba8a 100644
--- a/be/src/service/http/action/stream_load.cpp
+++ b/be/src/service/http/action/stream_load.cpp
@@ -57,6 +57,7 @@
#include "load/stream_load/stream_load_recorder.h"
#include "runtime/cluster_info.h"
#include "runtime/exec_env.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_common.h"
#include "service/http/http_headers.h"
@@ -84,7 +85,6 @@ bvar::LatencyRecorder
g_stream_load_commit_and_publish_latency_ms("stream_load",
"commit_and_publish_ms");
static constexpr size_t MIN_CHUNK_SIZE = 64 * 1024;
-static constexpr size_t MEBIBYTE = 1024 * 1024;
static const std::string CHUNK = "chunked";
static const std::string OFF_MODE = "off_mode";
static const std::string SYNC_MODE = "sync_mode";
diff --git a/be/src/service/http/action/tablet_migration_action.cpp
b/be/src/service/http/action/tablet_migration_action.cpp
index 0883511cf8b..779b543bdaa 100644
--- a/be/src/service/http/action/tablet_migration_action.cpp
+++ b/be/src/service/http/action/tablet_migration_action.cpp
@@ -24,6 +24,7 @@
#include "common/config.h"
#include "common/status.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_headers.h"
#include "service/http/http_request.h"
@@ -34,7 +35,6 @@
#include "storage/task/engine_storage_migration_task.h"
namespace doris {
-const static std::string HEADER_JSON = "application/json";
void TabletMigrationAction::_init_migration_action() {
int32_t max_thread_num = config::max_tablet_migration_threads;
diff --git a/be/src/service/http/action/tablets_distribution_action.cpp
b/be/src/service/http/action/tablets_distribution_action.cpp
index 7d1b78baae6..faa0efdaa40 100644
--- a/be/src/service/http/action/tablets_distribution_action.cpp
+++ b/be/src/service/http/action/tablets_distribution_action.cpp
@@ -29,6 +29,7 @@
#include "absl/strings/substitute.h"
#include "common/status.h"
#include "service/backend_options.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_headers.h"
#include "service/http/http_request.h"
@@ -40,8 +41,6 @@
namespace doris {
-const static std::string HEADER_JSON = "application/json";
-
TabletsDistributionAction::TabletsDistributionAction(ExecEnv* exec_env,
StorageEngine& engine,
TPrivilegeHier::type hier,
TPrivilegeType::type type)
diff --git a/be/src/service/http/action/tablets_info_action.cpp
b/be/src/service/http/action/tablets_info_action.cpp
index 90c3614b40b..c3f082c6499 100644
--- a/be/src/service/http/action/tablets_info_action.cpp
+++ b/be/src/service/http/action/tablets_info_action.cpp
@@ -29,6 +29,7 @@
#include "cloud/config.h"
#include "runtime/exec_env.h"
#include "service/backend_options.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_headers.h"
#include "service/http/http_request.h"
@@ -39,8 +40,6 @@
namespace doris {
-const static std::string HEADER_JSON = "application/json";
-
TabletsInfoAction::TabletsInfoAction(ExecEnv* exec_env, TPrivilegeHier::type
hier,
TPrivilegeType::type type)
: HttpHandlerWithAuth(exec_env, hier, type) {}
diff --git a/be/src/service/http/action/version_action.cpp
b/be/src/service/http/action/version_action.cpp
index 76f6f064af0..c638e61790c 100644
--- a/be/src/service/http/action/version_action.cpp
+++ b/be/src/service/http/action/version_action.cpp
@@ -20,6 +20,7 @@
#include <string>
#include "common/version_internal.h"
+#include "service/http/action/action_constants.h"
#include "service/http/http_channel.h"
#include "service/http/http_headers.h"
#include "service/http/http_request.h"
@@ -28,8 +29,6 @@
namespace doris {
-const static std::string HEADER_JSON = "application/json";
-
VersionAction::VersionAction(ExecEnv* exec_env, TPrivilegeHier::type hier,
TPrivilegeType::type type)
: HttpHandlerWithAuth(exec_env, hier, type) {}
diff --git a/be/src/storage/CMakeLists.txt b/be/src/storage/CMakeLists.txt
index e7a82b486db..888b16ebe32 100644
--- a/be/src/storage/CMakeLists.txt
+++ b/be/src/storage/CMakeLists.txt
@@ -45,3 +45,20 @@ if (OS_MACOSX)
endif()
pch_reuse(Storage)
+
+# Unity build scoped to storage/index/: 115 homogeneous reader/writer/iterator
+# TUs sharing one (CLucene-heavy) header closure. The rest of Storage keeps
+# compiling individually. Batch 32 bounds jumbo-TU size and memory. Four index
+# files stay individual because their file-scope macros (CL_MAX_PATH and
+# friends, IS_CHINESE_CHAR, APPLY_FOR_PRIMITITYPE) must not leak into unity
+# siblings.
+set(STORAGE_UNITY_SKIP ${SRC_FILES})
+list(FILTER STORAGE_UNITY_SKIP EXCLUDE REGEX ".*/storage/index/.*")
+list(APPEND STORAGE_UNITY_SKIP
+
${CMAKE_CURRENT_SOURCE_DIR}/index/inverted/inverted_index_compound_reader.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/index/inverted/inverted_index_fs_directory.cpp
+
${CMAKE_CURRENT_SOURCE_DIR}/index/inverted/tokenizer/basic/basic_tokenizer.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/index/zone_map/zone_map_index.cpp)
+set_source_files_properties(${STORAGE_UNITY_SKIP} PROPERTIES
SKIP_UNITY_BUILD_INCLUSION ON)
+set_target_properties(Storage PROPERTIES UNITY_BUILD ${DORIS_UNITY_BUILD}
+ UNITY_BUILD_BATCH_SIZE 32)
diff --git a/be/src/storage/index/inverted/query/prefix_query.cpp
b/be/src/storage/index/inverted/query/prefix_query.cpp
index fa1a92592da..4153a5696b4 100644
--- a/be/src/storage/index/inverted/query/prefix_query.cpp
+++ b/be/src/storage/index/inverted/query/prefix_query.cpp
@@ -49,8 +49,8 @@ void PrefixQuery::search(roaring::Roaring& roaring) {
}
}
-void PrefixQuery::get_prefix_terms(IndexReader* reader, const std::wstring&
field_name,
- const std::string& prefix,
+void PrefixQuery::get_prefix_terms(lucene::index::IndexReader* reader,
+ const std::wstring& field_name, const
std::string& prefix,
std::vector<std::string>& prefix_terms,
int32_t max_expansions) {
std::wstring ws_prefix = StringUtil::string_to_wstring(prefix);
diff --git a/be/src/storage/index/inverted/query/prefix_query.h
b/be/src/storage/index/inverted/query/prefix_query.h
index 1f146cc235d..e21f884498f 100644
--- a/be/src/storage/index/inverted/query/prefix_query.h
+++ b/be/src/storage/index/inverted/query/prefix_query.h
@@ -31,7 +31,10 @@ public:
void add(const InvertedIndexQueryInfo& query_info) override;
void search(roaring::Roaring& roaring) override;
- void get_prefix_terms(IndexReader* reader, const std::wstring& field_name,
+ // Explicitly qualified: unqualified IndexReader inside doris::segment_v2
+ // resolves to doris::segment_v2::IndexReader whenever that type is in
+ // scope (e.g. in a unity TU), not to the CL_NS_USE(index) one intended.
+ void get_prefix_terms(lucene::index::IndexReader* reader, const
std::wstring& field_name,
const std::string& prefix, std::vector<std::string>&
prefix_terms,
int32_t max_expansions = 50);
diff --git a/build-support/compile-bench/cut_impact.py
b/build-support/compile-bench/cut_impact.py
index 5507c01076a..e9c2d388fcc 100644
--- a/build-support/compile-bench/cut_impact.py
+++ b/build-support/compile-bench/cut_impact.py
@@ -67,6 +67,9 @@ REPO_ROOT = os.path.dirname(os.path.dirname(SCRIPT_DIR))
SOURCE_EXTS = (".cpp", ".cc", ".c", ".cxx")
HEADER_EXTS = (".h", ".hpp", ".hh", ".inc", ".ipp")
+# CMake writes unity sources as <target-dir>/Unity/unity_<n>_cxx.cxx (or _c.c).
+UNITY_SOURCE_RE = re.compile(r"/Unity/unity_\d+_c(?:xx)?\.c(?:xx)?$")
+
INCLUDE_RE = re.compile(r'^\s*#\s*include\s+(["<])([^">]+)[">]', re.M)
COMMENT_RE = re.compile(r"//[^\n]*|/\*.*?\*/", re.S)
TOKEN_RE = re.compile(r"[A-Za-z_]\w{2,}")
@@ -170,7 +173,15 @@ class IncludeGraph:
def load_tus(build_dir, graph):
- """source abs path -> set of project files in its real (ninja) closure."""
+ """source abs path -> set of project files in its real (ninja) closure.
+
+ Refuses a unity build dir instead of analyzing it. A unity TU concatenates
+ many sources into one jumbo .cxx, so ninja reports a single flat closure
for
+ the whole batch: the per-source closures this tool needs are simply not in
+ the database. Splitting the union back over the members would be worse than
+ useless -- every member would appear to reach every header any sibling
pulls
+ in, inflating both the affected-TU counts and the seeding advice.
+ """
proc = subprocess.Popen(
["ninja", "-C", build_dir, "-t", "deps"],
stdout=subprocess.PIPE,
@@ -178,6 +189,7 @@ def load_tus(build_dir, graph):
text=True,
)
tus = {}
+ unity = 0 # unity TUs seen: their member sources are not individually
visible
src_prefix = os.path.join(REPO_ROOT, "be", "src") + os.sep
gen_prefix = os.path.join(REPO_ROOT, "gensrc", "build") + os.sep
cur = None # dep set of the current block's TU; None while skipping a
block
@@ -195,6 +207,8 @@ def load_tus(build_dir, graph):
cur = tus.setdefault(key, set())
cur.add(key)
else:
+ if UNITY_SOURCE_RE.search(p):
+ unity += 1
cur = None # foreign TU (contrib etc.): skip whole block
elif cur is not None and p in graph.files:
cur.add(sys.intern(p))
@@ -202,6 +216,13 @@ def load_tus(build_dir, graph):
expect_source = line.rstrip().endswith("(VALID)")
cur = None
proc.wait()
+ if unity:
+ sys.exit(
+ "error: {} unity translation units in {}\n"
+ " Unity TUs hide their member sources from `ninja -t deps`, so
this\n"
+ " analysis would silently run on a partial TU set. Reconfigure
that\n"
+ " build directory with unity builds off and rebuild:\n"
+ " ENABLE_UNITY_BUILD=OFF ./build.sh
--compile-bench".format(unity, build_dir))
return tus
diff --git a/build-support/compile-bench/rebuild_radius.py
b/build-support/compile-bench/rebuild_radius.py
index ceaa6a6e5a5..400836ebecd 100644
--- a/build-support/compile-bench/rebuild_radius.py
+++ b/build-support/compile-bench/rebuild_radius.py
@@ -72,6 +72,7 @@ def main():
counts = {h: 0 for h in headers}
pch_deps = set()
objects = 0
+ unity_objects = 0
proc = subprocess.Popen(["ninja", "-C", args.build_dir, "-t", "deps"],
stdout=subprocess.PIPE, text=True,
errors="replace",
@@ -79,7 +80,7 @@ def main():
target, target_is_pch, hits = None, False, set()
def flush():
- nonlocal objects
+ nonlocal objects, unity_objects
if target is None:
return
if target_is_pch:
@@ -89,6 +90,8 @@ def main():
counts[h] += 1
if target.endswith((".o", ".obj")):
objects += 1
+ if "/Unity/unity_" in target:
+ unity_objects += 1
for line in proc.stdout:
if line[:1] not in (" ", "\t", "\n"):
@@ -105,6 +108,10 @@ def main():
proc.wait()
print(f"{objects:,} object targets in the deps database\n")
+ if unity_objects:
+ print(f"note: {unity_objects:,} of them are unity TUs, so the sources
they batch "
+ "count once per\n batch instead of once per file. For
per-file counts "
+ "rebuild with ENABLE_UNITY_BUILD=OFF.\n")
print(f"{'header':58s} {'dependents':>10s} via PCH (= rebuilds
everything)")
for h in headers:
flag = "YES" if h in pch_deps else "no"
diff --git a/build-support/compile-bench/report.py
b/build-support/compile-bench/report.py
index d70ed286f00..e8f95e414a5 100755
--- a/build-support/compile-bench/report.py
+++ b/build-support/compile-bench/report.py
@@ -38,6 +38,7 @@ Outputs inside <run_dir>: report.txt (human) and summary.json
(machine).
import argparse
import json
import os
+import re
import sys
from collections import defaultdict
@@ -47,6 +48,10 @@ TOP_TEMPLATES = 20
TOP_TAIL = 10
WIDTH = 78
+# be/<build dir>/<mirrored source
dir>/CMakeFiles/<target>.dir/Unity/unity_<n>_cxx.cxx
+UNITY_SOURCE_RE = re.compile(
+
r"^be/build[^/]*/(.+?)/CMakeFiles/[^/]+\.dir/Unity/unity_\d+_c(?:xx)?\.c(?:xx)?$")
+
def section(title):
text = "-- " + title + " "
@@ -166,9 +171,25 @@ def shorten_header(path, meta):
return path
+def unity_source_dir(rel_src):
+ """Logical source directory of a generated unity TU, or None.
+
+ CMake writes unity sources into the build tree, mirroring the source
layout:
+
be/build_Release_compile_bench/src/storage/CMakeFiles/Storage.dir/Unity/unity_0_cxx.cxx
+ which batches sources from be/src/storage. Without this the whole batch
rolls
+ up under the build directory instead of the module it came from.
+
+ Attribution is only as fine as the target's own directory: a batch that
+ merges just be/src/service/http sources still lands on be/src/service, and
a
+ unity TU is one timing for all of its members by construction.
+ """
+ match = UNITY_SOURCE_RE.match(rel_src)
+ return "be/" + match.group(1) if match else None
+
+
def group_keys(rel_src):
"""Return (level1, level2) directory grouping keys for a
doris_home-relative source."""
- rel_dir = os.path.dirname(rel_src)
+ rel_dir = unity_source_dir(rel_src) or os.path.dirname(rel_src)
if rel_dir.startswith("be/src"):
prefix, rest = "be/src", rel_dir[len("be/src"):].strip("/")
else:
diff --git a/build-support/tests/test-compile-bench-unity.sh
b/build-support/tests/test-compile-bench-unity.sh
new file mode 100755
index 00000000000..2145a9b9908
--- /dev/null
+++ b/build-support/tests/test-compile-bench-unity.sh
@@ -0,0 +1,116 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# The compile-bench tools read a build directory that may or may not have been
+# configured with ENABLE_UNITY_BUILD=ON, and a unity TU looks nothing like the
+# per-file shape they were written against. Both behaviours below are invisible
+# on a real build (one is a refusal, the other a grouping key), so they need a
+# fixture to stay honest.
+
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+
+python3 - "${ROOT}/compile-bench" <<'PY'
+import importlib.util
+import os
+import stat
+import sys
+import tempfile
+import types
+
+bench_dir = sys.argv[1]
+
+
+def load(name):
+ spec = importlib.util.spec_from_file_location(name,
os.path.join(bench_dir, name + ".py"))
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ return module
+
+
+cut_impact = load("cut_impact")
+report = load("report")
+repo = cut_impact.REPO_ROOT
+failures = []
+
+
+def check(label, cond):
+ print((" ok " if cond else " FAIL ") + label)
+ if not cond:
+ failures.append(label)
+
+
+def load_tus_with(deps_output):
+ """Run cut_impact.load_tus against a canned `ninja -t deps` database."""
+ tmp = tempfile.mkdtemp()
+ fake_ninja = os.path.join(tmp, "ninja")
+ with open(fake_ninja, "w") as fh:
+ fh.write("#!/bin/sh\ncat <<'EOF'\n" + deps_output + "\nEOF\n")
+ os.chmod(fake_ninja, os.stat(fake_ninja).st_mode | stat.S_IEXEC)
+ old_path = os.environ["PATH"]
+ os.environ["PATH"] = tmp + os.pathsep + old_path
+ try:
+ return cut_impact.load_tus(os.path.join(tmp, "build"),
types.SimpleNamespace(files=set()))
+ finally:
+ os.environ["PATH"] = old_path
+
+
+print("cut_impact.load_tus")
+
+# GREEN: the per-file shape the analysis is written against.
+standalone = (
+ "src/service/CMakeFiles/Service.dir/http/action/health_action.cpp.o: "
+ "#deps 2, deps mtime 1 (VALID)\n"
+ " {repo}/be/src/service/http/action/health_action.cpp\n"
+ " {repo}/be/src/service/http/action/health_action.h"
+).format(repo=repo)
+tus = load_tus_with(standalone)
+check("keeps a standalone TU", list(tus) == [repo +
"/be/src/service/http/action/health_action.cpp"])
+
+# RED: a unity TU. Its member sources are invisible here, so the analysis must
+# refuse rather than quietly run on whatever is left.
+unity = (
+
"src/information_schema/CMakeFiles/InformationSchema.dir/Unity/unity_0_cxx.cxx.o:
"
+ "#deps 2, deps mtime 1 (VALID)\n"
+ "
src/information_schema/CMakeFiles/InformationSchema.dir/Unity/unity_0_cxx.cxx\n"
+ " {repo}/be/src/information_schema/schema_scanner.cpp"
+).format(repo=repo)
+try:
+ load_tus_with(unity)
+ check("refuses a unity deps database", False)
+except SystemExit as exc:
+ message = str(exc)
+ check("refuses a unity deps database", "unity translation units" in
message)
+ check("names the way out", "ENABLE_UNITY_BUILD=OFF" in message)
+
+print("report.group_keys")
+unity_src =
("be/build_Release_compile_bench/src/storage/CMakeFiles/Storage.dir"
+ "/Unity/unity_0_cxx.cxx")
+check("unity TU groups under the module it batches",
+ report.group_keys(unity_src) == ("be/src/storage", "be/src/storage"))
+check("ordinary source is unchanged",
+ report.group_keys("be/src/service/http/action/health_action.cpp")
+ == ("be/src/service", "be/src/service/http"))
+check("build-tree non-unity path is unchanged",
+ report.group_keys("be/build_Release/src/agent/foo.cpp")[0] == "be")
+
+if failures:
+ sys.exit("{} check(s) failed".format(len(failures)))
+print("all checks passed")
+PY
diff --git a/build.sh b/build.sh
index e25b9aa0959..790da28c7f3 100755
--- a/build.sh
+++ b/build.sh
@@ -767,6 +767,7 @@ echo "Get params:
DENABLE_CLANG_COVERAGE -- ${DENABLE_CLANG_COVERAGE}
DISPLAY_BUILD_TIME -- ${DISPLAY_BUILD_TIME}
ENABLE_PCH -- ${ENABLE_PCH}
+ ENABLE_UNITY_BUILD -- ${ENABLE_UNITY_BUILD:-ON}
EXTRA_FE_MODULES -- ${EXTRA_FE_MODULES}
EXTRA_BE_MODULES -- ${EXTRA_BE_MODULES}
EXTRA_CLOUD_MODULES -- ${EXTRA_CLOUD_MODULES}
@@ -943,6 +944,7 @@ if [[ "${BUILD_BE}" -eq 1 ]]; then
-DSTRIP_DEBUG_INFO="${STRIP_DEBUG_INFO}" \
-DDISPLAY_BUILD_TIME="${DISPLAY_BUILD_TIME}" \
-DENABLE_PCH="${ENABLE_PCH}" \
+ -DENABLE_UNITY_BUILD="${ENABLE_UNITY_BUILD:-ON}" \
-DUSE_JEMALLOC="${USE_JEMALLOC}" \
-DUSE_AVX2="${USE_AVX2}" \
-DARM_MARCH="${ARM_MARCH}" \
diff --git a/run-be-ut.sh b/run-be-ut.sh
index 3ff9cd573ee..7eeece8329e 100755
--- a/run-be-ut.sh
+++ b/run-be-ut.sh
@@ -223,6 +223,7 @@ echo "Get params:
PARALLEL -- ${PARALLEL}
CLEAN -- ${CLEAN}
ENABLE_PCH -- ${ENABLE_PCH}
+ ENABLE_UNITY_BUILD -- ${ENABLE_UNITY_BUILD:-ON}
EXTRA_BE_MODULES -- ${EXTRA_BE_MODULES}
"
echo "Build Backend UT"
@@ -335,6 +336,7 @@ cd "${CMAKE_BUILD_DIR}"
${CMAKE_USE_CCACHE_CXX:+${CMAKE_USE_CCACHE_CXX}} \
${CMAKE_USE_CCACHE_C:+${CMAKE_USE_CCACHE_C}} \
-DENABLE_PCH="${ENABLE_PCH}" \
+ -DENABLE_UNITY_BUILD="${ENABLE_UNITY_BUILD:-ON}" \
-DDORIS_JAVA_HOME="${JAVA_HOME}" \
-DBUILD_AZURE="${BUILD_AZURE}" \
"${BE_EXTRA_CMAKE_ARGS[@]}" \
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]