nvidia/perf-host-optimization
Profiles and optimizes TensorRT-LLM host/CPU overhead using line_profiler (with nsys support planned). Runs iterative profile-analyze-optimize-validate rounds. Use when GPU utilization is low or optimizing PyExecutor throughput.
npx skills add https://github.com/NVIDIA/TensorRT-LLM --skill perf-host-optimization
Automates detection and optimization of host-side (CPU) overhead in TensorRT-LLM's PyTorch backend.
line_profiler measures *where* CPU time is spent but not *whether* CPU is the bottleneck.
If you need to confirm CPU is the limiting factor, run the perf-host-analysis skill first -- it provides a YES/NO verdict with metric evidence.
As a rough heuristic without nsys: if doubling the batch size does not proportionally increase GPU utilization or throughput, CPU overhead is likely the bottleneck.
If the perf-host-analysis skill has already been run, use its output to skip the confirmation step and prioritize targets:
_prepare_tp_inputs.top_regressing_ops in the handoff data block maps NVTX range names to source functions. Profile the function with the largest absolute delta first._prepare_tp_inputs (e.g., _fetch_new_requests, broadcast_requests, _update_requests), target that function's source file directly instead of defaulting to _prepare_tp_inputs. See references/trtllm-nvtx-ranges.md for the NVTX-to-source mapping.Environment Variables:
TLLM_LINE_PROFILER_ENABLED=True — Enable the profilerTLLM_LINE_PROFILER_PATH — Output file pathTLLM_LINE_PROFILER_FUNCTIONS — Additional functions to profile (comma-separated)Function specification format:
# Class methods: module.path.ClassName.method_name
TLLM_LINE_PROFILER_FUNCTIONS="tensorrt_llm._torch.pyexecutor.model_engine.PyTorchModelEngine._prepare_tp_inputs"
# Standalone functions: module.path::function_name
TLLM_LINE_PROFILER_FUNCTIONS="tensorrt_llm._torch.pyexecutor.sampler::_group_requests_by_strategy_key"
# Multiple functions (comma-separated)
TLLM_LINE_PROFILER_FUNCTIONS="module.Class.method1,module.Class.method2"
CPU core affinity can significantly affect host overhead measurements,
especially on multi-socket systems (e.g., B300). Pinning processes to cores
near the GPU's NUMA node reduces cross-socket memory access latency.
taskset -p <pid> or numactl --shownumactl --cpunodebind=<node> --membind=<node>When comparing profiling results across runs, ensure CPU affinity is consistent.
Do not externally modify the affinity, unless user requires to do this to examine the affects of this part.
Document the affinity setting in each round's report if it varies.
Each profiling run should have a unique suffix to track progress across rounds:
EXTRA_SUFFIX=round0_baseline bash profile.sh
EXTRA_SUFFIX=round1_eliminate_redundant_iter bash profile.sh
Before starting the loop, review references/optimization-strategy.md for strategic guidance on ordering (zero-risk-first), measurement traps, and overhead scoping.
Key insight: Optimizations are NOT independent. Fixing a 50ms bottleneck may reveal a 30ms bottleneck that was previously masked (hidden behind the larger one). Always re-profile after each significant change — the bottleneck landscape shifts.
Ordering principle: Within each round, prefer zero-risk optimizations (caching, pre-allocation, hoisting invariants) over medium/high-risk ones (graph partition changes, algorithm fusion). Zero-risk changes provide free gains and make subsequent profiling cleaner.
Run N rounds (default 3) of the following cycle:
FOR round = 1 to MAX_ROUNDS:
1. PROFILE (with Drill-Down)
2. ANALYZE (Multi-Option)
3. OPTIMIZE (Apply Change — prefer zero-risk first)
4. TEST (Unit Test Validation)
5. VALIDATE (Re-Profile — expect bottleneck landscape to shift)
6. REPORT
END FOR → FINAL SUMMARY
The default profiler covers top-level executor functions but not all sub-functions. When a profiled function shows most time in a single sub-call, you must drill down.
When: A single line consumes >80% of a function's time calling an unprofiled sub-function:
Line # Hits Time Per Hit % Time Line Contents
==============================================================
2848 4100 59200000000.0 14439024.4 98.7 output = self.model_engine.forward(...)
How:
tensorrt_llm._torch.pyexecutor.model_engine.PyTorchModelEngine._prepare_tp_inputs)TLLM_LINE_PROFILER_FUNCTIONSFor common drill-down targets, see references/hot-path-files.md.
For the chosen hotspot:
| Type | Indicators | Severity |
|------|------------|----------|
| HOST_SYNC | .item(), .cpu() in per-layer forward path | Critical |
| SYNC | .item(), .cpu(), synchronize() in step-level code | Critical |
| CUSTOM_OP | Chain of Python tensor ops (view/slice/cast) before kernel launch | Critical |
| GRAPH_BREAK | Op that prevents CUDA graph capture of surrounding code (fix via GRAPH_EXPAND / GRAPH_SPLIT) | High |
| ALLOC | torch.zeros/empty/tensor() in loops, .clone() | High |
| HOIST | Per-layer recomputation of step-invariant values | High |
| PYLOOP | for x in collection: with many iterations | High |
| REDUNDANT_ITER | Multiple passes over the same collection | High |
| DEAD_WORK | Object construction whose results are always discarded | High |
| CONTAINER | Dict/set lookups in hot loops | Medium |
| FUNCALL | Repeated method/property calls | Medium |
| COMM | dist.all_reduce, dist.barrier, NCCL overhead in TP/PP paths | Medium |
| GIL | Lock/queue contention | Medium |
| SERIALIZE | pickle.dumps/loads, json.dumps/loads in request processing | Medium |
| GC | Periodic latency spikes, non-deterministic pauses (tail latency) | Low |
| COMPUTE | Actual computation (may not be optimizable) | Low |
For detailed classification with code examples, see references/hotspot-classification.md.
| Option | Description | Estimated Savings | Risk | Complexity |
|--------|-------------|-------------------|------|------------|
| A | ... | ... | Low/Med/High | ... |
| B | ... | ... | ... | ... |
For optimization patterns by type, see references/optimization-patterns.md (index) — it links to the relevant sub-file for each hotspot type. For GPU-specific patterns (CUSTOM_OP, GRAPH_SPLIT, GRAPH_EXPAND), see references/patterns/gpu-graph.md.
Mandatory after each optimization. Find and run related UTs to verify correctness.
Finding related tests:
# Search by modified file name
grep -rl "model_engine\|PyTorchModelEngine" tests/unittest/_torch/executor/
# Search by modified function name
grep -rl "_prepare_tp_inputs\|prepare_inputs" tests/
Running tests:
# Run specific test file with stop-on-first-failure
pytest tests/unittest/_torch/executor/test_pytorch_model_engine.py -v -x --timeout=120
# Run specific test method
pytest tests/unittest/_torch/executor/test_pytorch_model_engine.py::PyTorchModelEngineTestCase::test_position_id_preparation -v -x
For the full UT-to-file mapping, see references/hot-path-files.md.
If tests fail:
git checkout -- <file>)round<N>_<description>If regression detected (function time increased or metrics worsened):
Log for this round:
Timer unit: 1e-06 s
Total time: 1.234 s
File: /path/to/file.py
Function: my_function at line 100
Line # Hits Time Per Hit % Time Line Contents
==============================================================
100 def my_function(self):
101 500 890000.0 1780.0 72.1 result = tensor.item()
102 500 234567.0 469.1 19.0 return result
How to read effectively:
for x in range(1): loop overhead (2 hits = enter + exit check)Stop the optimization loop when:
Primary success metric: Benchmark throughput (requests/sec or tokens/sec) as measured by the profiling script. line_profiler time reductions are leading indicators, but throughput is the ground truth — a function-level speedup that doesn't improve throughput is not a real win.
The final report should include:
For a concrete multi-round example, see references/examples.md.
| File | Contents |
|------|----------|
| references/optimization-patterns.md | Pattern index — links to 6 sub-files: sync-alloc, loop-iteration, python-overhead, gpu-graph, system, compound-pitfalls |
| references/optimization-strategy.md | Zero-risk-first ordering, metric traps, three scopes of host overhead, pattern selection guide |
| references/hotspot-classification.md | Extended per-type indicators and code examples (including CUSTOM_OP, GRAPH_BREAK, HOST_SYNC) |
| references/communication-patterns.md | Communication overhead patterns (NCCL batching, barrier removal, async overlap, reduce_scatter) |
| references/hot-path-files.md | Key file tables, drill-down targets, UT mapping |
| references/examples.md | Usage examples and multi-round walkthrough |
| trtllm-nvtx-ranges.md | TRT-LLM NVTX range reference (from analysis skill) — maps range names to source functions |
Take nvidia/perf-host-optimization from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.