Skip to content
This repository was archived by the owner on Apr 20, 2026. It is now read-only.

trtllm nsys#231

Draft
karen-sy wants to merge 1 commit into
ishandhanani:mainfrom
karen-sy:karenc/nsys-profiling-trtllm
Draft

trtllm nsys#231
karen-sy wants to merge 1 commit into
ishandhanani:mainfrom
karen-sy:karenc/nsys-profiling-trtllm

Conversation

@karen-sy

@karen-sy karen-sy commented Mar 30, 2026

Copy link
Copy Markdown
Contributor

Changes to enable range-based nsys profiling for TRTLLM deployments

  1. src/srtctl/core/schema.py:
    • get_nsys_prefix now always uses cuda,nvtx,ucx (ucx added)
    • New continue_benchmark_after_profilng parameter (default: False for backward compat) to not kill benchmark when nsys range finishes. When True, adds --sample=none, --kill none, --wait all
    • Added TLLM_PROFILE_START_STOP and TLLM_LLMAPI_ENABLE_NVTX env vars for TRTLLM
    • Added validation: torch profiling not supported for trtllm
  2. src/srtctl/benchmarks/scripts/profiling/profile.sh:
    • TRTLLM: uses aiperf for profiling traffic generation
    • SGLang: uses sglang.bench_serving (unchanged)
  3. src/srtctl/cli/mixins/benchmark_stage.py:
    • Added PROFILING_BACKEND env var

Example YAML usage for trtllm:

  profiling:
    type: nsys
    continue_benchmark_after_profiling: true
    isl: 4000  # ISL/OSL profile for 50 synthetic requests to bench on
    osl: 600
    concurrency: 1
    prefill:  # generates nsys-rep for each worker
      start_step: 10  # corresp. to TLLM_PROFILE_START_STOP
      stop_step: 50
    decode:
      start_step: 1000
      stop_step: 1100

Summary by CodeRabbit

  • New Features

    • Added TRTLLM backend support for profiling with Nsight Systems
    • New continue_benchmark_after_profiling configuration option to control benchmark lifecycle after profiling
    • Enhanced profiling output to include UCX tracing data
  • Improvements

    • Configuration validation now prevents incompatible profiling type and backend combinations with clearer error guidance

@coderabbitai

coderabbitai Bot commented Mar 30, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

The PR adds TRTLLM-specific profiling support by introducing a PROFILING_BACKEND environment variable to conditionally branch profiling traffic generation between TRTLLM (using aiperf) and default backends (using sglang bench_serving), extends Nsight Systems configuration with TRTLLM-specific NVTX and phase-aware controls, and enforces validation to prevent incompatible torch/trtllm profiling combinations.

Changes

Cohort / File(s) Summary
Profiling Script Updates
src/srtctl/benchmarks/scripts/profiling/profile.sh
Added conditional branching on PROFILING_BACKEND to route traffic generation: TRTLLM uses aiperf against router with /v1/chat/completions endpoint; other backends continue using sglang.bench_serving and lm-eval. Sets AIPERF_RECORD_EXPORT_BATCH_SIZE=1 for frequent flushing of profile exports to /logs/artifacts/nsys_profile.
Profiling Environment Setup
src/srtctl/cli/mixins/benchmark_stage.py
Exports PROFILING_BACKEND environment variable set to backend type within profiling environment configuration.
Profiling Configuration Schema
src/srtctl/core/schema.py
Added continue_benchmark_after_profiling boolean field to ProfilingConfig. Extended get_env_vars() to conditionally set TLLM_PROFILE_START_STOP and TLLM_LLMAPI_ENABLE_NVTX for TRTLLM Nsight Systems profiling. Modified get_nsys_prefix() to include ucx in trace targets and append conditional flags (--sample=none, --kill none, --wait all) when benchmark continuation is enabled. Added validation in SrtConfig._validate_profiling() to reject torch profiling with TRTLLM backend.

Sequence Diagram(s)

sequenceDiagram
    participant Script as Profiling Script
    participant Backend as Backend Decision
    participant TRTLLM as TRTLLM Path
    participant Router as Router Service
    participant SGLang as SGLang Path
    participant Eval as LM-Eval

    Script->>Backend: Check PROFILING_BACKEND env var
    alt PROFILING_BACKEND == trtllm
        Backend->>TRTLLM: Route to TRTLLM profiling
        TRTLLM->>Router: aiperf profile /v1/chat/completions
        Router->>Router: Generate synthetic completions
        TRTLLM->>TRTLLM: Export raw profile data to nsys_profile
    else Default (sglang)
        Backend->>SGLang: Route to SGLang profiling
        SGLang->>SGLang: python3 -m sglang.bench_serving
        SGLang->>Eval: Invoke lm-eval for coverage
        Eval->>Eval: Evaluate benchmark results
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Suggested reviewers

  • ishandhanani

🐰 Profiling branches like a rabbit's ears so fine,
TRTLLM hops one way, SGLang down the line,
Nsys catches traces in the nvtx glow,
Benchmarks keep running, steal the profiling show!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is vague and does not clearly convey the primary change; 'trtllm nsys' lacks context about the specific functionality being added. Use a more descriptive title that summarizes the main change, such as 'Add range-based nsys profiling support for TRTLLM' or 'Support TRTLLM nsys profiling with aiperf integration'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch karenc/nsys-profiling-trtllm

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/srtctl/core/schema.py (1)

645-649: TRTLLM-specific env vars set unconditionally for nsys profiling.

TLLM_PROFILE_START_STOP and TLLM_LLMAPI_ENABLE_NVTX are TRTLLM-specific environment variables, but they're set for any backend when is_nsys is true. While these variables are harmless for non-TRTLLM backends (they'll be ignored), it would be cleaner to only set them when the backend is actually TRTLLM.

However, this method doesn't have access to the backend type. Consider whether this is acceptable as-is (extra env vars are benign) or if the backend type should be passed to this method for conditional logic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/srtctl/core/schema.py` around lines 645 - 649, The code unconditionally
sets TRTLLM-specific env vars (TLLM_PROFILE_START_STOP, TLLM_LLMAPI_ENABLE_NVTX)
whenever is_nsys is true; update the logic to only set these when the backend is
TRTLLM by either adding a backend_type parameter to the method or using an
existing backend attribute on self. Concretely, change the method signature to
accept backend_type (or read self.backend_type if available), then wrap the env
assignments inside a conditional like: if is_nsys and backend_type == "trtllm"
and phase_config and phase_config.start_step is not None and
phase_config.stop_step is not None, set TLLM_PROFILE_START_STOP and
TLLM_LLMAPI_ENABLE_NVTX; otherwise skip them. Ensure references to is_nsys,
phase_config, TLLM_PROFILE_START_STOP, and TLLM_LLMAPI_ENABLE_NVTX are updated
accordingly.
src/srtctl/benchmarks/scripts/profiling/profile.sh (2)

138-159: Consider making --request-count configurable or documenting the hardcoded value.

The TRTLLM path uses --request-count 50 while the SGLang path uses --num-prompts 128. If this difference is intentional (e.g., TRTLLM profiling needs fewer requests), a comment explaining the rationale would be helpful. Otherwise, consider using a common environment variable like PROFILE_NUM_REQUESTS for consistency.

Also, the --tokenizer /model/ path assumes the model is mounted at /model/ which is consistent with the backend implementations shown in the context snippets.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/srtctl/benchmarks/scripts/profiling/profile.sh` around lines 138 - 159,
In the TRTLLM branch of profile.sh (the if checking PROFILING_BACKEND ==
"trtllm") the aiperf profile call hardcodes --request-count 50; make this
configurable by replacing the literal with an environment variable (e.g., use
PROFILE_NUM_REQUESTS with a sensible default) and add a short comment explaining
the chosen default or why TRTLLM differs from SGLang's --num-prompts; also
document or assert the assumed tokenizer mount path (/model/) near the aiperf
invocation to match other backend implementations.

176-184: lm-eval only runs for SGLang backend - verify this is intentional.

The lm-eval evaluation step runs only in the SGLang branch. If this is intentional (e.g., TRTLLM profiling focuses solely on nsys captures), consider adding a comment explaining why. Otherwise, the lm-eval block could be moved outside the if/else to run for both backends since it uses the OpenAI-compatible /v1/completions endpoint.

Also, the inline pip install with output suppression (> /dev/null 2>&1) could hide installation failures. Consider checking the exit code or at least preserving stderr.

♻️ Suggested improvement for pip install
-        pip install lm-eval tenacity > /dev/null 2>&1
+        pip install lm-eval tenacity > /dev/null || echo "Warning: pip install failed"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/srtctl/benchmarks/scripts/profiling/profile.sh` around lines 176 - 184,
The lm-eval block (the python -m lm_eval invocation using
base_url=http://${head_node}:${head_port}/v1/completions and variables like
${model_name} and ${PROFILE_CONCURRENCY}) currently executes only in the SGLang
branch; decide whether it should run for both backends—if yes, move that entire
lm-eval block out of the if/else so both branches run it, otherwise add a
concise comment above the block explaining why TRTLLM is intentionally excluded.
Also stop silently swallowing pip install failures: run pip install lm-eval
tenacity without redirecting both stdout/stderr or check its exit code (e.g.,
test the install return code) and surface errors to the console before
proceeding to python -m lm_eval.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/srtctl/benchmarks/scripts/profiling/profile.sh`:
- Around line 161-188: The script currently overwrites the primary benchmark's
exit status with lm-eval's by assigning exit_code=$? only at the end; modify the
SGLang branch to capture the exit status immediately after running python3 -m
sglang.bench_serving (e.g., save $? into a variable like bench_status), then run
lm-eval, and when computing exit_code prefer the primary benchmark's non-zero
status (set exit_code to bench_status if bench_status is non-zero, otherwise use
the lm-eval status or 0). Update references to the sglang.bench_serving command
and the exit_code variable accordingly so failures in the primary benchmark are
not masked.

---

Nitpick comments:
In `@src/srtctl/benchmarks/scripts/profiling/profile.sh`:
- Around line 138-159: In the TRTLLM branch of profile.sh (the if checking
PROFILING_BACKEND == "trtllm") the aiperf profile call hardcodes --request-count
50; make this configurable by replacing the literal with an environment variable
(e.g., use PROFILE_NUM_REQUESTS with a sensible default) and add a short comment
explaining the chosen default or why TRTLLM differs from SGLang's --num-prompts;
also document or assert the assumed tokenizer mount path (/model/) near the
aiperf invocation to match other backend implementations.
- Around line 176-184: The lm-eval block (the python -m lm_eval invocation using
base_url=http://${head_node}:${head_port}/v1/completions and variables like
${model_name} and ${PROFILE_CONCURRENCY}) currently executes only in the SGLang
branch; decide whether it should run for both backends—if yes, move that entire
lm-eval block out of the if/else so both branches run it, otherwise add a
concise comment above the block explaining why TRTLLM is intentionally excluded.
Also stop silently swallowing pip install failures: run pip install lm-eval
tenacity without redirecting both stdout/stderr or check its exit code (e.g.,
test the install return code) and surface errors to the console before
proceeding to python -m lm_eval.

In `@src/srtctl/core/schema.py`:
- Around line 645-649: The code unconditionally sets TRTLLM-specific env vars
(TLLM_PROFILE_START_STOP, TLLM_LLMAPI_ENABLE_NVTX) whenever is_nsys is true;
update the logic to only set these when the backend is TRTLLM by either adding a
backend_type parameter to the method or using an existing backend attribute on
self. Concretely, change the method signature to accept backend_type (or read
self.backend_type if available), then wrap the env assignments inside a
conditional like: if is_nsys and backend_type == "trtllm" and phase_config and
phase_config.start_step is not None and phase_config.stop_step is not None, set
TLLM_PROFILE_START_STOP and TLLM_LLMAPI_ENABLE_NVTX; otherwise skip them. Ensure
references to is_nsys, phase_config, TLLM_PROFILE_START_STOP, and
TLLM_LLMAPI_ENABLE_NVTX are updated accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c09e9d5f-d92b-497d-bd88-dc87269d242f

📥 Commits

Reviewing files that changed from the base of the PR and between 50c91ba and eee492c.

📒 Files selected for processing (3)
  • src/srtctl/benchmarks/scripts/profiling/profile.sh
  • src/srtctl/cli/mixins/benchmark_stage.py
  • src/srtctl/core/schema.py

Comment on lines +161 to 188
else
# SGLang: use sglang.bench_serving
python3 -m sglang.bench_serving \
--backend sglang \
--model "${model_name}" \
--host "${head_node}" --port "${head_port}" \
--dataset-name random \
--max-concurrency "${PROFILE_CONCURRENCY}" \
--num-prompts 128 \
--random-input-len "${PROFILE_ISL}" \
--random-output-len "${PROFILE_OSL}" \
--random-range-ratio 1 \
--warmup-request 0


# lm-eval for additional coverage (uses OpenAI /v1/completions — works for both backends)
echo ""
echo "Running lm-eval..."
pip install lm-eval tenacity > /dev/null 2>&1
python -m lm_eval \
--model local-completions \
--tasks gsm8k \
--model_args "base_url=http://${head_node}:${head_port}/v1/completions,model=${model_name},tokenized_requests=False,tokenizer_backend=None,num_concurrent=${PROFILE_CONCURRENCY},timeout=6000,max_retries=1" \
--limit 10
fi
fi

exit_code=$?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Exit code only captures the last command, potentially masking earlier failures.

In the SGLang branch, exit_code=$? (line 188) captures the exit code of lm-eval, not sglang.bench_serving. If the primary benchmark (bench_serving) fails but lm-eval succeeds, the failure is masked.

Consider capturing and checking the exit code of the primary benchmark command:

🐛 Proposed fix to capture primary benchmark exit code
     else
         # SGLang: use sglang.bench_serving
         python3 -m sglang.bench_serving \
             --backend sglang \
             --model "${model_name}" \
             --host "${head_node}" --port "${head_port}" \
             --dataset-name random \
             --max-concurrency "${PROFILE_CONCURRENCY}" \
             --num-prompts 128 \
             --random-input-len "${PROFILE_ISL}" \
             --random-output-len "${PROFILE_OSL}" \
             --random-range-ratio 1 \
             --warmup-request 0
+        bench_exit_code=$?

             
         # lm-eval for additional coverage (uses OpenAI /v1/completions — works for both backends)
         echo ""
         echo "Running lm-eval..."
         pip install lm-eval tenacity > /dev/null 2>&1
         python -m lm_eval \
             --model local-completions \
             --tasks gsm8k \
             --model_args "base_url=http://${head_node}:${head_port}/v1/completions,model=${model_name},tokenized_requests=False,tokenizer_backend=None,num_concurrent=${PROFILE_CONCURRENCY},timeout=6000,max_retries=1" \
             --limit 10
+        
+        # Use bench_serving exit code as primary result
+        exit_code=${bench_exit_code}
     fi
 fi
 
-exit_code=$?
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
else
# SGLang: use sglang.bench_serving
python3 -m sglang.bench_serving \
--backend sglang \
--model "${model_name}" \
--host "${head_node}" --port "${head_port}" \
--dataset-name random \
--max-concurrency "${PROFILE_CONCURRENCY}" \
--num-prompts 128 \
--random-input-len "${PROFILE_ISL}" \
--random-output-len "${PROFILE_OSL}" \
--random-range-ratio 1 \
--warmup-request 0
# lm-eval for additional coverage (uses OpenAI /v1/completions — works for both backends)
echo ""
echo "Running lm-eval..."
pip install lm-eval tenacity > /dev/null 2>&1
python -m lm_eval \
--model local-completions \
--tasks gsm8k \
--model_args "base_url=http://${head_node}:${head_port}/v1/completions,model=${model_name},tokenized_requests=False,tokenizer_backend=None,num_concurrent=${PROFILE_CONCURRENCY},timeout=6000,max_retries=1" \
--limit 10
fi
fi
exit_code=$?
else
# SGLang: use sglang.bench_serving
python3 -m sglang.bench_serving \
--backend sglang \
--model "${model_name}" \
--host "${head_node}" --port "${head_port}" \
--dataset-name random \
--max-concurrency "${PROFILE_CONCURRENCY}" \
--num-prompts 128 \
--random-input-len "${PROFILE_ISL}" \
--random-output-len "${PROFILE_OSL}" \
--random-range-ratio 1 \
--warmup-request 0
bench_exit_code=$?
# lm-eval for additional coverage (uses OpenAI /v1/completions — works for both backends)
echo ""
echo "Running lm-eval..."
pip install lm-eval tenacity > /dev/null 2>&1
python -m lm_eval \
--model local-completions \
--tasks gsm8k \
--model_args "base_url=http://${head_node}:${head_port}/v1/completions,model=${model_name},tokenized_requests=False,tokenizer_backend=None,num_concurrent=${PROFILE_CONCURRENCY},timeout=6000,max_retries=1" \
--limit 10
# Use bench_serving exit code as primary result
exit_code=${bench_exit_code}
fi
fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/srtctl/benchmarks/scripts/profiling/profile.sh` around lines 161 - 188,
The script currently overwrites the primary benchmark's exit status with
lm-eval's by assigning exit_code=$? only at the end; modify the SGLang branch to
capture the exit status immediately after running python3 -m
sglang.bench_serving (e.g., save $? into a variable like bench_status), then run
lm-eval, and when computing exit_code prefer the primary benchmark's non-zero
status (set exit_code to bench_status if bench_status is non-zero, otherwise use
the lm-eval status or 0). Update references to the sglang.bench_serving command
and the exit_code variable accordingly so failures in the primary benchmark are
not masked.

@karen-sy
karen-sy marked this pull request as draft March 30, 2026 17:00
@karen-sy

Copy link
Copy Markdown
Contributor Author

I should rebase changes onto Ishan main (which also will make it easier to add the other trtllm nsys profiling variants I have for trace- and time-based profiling)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant