cmake : add PCH and unity build to improve build times (#28091)

* scripts : add initial profiling script (wip)

* src : add precompile headers (PCH) for models.h

* common : add common.h as PCH

* ggml : add PCH for ggml-impl.h

* mtmd : use PCH for models.h

* scripts : add script to build with Server/Tools/Tests

* server : add PCH for common.h

* docs: add profiling progress notes (wip)

* ggml : add exclude for GCC + SVE on ARM

Refs: https://github.com/ggml-org/llama.cpp/actions/runs/33393906061/job/99493756214?pr=28091

* ggml : attempt to fix use of std::hardware_destructive_inference_size

Refs: https://github.com/ggml-org/llama.cpp/actions/runs/33396221677/job/99501265689?pr=28091

* squash! ggml : attempt to fix use of std::hardware_destructive_inference_size

Add a version check for GCC 12 to conditionally apply the `-Winterference-size`
pragma.

* editorconfig : exclude profiling reports dir

This directory will not be included in the merge later and this commit
can be ignore at that point. Just fixing to keep CI happy.

* ggml : skip PCH for gcc on non-x86 architectures

* tests : add PCH for peg-parser/tests.h

There are 7 peg-parser tests that can share one PCH instead of then each
parsing the full tests.h.

* common : add PCH for chat.h

* docs : update linux build profiling full results

Just updating after a number of PCH additions. These are not exact
figures and will vary a bit from run to run, but they give a general idea
of the performance impact of PCH.

* cmake : introduce unity build for models

This commit introduces a unity build for the models to improve
compilation time.

The improvements were roughly the following:
```console
+------------------------+-----+------------+------------+------------+
| Build                  | TUs | Frontend   | Backend    | Total      |
+------------------------+-----+------------+------------+------------+
| Full,    master        | 396 |   811.0 s  |   692.2 s  | 1,503.2 s  |
| Full,    with PCH      | 405 |   380.0 s  |   664.7 s  | 1,044.7 s  |
| Full,    with PCH + UB | 264 |   357.7 s  |   635.7 s  |   993.4 s  |
+------------------------+-----+------------+------------+------------+

TU   = Translation Unit.
Full = includes Server, Tools, and Tests.
PCH  = precompiled headers.
UB   = unity build for models.
```

* docs : update linux profiling table with unitiy build results

* docs : update mac profiling results to include unity build [no ci]

* docs: remove profiling reports

* scripts : merge build profile scripts into one script

I was lazy before and just copied the first script to enable Tests,
Server, and Tools. This now merges them into a single script.

* Revert "editorconfig : exclude profiling reports dir" [no ci]

This reverts commit 2922a12118.

* src : rename ggml_view_2d_slice to gemma3n_view_2d_slice

This is to be consistent with the rename in gemma4.cpp which was
required to avoid a name clash.

* cmake : add build profile script for windows [no ci]

This commit adds a port of the scripts/build-profile.sh script to
windows powershell.

This was developed on Windows on ARM but should work on X64 as well but
needs to be tested there as well.
This commit is contained in:
Daniel Bevenius
2026-09-11 13:01:29 +02:00
committed by GitHub
parent 1dfe94e048
commit 3bcfeb700f
12 changed files with 462 additions and 44 deletions
+136
View File
@@ -0,0 +1,136 @@
# Compile-time profiling using clang -ftime-trace + ClangBuildAnalyzer.
#
# Usage:
# .\scripts\build-profile.ps1 [-Full] [-Jobs N]
#
# -Full : include Server, Tools, and Tests (default: minimal build)
# -Jobs : number of parallel jobs (default: all cores)
#
# Requires ClangBuildAnalyzer:
# https://github.com/aras-p/ClangBuildAnalyzer
param(
[switch]$Full,
[int]$Jobs = [Environment]::ProcessorCount
)
$ErrorActionPreference = "Stop"
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$RootDir = Split-Path -Parent $ScriptDir
if ($Full) {
$BuildDir = Join-Path $RootDir "build-profile-full"
$Report = Join-Path $BuildDir "profile-report-full.txt"
} else {
$BuildDir = Join-Path $RootDir "build-profile-baseline"
$Report = Join-Path $BuildDir "profile-report.txt"
}
$OutputBin = Join-Path $BuildDir "clang_analysis.bin"
if (-not (Get-Command clang++ -ErrorAction SilentlyContinue)) {
Write-Error "clang++ not found"
exit 1
}
if (-not (Get-Command ninja -ErrorAction SilentlyContinue)) {
Write-Error "ninja not found (required so cmake does not fall back to the Visual Studio/MSVC generator)"
exit 1
}
if (-not (Get-Command ClangBuildAnalyzer -ErrorAction SilentlyContinue)) {
Write-Error "ClangBuildAnalyzer not found`n https://github.com/aras-p/ClangBuildAnalyzer/releases"
exit 1
}
$ClangVer = (clang++ --version | Select-Object -First 1)
Write-Host "compiler : $ClangVer"
Write-Host "build dir: $BuildDir"
Write-Host "output : $OutputBin"
Write-Host "jobs : $Jobs"
Write-Host ""
if (Get-Command ccache -ErrorAction SilentlyContinue) {
Write-Host "clearing ccache..."
ccache -C -z
}
$env:CCACHE_DISABLE = "1"
$TestsFlag = if ($Full) { "ON" } else { "OFF" }
$ToolsFlag = if ($Full) { "ON" } else { "OFF" }
$ServerFlag = if ($Full) { "ON" } else { "OFF" }
cmake --fresh `
-S $RootDir `
-B $BuildDir `
-G "Ninja" `
-DCMAKE_BUILD_TYPE=Release `
-DCMAKE_C_COMPILER=clang `
-DCMAKE_CXX_COMPILER=clang++ `
-DCMAKE_C_FLAGS="-ftime-trace" `
-DCMAKE_CXX_FLAGS="-ftime-trace" `
-DGGML_CCACHE=OFF `
-DGGML_OPENMP=ON `
-DGGML_NATIVE=OFF `
"-DLLAMA_BUILD_TESTS=$TestsFlag" `
-DLLAMA_BUILD_EXAMPLES=OFF `
"-DLLAMA_BUILD_TOOLS=$ToolsFlag" `
"-DLLAMA_BUILD_SERVER=$ServerFlag" `
-DLLAMA_BUILD_APP=OFF
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$StrayTrace = Join-Path $RootDir "-.json"
if (Test-Path $StrayTrace) {
Remove-Item $StrayTrace -Force
}
Write-Host ""
Write-Host "Initializing ClangBuildAnalyzer..."
ClangBuildAnalyzer --start $BuildDir
Write-Host ""
Write-Host "building..."
Write-Host ""
$StartTime = Get-Date
cmake --build $BuildDir --clean-first -j $Jobs
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$Elapsed = (Get-Date) - $StartTime
Write-Host ""
Write-Host ("build time: {0}s ({1}m {2}s)" -f [int]$Elapsed.TotalSeconds, [int]$Elapsed.TotalMinutes, $Elapsed.Seconds)
Write-Host ""
Write-Host "Aggregating profile metrics..."
ClangBuildAnalyzer --stop $BuildDir $OutputBin | Out-Null
Write-Host ""
Write-Host ("=" * 80)
$TUs = "?"
if (Test-Path $Report) {
$Match = Select-String -Path $Report -Pattern "Compilation \((\d+)" | Select-Object -First 1
if ($Match) { $TUs = $Match.Matches[0].Groups[1].Value }
}
ClangBuildAnalyzer --analyze $OutputBin | Tee-Object -FilePath $Report
Write-Host ""
Write-Host "translation units: $TUs"
Write-Host ""
Write-Host "largest trace files (top 20 by size):"
Get-ChildItem -Path $BuildDir -Recurse -Filter "*.json" |
Where-Object { $_.Name -ne "compile_commands.json" } |
Sort-Object Length -Descending |
Select-Object -First 20 |
ForEach-Object { "{0,8:F1} KB {1}" -f ($_.Length / 1024), $_.FullName }
Write-Host ""
Write-Host "ClangBuildAnalyzer report was generated: $Report"
+122
View File
@@ -0,0 +1,122 @@
#!/usr/bin/env bash
# Compile-time profiling using clang -ftime-trace + ClangBuildAnalyzer.
#
# Usage:
# ./scripts/build-profile.sh [--full] [-jN]
#
# --full: include Server, Tools, and Tests (default: minimal build)
# -jN : number of parallel jobs (default: all cores)
#
# Requires ClangBuildAnalyzer:
# macOS: brew install clang-build-analyzer
# Linux: https://github.com/aras-p/ClangBuildAnalyzer.git
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
FULL=0
JOBS="-j$(nproc 2>/dev/null || sysctl -n hw.ncpu)"
for arg in "$@"; do
case "${arg}" in
--full) FULL=1 ;;
-j*) JOBS="${arg}" ;;
*) echo "error: unknown argument: ${arg}" >&2; exit 1 ;;
esac
done
if [ "${FULL}" -eq 1 ]; then
BUILD_DIR="${ROOT_DIR}/build-profile-full"
REPORT="${BUILD_DIR}/profile-report-full.txt"
else
BUILD_DIR="${ROOT_DIR}/build-profile-baseline"
REPORT="${BUILD_DIR}/profile-report.txt"
fi
OUTPUT_BIN="${BUILD_DIR}/clang_analysis.bin"
if ! command -v clang++ &>/dev/null; then
echo "error: clang++ not found" >&2
exit 1
fi
if ! command -v ClangBuildAnalyzer &>/dev/null; then
echo "error: ClangBuildAnalyzer not found" >&2
echo " brew install clangbuildanalyzer (macOS)" >&2
echo " or: https://github.com/aras-p/ClangBuildAnalyzer/releases" >&2
exit 1
fi
CLANG_VER=$(clang++ --version | head -1)
echo "compiler : ${CLANG_VER}"
echo "build dir: ${BUILD_DIR}"
echo "output : ${OUTPUT_BIN}"
echo "jobs : ${JOBS}"
echo
if command -v ccache &>/dev/null; then
echo "clearing ccache..."
ccache -C -z
fi
export CCACHE_DISABLE=1
cmake --fresh \
-S "${ROOT_DIR}" \
-B "${BUILD_DIR}" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_C_FLAGS="-ftime-trace" \
-DCMAKE_CXX_FLAGS="-ftime-trace" \
-DGGML_CCACHE=OFF \
-DGGML_OPENMP=ON \
-DGGML_NATIVE=OFF \
-DLLAMA_BUILD_TESTS=$([ "${FULL}" -eq 1 ] && echo ON || echo OFF) \
-DLLAMA_BUILD_EXAMPLES=OFF \
-DLLAMA_BUILD_TOOLS=$([ "${FULL}" -eq 1 ] && echo ON || echo OFF) \
-DLLAMA_BUILD_SERVER=$([ "${FULL}" -eq 1 ] && echo ON || echo OFF) \
-DLLAMA_BUILD_APP=OFF
echo
echo "Initializing ClangBuildAnalyzer..."
ClangBuildAnalyzer --start "${BUILD_DIR}"
echo
echo "building..."
echo
START=$(date +%s)
cmake --build "${BUILD_DIR}" --clean-first "${JOBS}"
END=$(date +%s)
ELAPSED=$((END - START))
echo
printf "build time: %ds (%dm %ds)\n" "${ELAPSED}" "$((ELAPSED / 60))" "$((ELAPSED % 60))"
echo
echo "Aggregating profile metrics..."
ClangBuildAnalyzer --stop "${BUILD_DIR}" "${OUTPUT_BIN}" > /dev/null
echo
echo "================================================================================"
TUS=$(grep -oP "Compilation \(\K[0-9]+" "${REPORT}" 2>/dev/null || echo "?")
ClangBuildAnalyzer --analyze "${OUTPUT_BIN}" | tee "${REPORT}"
echo
echo "translation units: ${TUS}"
echo
echo "largest trace files (top 20 by size):"
find "${BUILD_DIR}" -name "*.json" ! -name "compile_commands.json" \
| xargs ls -l 2>/dev/null \
| awk 'NF>5 {print $5, $NF}' \
| sort -rn \
| awk 'NR<=20 {printf "%8.1f KB %s\n", $1/1024, $2}'
echo
echo "ClangBuildAnalyzer report was generated: ${REPORT}"