Skip to content

[TRTLLM-14628][perf] Incremental, streamed artifact copy-back into the checkout - #17538

Draft
brnguyen2 wants to merge 6 commits into
NVIDIA:mainfrom
brnguyen2:feat/trtllm-14628-archive-transfer
Draft

[TRTLLM-14628][perf] Incremental, streamed artifact copy-back into the checkout#17538
brnguyen2 wants to merge 6 commits into
NVIDIA:mainfrom
brnguyen2:feat/trtllm-14628-archive-transfer

Conversation

@brnguyen2

Copy link
Copy Markdown
Collaborator

Description

Stacked on #17524 and #17525 — only the top commit is new here; will be rebased as those merge.

Every build cleared tensorrt_llm/include (~9k files) and the deep_gemm/deep_ep/flash_mla Python trees out of the checkout and re-copied them file by file, even when nothing changed. When the checkout lives on a network filesystem (Lustre, NFS, GPFS), this per-file copy storm dominates warm rebuild time; the clear also deleted the FMHA include-tree generation stamps every build, so that skip-if-unchanged mechanism never actually fired.

This PR replaces the rmtree+copytree pattern with sync_tree:

  • a missing destination is populated by one streamed tar pipeline (posix format, preserving sub-second mtimes) instead of per-file copies;
  • an existing destination is mirrored by size/mtime comparison — only changed files are rewritten, entries missing from the source are deleted, so warm rebuilds cause almost no destination I/O;
  • symlinks are dereferenced as before (copytree(symlinks=False) semantics), and wheel contents are byte-identical in file list.

tensorrt_llm/include is no longer cleared up front (its subtrees are synced-with-deletion or stamp-guarded, and the stamps now take effect). The hermetic staging tree from #17525 reuses sync_tree, making repeated staging incremental too. Final artifacts that are few and large (libs/*.so, bindings, wheels) remain plain copies.

Test Coverage

  • sync_tree unit tests: cold populate + symlink dereference, warm no-op (no mtime churn), change/deletion propagation, file↔dir type swaps, same-directory guard, symlink destinations, exclude patterns.
  • Full-build A/B benchmark on identical hardware (checkout on a Lustre-backed network filesystem, build state on node-local disk; also fully in-tree): warm rebuild wall time measured twice per variant, wheel file lists compared — identical. Numbers in the PR comments.

PR Checklist

  • PR title follows the required format
  • PR description explains the change and test coverage
  • Commits are signed off (DCO)
  • Documentation updated

Developers increasingly build from checkouts on network filesystems
(Lustre, NFS, GPFS), which are slow for metadata-heavy workloads. The
build currently writes high-churn state into the checkout: the CMake
build dir (default cpp/build*), the build venv (~70k files), the
setuptools wheel staging tree and *.egg-info, extension-module object
files, and (by default) the ccache directory.

Add --build_root DIR (env: TRTLLM_BUILD_ROOT) to build_wheel.py. When
set, all of the above default under DIR so it can be pointed at fast
node-local storage while the checkout stays on shared storage. Each
piece remains individually overridable (--build_dir, CCACHE_DIR,
TRTLLM_WHEEL_STAGING_DIR). Only final artifacts (tensorrt_llm/libs,
include, bindings, stubs, wheels) are still written into the checkout.

setup.py learns TRTLLM_WHEEL_STAGING_DIR, redirecting setuptools
build_base and egg_base out of the source tree.

Behavior without --build_root is unchanged.

Documented in docs/source/installation/build-from-source.md along with
CCACHE_DIR / CONAN_HOME / --use-3rdparty-cache guidance for
shared-storage workflows.

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
…o the checkout

Builds on --build_root: with --hermetic, the remaining source-tree writes
are redirected so the checkout can be mounted read-only:

- Generated FMHA kernel sources move to <build_root>/fmha-gen, consumed
  via a new TRTLLM_FMHA_GEN_DIR CMake cache variable (default: the
  historical in-source location). The generator runs from a scratch copy
  since it writes ./generated, ./temp and ./obj relative to itself, and
  generated kernels now include fused_multihead_attention_common.h
  without a "../" prefix so they compile from either location.
- The configured executor/version.h can be written to the build tree via
  TRTLLM_VERSION_H_INCLUDE_DIR instead of cpp/include.
- The wheel is assembled from a staging copy of the Python package under
  <build_root>/package (sources staged, compiled artifacts and stubs
  installed there), with the wheel landing in <build_root>/dist by
  default.
- Missing submodules become an error instead of an in-place
  'git submodule update', and --version-override is rejected (it would
  edit tensorrt_llm/version.py in the checkout).

Editable-install workflows (--skip_building_wheel,
--linking_install_binary, --install) are incompatible with --hermetic by
design: they import compiled artifacts from the checkout.

Default behavior without --hermetic is unchanged.

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
fused_multihead_attention_v2.h (#include "cubin/fmha_cubin.h") is included
transitively by sources in other targets (e.g. common_src/attentionOp.cpp),
so the generated-FMHA include dir must be global when TRTLLM_FMHA_GEN_DIR
redirects generation out of the source tree. Found by the read-only-checkout
validation build: in-tree builds resolve the header includer-relative and
never see the problem.

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
…e checkout

Every build cleared tensorrt_llm/include (~9k files) and the deep_gemm/
deep_ep/flash_mla Python trees and re-copied them file by file, even when
nothing changed. On network filesystems (Lustre/NFS/GPFS) this per-file
copy storm dominates incremental rebuild time; it also defeated the FMHA
include-tree generation stamps, which were deleted by the clear on every
build.

Replace the rmtree+copytree pattern with sync_tree:
- a missing destination is populated via one streamed tar pipeline
  (posix format to preserve sub-second mtimes) instead of per-file copies;
- an existing destination is mirrored by size/mtime comparison — only
  changed files are rewritten and entries missing from the source are
  deleted, so warm rebuilds cause almost no destination I/O;
- symlinks are dereferenced as before (copytree(symlinks=False) semantics).

tensorrt_llm/include is no longer cleared up front: its subtrees are
synced with deletion or guarded by generation stamps (which now actually
take effect). deep_ep is removed explicitly when a build does not produce
it; the linking-install mode handles a leftover copy-mode directory.
The hermetic staging tree reuses sync_tree, making repeated staging
incremental as well.

Final artifacts that are few and large (libs/*.so, bindings, wheels)
remain plain copies. Wheel contents are unchanged.

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
@brnguyen2

Copy link
Copy Markdown
Collaborator Author

Warm-rebuild benchmark for this change (same node and checkout per config, identical build flags, single GPU architecture, 64 compile jobs; each variant timed twice, values in seconds of total build_wheel.py wall time including wheel packaging):

Config Before (rmtree+copytree) After (sync_tree) Delta
Checkout + build state on network FS (in-tree) 770 / 770 591 / 595 ~23% faster
Checkout on network FS, build state on local disk (--build_root) 480 / 473 393 / 399 ~17% faster
--hermetic (all state local) 404 / 395 copy-back was already local; staging re-copy drops ~32s → ~5s

The savings are pure copy-back elimination: ~3 minutes per warm rebuild in-tree and ~80 seconds in the --build_root config, on a warm ccache where compilation itself is near-zero. Wheel file lists are identical between the before/after variants in both configs.

setup.py's root-level find_packages() ships examples.configs.database;
without examples/ in the staging tree the hermetic wheel was missing
those four files (found by file-list comparison against a conventional
build's wheel).

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
…8-archive-transfer

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>

# Conflicts:
#	scripts/build_wheel.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant