Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions tests/unittest/others/test_import_side_effects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Guards against CUDA side effects of ``import tensorrt_llm``.

Importing tensorrt_llm must not create a CUDA context: every process pays the
context + module memory (~0.5-1.2 GiB depending on arch) on its default device,
including processes that never launch a kernel (e.g. the trtllm-bench parent)
and MPI workers that have not yet called ``torch.cuda.set_device``. Those
contexts are resident when the KV cache pool is sized from free GPU memory,
silently shrinking it (nvbug 6419139: ~10% throughput regression on RTX 6000D
between 1.3.0rc19 and 1.3.0rc20, caused by an import-time deep_gemm.set_pdl()
call instantiating DeepGEMM's DeviceRuntime).
"""

import os
import subprocess
import sys

import pytest
import torch

_PDL_FLAG_SCRIPT = r"""
import sys

import tensorrt_llm # noqa: F401 (the import under test)
from tensorrt_llm._torch.pyexecutor import model_engine

if model_engine._DEEP_GEMM_PDL_CONFIGURED:
sys.exit("DeepGEMM PDL was configured at import time; it must stay lazy "
"(nvbug 6419139): deep_gemm.set_pdl() instantiates DeepGEMM's "
"DeviceRuntime and creates a CUDA context")
"""

_NO_CONTEXT_SCRIPT = r"""
import os
import sys

import pynvml

pynvml.nvmlInit()
# NVML indices are physical and ignore CUDA_VISIBLE_DEVICES; the test runner
# pins CUDA_VISIBLE_DEVICES to a single physical index, so use that one.
physical = int(os.environ["CUDA_VISIBLE_DEVICES"])
handle = pynvml.nvmlDeviceGetHandleByIndex(physical)

import tensorrt_llm # noqa: F401 (the import under test)

procs = pynvml.nvmlDeviceGetComputeRunningProcesses(handle)
mine = [p for p in procs if p.pid == os.getpid()]
if mine:
used = (mine[0].usedGpuMemory or 0) >> 20
sys.exit(f"import tensorrt_llm created a CUDA context using {used} MiB")
"""
Comment on lines +46 to +65

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how CI configures CUDA_VISIBLE_DEVICES for GPU test jobs.
rg -n "CUDA_VISIBLE_DEVICES" --type=yaml
rg -n "CUDA_VISIBLE_DEVICES" -g '*.groovy' -g '*.sh'

Repository: NVIDIA/TensorRT-LLM

Length of output: 212


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- test_import_side_effects.py (outline) ---'
ast-grep outline tests/unittest/others/test_import_side_effects.py --view expanded || true

echo
echo '--- test_import_side_effects.py (lines 1-220) ---'
sed -n '1,220p' tests/unittest/others/test_import_side_effects.py

echo
echo '--- search for CUDA_VISIBLE_DEVICES in repo ---'
rg -n "CUDA_VISIBLE_DEVICES" .

echo
echo '--- likely CI / workflow files mentioning GPU or nvidia ---'
find . -path '*/.github/workflows/*' -o -path '*/.gitlab-ci*' -o -path '*/Jenkinsfile*' -o -path '*/.azure/*' -o -path '*/.circleci/*' | sed 's#^\./##' | sort

echo
echo '--- search workflows for GPU env setup ---'
rg -n "CUDA_VISIBLE_DEVICES|pynvml|nvml|gpu" .github/workflows .gitlab-ci* Jenkinsfile* .azure .circleci 2>/dev/null || true

Repository: NVIDIA/TensorRT-LLM

Length of output: 19103


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- target file with line numbers ---'
cat -n tests/unittest/others/test_import_side_effects.py | sed -n '1,220p'

echo
echo '--- repo files under .github/workflows ---'
if [ -d .github/workflows ]; then
  find .github/workflows -maxdepth 1 -type f | sed 's#^\./##' | sort
else
  echo '(none)'
fi

echo
echo '--- focused search in workflows / ci-like files ---'
rg -n "CUDA_VISIBLE_DEVICES|pynvml|nvml|nvidia-smi|GPU" .github/workflows .gitlab-ci* .azure .circleci scripts tests 2>/dev/null || true

echo
echo '--- test_import_side_effects.py references ---'
rg -n "test_import_side_effects|NO_CONTEXT_SCRIPT|_run_in_subprocess|pynvml" tests -g '*.py'

Repository: NVIDIA/TensorRT-LLM

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- usages of pynvml ---'
rg -n "\bpynvml\b|import pynvml" . --glob '!**/.git/**'

echo
echo '--- dependency manifests mentioning pynvml/nvml ---'
rg -n "pynvml|nvidia-ml-py|nvml" pyproject.toml requirements*.txt setup.py setup.cfg tox.ini .github/workflows tests scripts 2>/dev/null || true

echo
echo '--- files that look like test dependency docs ---'
find . -maxdepth 2 \( -name 'requirements*.txt' -o -name 'pyproject.toml' -o -name 'setup.py' -o -name 'environment*.yml' -o -name 'dev*.txt' \) | sed 's#^\./##' | sort

Repository: NVIDIA/TensorRT-LLM

Length of output: 32601


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- jenkins/L0_Test.groovy around CUDA_VISIBLE_DEVICES handling ---'
sed -n '820,855p' jenkins/L0_Test.groovy

echo
echo '--- l0-test workflow references ---'
rg -n "L0_Test|CUDA_VISIBLE_DEVICES|gpu-type|gpu" .github/workflows/l0-test.yml jenkins/L0_Test.groovy 2>/dev/null || true

echo
echo '--- any docs that mention UUID-style CUDA_VISIBLE_DEVICES ---'
rg -n "CUDA_VISIBLE_DEVICES.*UUID|UUID.*CUDA_VISIBLE_DEVICES|GPU-" tests docs examples jenkins .github 2>/dev/null || true

Repository: NVIDIA/TensorRT-LLM

Length of output: 19683


CUDA_VISIBLE_DEVICES needs a non-numeric fallback. int(os.environ["CUDA_VISIBLE_DEVICES"]) will fail for UUID-style device selectors, so this subprocess can abort before it checks the import side effect. Resolve the NVML handle from the token instead of assuming a bare index.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/others/test_import_side_effects.py` around lines 43 - 62, The
import-side-effect test script assumes CUDA_VISIBLE_DEVICES is always a numeric
index, but it may be a UUID-style selector and fail before the NVML check runs.
Update _NO_CONTEXT_SCRIPT to resolve the NVML device handle from the
CUDA_VISIBLE_DEVICES token in a way that works for both numeric indices and
non-numeric identifiers, so the subprocess can still reach the tensorrt_llm
import assertion.



def _run_in_subprocess(script: str) -> subprocess.CompletedProcess:
env = os.environ.copy()
# Pin to one physical device so the pynvml handle and the CUDA default
# device agree regardless of the outer environment.
visible = env.get("CUDA_VISIBLE_DEVICES", "").split(",")[0] or "0"
env["CUDA_VISIBLE_DEVICES"] = visible
return subprocess.run([sys.executable, "-c", script],
env=env,
capture_output=True,
text=True,
timeout=300)


def test_deep_gemm_pdl_configuration_is_lazy():
"""DeepGEMM PDL setup must not run at import (nvbug 6419139)."""
result = _run_in_subprocess(_PDL_FLAG_SCRIPT)
assert result.returncode == 0, (result.stdout + result.stderr)


@pytest.mark.skipif(not torch.cuda.is_available(),
reason="requires a CUDA device")
def test_import_creates_no_cuda_context():
result = _run_in_subprocess(_NO_CONTEXT_SCRIPT)
assert result.returncode == 0, (result.stdout + result.stderr)
Loading