NVIDIA / NVIDIA/TensorRT

AOT plugins leak CUDA modules on dynamic-shape changes in TensorRT 11.1/11.2.1

Open
#4,849 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Module:Plugins
Dominant language
C++
Stars
13.4k
Forks
2.4k
Avg merge
5d 3h
Merged PRs (30d)
2

Description

Description

GPU memory accumulates when an engine containing an AOT plugin is executed repeatedly with different input shapes in the same execution context. The extra memory remains after destroying the execution context, engine, and TensorRT runtime. Returning to previously used shapes continues the growth.

The attached minimal reproducer uses one hand-written PTX kernel implementing output[i] = 2 * input[i] + 1, registered through tensorrt.plugin.aot_impl. Inference runs in a standalone C++ TensorRT/CUDA process.

The same kernel and one tactic are used for all shapes. The plugin has no allocation or caching code. All outputs match an independent CPU reference exactly. The standard TensorRT affine and convolution controls do not show the retained memory observed with the AOT plugin.

Observed behavior

The clean-environment TensorRT 11.2.1.2 run produces the following results. Each row runs 30,000 requests in a separate process. Memory differences are measured by cudaMemGetInfo between a fresh CUDA context and destruction of all TensorRT objects, without a profiler:

Engine Input H/W Shape changes Extra memory after TensorRT teardown
Standard Scale (2*x + 1) Fixed 128x128 0 0 bytes
Standard Scale (2*x + 1) W alternates 128/192 19,999 0 bytes
Standard 3x3 convolution Fixed 128x128 0 0 bytes
Standard 3x3 convolution W alternates 128/192 19,999 0 bytes
One AOT affine plugin Fixed 128x128 0 0 bytes
One AOT affine plugin W alternates 128/192 19,999 50,331,648 bytes (48 MiB)

The AOT fixed/width pair and standard Scale width control produce the same memory differences on TensorRT 11.1.0.106. These byte counts describe this particular kernel and environment; the issue is continued accumulation, not a particular allocation size or threshold.

A separate TensorRT 11.2.1.2 CUPTI trace records 20,000 successful cuModuleLoadData calls: the first inference plus 19,999 shape changes. Every loaded module's requested kernel is affine. Teardown issues one successful cuModuleUnload, for the last loaded module. The earlier 19,999 modules remain loaded. Module-load caller stacks enter libnvinfer.so.11.

Additional controls reproduce retention when only H changes, or when H/W are transposed between 128x192 and 192x128 with unchanged element count. Three chained AOT plugins retain three modules per shape change. Skipping redundant setInputShape calls does not eliminate retention.

The CUDA context stays alive during the TensorRT-teardown measurements. Separately destroying it with cudaDeviceReset removes the extra memory: both fixed-shape and changing-shape controls reach the same 4 MiB device reading while their processes remain alive, then the 1 MiB idle reading on process exit. Destroying TensorRT execution contexts alone does not have this effect.

Expected behavior

Repeatedly revisiting the same legal input shapes should have bounded resource usage. CUDA modules loaded for AOT execution should be released when they are no longer needed, including when their owning TensorRT objects are destroyed. A long-running process should not accumulate GPU memory with each shape change.

Environment

TensorRT Version: 11.2.1.2; also reproduced on 11.1.0.106. Each engine was built with the same TensorRT version used to execute it. Official, unmodified tensorrt-cu13 Python bindings/libraries.

NVIDIA GPU: RTX 3080 Ti, 12 GiB, compute capability 8.6. One otherwise idle GPU was selected for every experiment.

NVIDIA Driver Version: 580.173.02.

CUDA Version: CUDA toolkit 13.0; nvcc 13.0.88.

CUDNN Version: Not installed in the clean reproducer environment and not required by its source or link command.

Operating System: Ubuntu 24.04.5 LTS, Linux x86_64.

Python Version: 3.13.15, used for engine construction only. NumPy 2.5.1.

C++ compiler: GCC/G++ 13.3.0, C++17. Consumer headers are TensorRT 11.2.1.2; the 11.1 controls use the same shared API subset with their matching 11.1 engine/runtime. The clean 11.2.1 reproduction uses matching headers throughout.

Relevant Files

Attached tensorrt_shape_memory.tar.gz for reproducer.

Steps To Reproduce

Commands or scripts:

Extract tensorrt_shape_memory.tar.gz. Use a CUDA 13 development installation and G++ on Linux. Install NumPy and the matching TensorRT Python packages in an isolated Python 3.13 environment:

python3.13 -m venv .venv
.venv/bin/python -m pip install numpy==2.5.1 tensorrt-cu13==11.2.1.2

Obtain the matching public TensorRT 11.2.1.2 headers:

git clone https://github.com/NVIDIA/TensorRT.git TensorRT-headers
git -C TensorRT-headers checkout c93b7d4893184af4882f9f1862e13a5c64b8677d

Run the attached script on one idle GPU (the reported tests selected GPU 0):

GPU=0 \
PYTHON="$PWD/.venv/bin/python" \
TRT_INCLUDE="$PWD/TensorRT-headers/include" \
TRT_LIB_DIR="$PWD/.venv/lib/python3.13/site-packages/tensorrt_libs" \
CUDA_ROOT=/usr/local/cuda \
REQUESTS=30000 \
bash run.sh

The script compiles the C++ consumer, builds each engine in a separate process, and runs each fixed/width control in another fresh process. Full verbose logs are saved in the output directory it reports.

All engines use input [1,1,H,W], one optimization profile with minimum [1,1,64,64], optimum [1,1,128,128], and maximum [1,1,1152,1152]. The width workload repeats 128x128, 128x128, 128x192. Input/output buffers are allocated once at maximum test size, every request is synchronized, and every output is checked against the CPU reference. The aliased-plugin-I/O preview feature is not enabled.

The affected result reports extra_bytes_after_runtime_destruction: 50331648 with passed: true; all five controls report zero extra bytes. There is no inference exception or numerical failure; GPU memory and module lifetimes are the failing behavior.

Have you tried the latest release?: Tested 11.1.0.106 and 11.2.1.2. TensorRT 11.3.0 has not been tested.

Can this model run on other frameworks?: This is a TensorRT network-API reproducer, not an exported framework model. All outputs match the independent CPU implementations in replay.cc, and the equivalent standard TensorRT Scale control executes without the observed retention.

tensorrt_shape_memory.tar.gz

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by extracting the attached reproducer and reading run.sh and replay.cc, then run the documented fixed-shape and changing-shape controls. Use the reported module-load and teardown behavior to trace the AOT plugin lifecycle; done means repeated legal shape changes have bounded memory and TensorRT teardown releases loaded modules without affecting the passing output checks.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, python
Domain
ai-infra-agents, performance
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.