Jump to content

Connect Leeroopedia MCP: Equip your AI agents to search best practices, build plans, verify code, diagnose failures, and look up hyperparameter defaults.

Heuristic:ClickHouse ClickHouse ThinLTO Build Tradeoffs

From Leeroopedia




Knowledge Sources
Domains Build_System, Optimization
Last Updated 2026-02-08 18:00 GMT

Overview

Build optimization tradeoffs when using Clang ThinLTO in ClickHouse: enables `-fno-pie` for 1-2% performance gain in production builds but requires careful handling of position-independent code, Rust symbol conflicts, debug info limitations, and memory-constrained linking.

Description

ThinLTO (Thin Link-Time Optimization) is ClickHouse's primary optimization for production (RelWithDebInfo) builds. It enables whole-program optimization with `-flto=thin` and `-fwhole-program-vtables`, providing significant performance improvements. However, it introduces several tradeoffs: disabling `-fPIC` for performance means sanitizer and Rust library builds need different treatment; Clang has a known bug that prevents emission of `.debug_aranges` sections (breaking fast `addr2line`); Rust libraries with LTO produce duplicate global symbols requiring `--allow-multiple-definition`; and linking consumes significantly more memory (limited to max 2 parallel link jobs).

Usage

Apply this heuristic when configuring production builds or debugging build/link failures in RelWithDebInfo mode. Key decision points: choosing between ThinLTO performance vs debuggability, diagnosing relocation errors, understanding why linking is slow or OOM-killed, and interpreting incomplete profiler output.

The Insight (Rule of Thumb)

  • Action: ThinLTO is enabled by default for RelWithDebInfo builds on Linux without tests or sanitizers.
  • Value: Produces 1-2% faster binaries through whole-program optimization and devirtualization.
  • Trade-off 1 (Position Independence): ThinLTO builds disable `-fPIC` and use `-fno-pie` for non-ThinLTO builds on AMD64/AArch64. Without ThinLTO, `-fPIC` is enabled to avoid relocation exhaustion errors with sanitizers and Rust.
  • Trade-off 2 (Debug Info): Clang fails to emit `.debug_aranges` with ThinLTO, making `addr2line` slow. CI Docker images include a custom `ld.lld` wrapper to work around this.
  • Trade-off 3 (Rust Symbols): ThinLTO causes Rust libraries to expose duplicate global symbols (e.g., `rust_eh_personality`). Resolved with `--allow-multiple-definition` linker flag.
  • Trade-off 4 (Memory): ThinLTO linking is memory-intensive. Parallel link jobs are capped at 2 (each needs ~5GB RAM).
  • Trade-off 5 (Compatibility): ThinLTO is Clang/Linux-only, disabled with tests/sanitizers/clang-tidy.

Reasoning

ThinLTO from `CMakeLists.txt:342-366`:

if (NOT ENABLE_TESTS AND NOT ENABLE_CLANG_TIDY AND NOT SANITIZE AND NOT SANITIZE_COVERAGE AND OS_LINUX)
    option(ENABLE_THINLTO "Clang-specific link time optimization" ON)
endif()

if (ENABLE_THINLTO AND NOT ENABLE_TESTS AND NOT SANITIZE)
    set (CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -flto=thin -fwhole-program-vtables")
    set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -flto=thin -fwhole-program-vtables")

Position independence tradeoff from `CMakeLists.txt:425-441`:

# We enable position independent encoding for all build types except build with -flto=thin:
# 1. Position independent binaries and libraries have no limit for maximum size of relocation.
# 2. Position independent binaries and libraries are a little bit slower.
# 3. For some unknown reason -flto=thin leads to some other way of linkage...
#    rust-lld: error: relocation R_X86_64_32S cannot be used against symbol 'SEED_encrypt'
if (NOT ENABLE_THINLTO AND (ARCH_AMD64 OR ARCH_AARCH64))
    set(ENABLE_POSITION_INDEPENDENT_BINARY 1)
endif()

Debug info bug from `CMakeLists.txt:304-306`:

# NOTE: that clang has a bug because of it does not emit .debug_aranges
# with ThinLTO, so custom ld.lld wrapper is shipped in docker images.

Rust symbol conflict from `CMakeLists.txt:569-587`:

# With LTO Rust adds few symbols with global visibility, the most common is
# rust_eh_personality. And this leads to linking errors because multiple
# Rust libraries contains the same symbol.
if (ENABLE_THINLTO)
    set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--allow-multiple-definition")
endif()

Related Pages

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment