StephennFernandes's picture
Anuvani-Diarization 1.0.0
63dd946
|
Raw History Blame Contribute Delete
13.5 kB

Python backend: execution model, data path, BLS, decoupled mode, TensorRT inside it

Scope: how Triton's Python backend runs model.py, how tensors reach it, BLS, decoupled mode, and running a TensorRT engine from inside it. Facts apply to Triton 24.10 (r24.10 branches of python_backend, server, core and backend) unless r25.06 or r26.05 is named; TensorRT facts come from the 10.x docs (10.16.1). Checked 9 October 2026.

Lifecycle and execute()

  • A Python model is a class TritonPythonModel with an optional auto_complete_config, an optional initialize(args), a required execute(requests) and an optional finalize() [1]. Objects created in auto_complete_config do not survive: that interpreter is destroyed when the function returns [1].
  • initialize receives args, a dictionary of strings with the keys model_config (JSON), model_instance_kind, model_instance_device_id, model_repository, model_version and model_name [1]. Placement is the model's job: a model "can be written to respect the kind setting", and the example builds cuda:<device_id> itself [1, 7].
  • In default mode execute receives a list of N requests and must return a list of exactly N responses in the same order; an element may carry an error but cannot be None, and Triton returns an error for every request if these rules are broken [1]. Request tensor data are deleted when execute returns, so request objects must not be kept [1].
  • Since 24.06 a default-mode model may send its one response through InferenceResponseSender instead [1].
  • When a model has no dynamic_batching (and no sequence batching), Triton core uses its dynamic batch scheduler with batching disabled, and each request is enqueued as its own payload [10]. With such a configuration each execute call therefore gets one request (inference from [10]).
  • Triton never makes simultaneous execute calls on one model instance [11]; a second request for a busy single instance waits in the scheduler queue (inference).
  • From r26.05 (and main) an optional is_ready() is called on v2/models/<model>/ready, with a 5 s timeout [2].

One process, and one CUDA context, per instance

  • Because of the GIL, the Python backend spawns a separate process for each model instance; ONNX Runtime, TensorFlow and PyTorch backends create threads instead [1].
  • That process is the stub triton_python_backend_stub, linked to one libpython [1]. The r24.10 and r25.06 READMEs say Python 3.10, the r26.05 README 3.12 [1, 2, 3]; the stub in each image links libpython3.10 (24.10) and libpython3.12 (25.06, 26.05), plus the CUDA runtime in all three [19], so the r25.06 text is stale.
  • The CUDA runtime creates one primary context per device for an application, shared by its host threads [12]. Each stub process that touches a GPU therefore holds its own context on that GPU (inference from [1, 12]).

Shared memory between Triton and the stub

  • Since 21.04 the backend connects user code to Triton through shared memory [1]. Each instance gets 1 MB by default and the region grows in 1 MB steps when needed; shm-default-byte-size and shm-growth-byte-size change this, and stub-timeout-seconds (default 30 s) bounds the stub connection time, all passed as --backend-config=python,<key>=<value> [1]. The source also sets shm_message_queue_size 1000 and thread-pool-size 32; the pool serves BLS requests and decoupled responses from the stub [4].
  • Docker's default --shm-size of 64 MB is called "very small" [1]. Since 24.04 region names carry a UUID, so several servers can share /dev/shm; older releases need shm-region-prefix-name [1].
  • For a CPU input the backend allocates the tensor inside the shared-memory pool and copies the request's data into it [4]. For a CPU output the backend copies the tensor from shared memory into Triton's response buffer [5]. GPU tensors cross the process boundary through CUDA IPC handles or a shared CUDA memory pool [4, 5].

GPU inputs and DLPack

  • By default the backend moves every input to CPU before the model sees it. With the parameter FORCE_CPU_ONLY_INPUT_TENSORS set to "no", inputs arrive in CPU or GPU memory "depending on how those tensors were last used", and the model must handle both [1].
  • pb_utils.Tensor.to_dlpack() and pb_utils.Tensor.from_dlpack() give zero-copy exchange with frameworks; from_dlpack also accepts objects with __dlpack__, needs C-contiguous data, and is the only route for BF16 tensors because as_numpy() does not support them [1]. is_cpu() reports the placement [1]. PyTorch's torch.utils.dlpack.from_dlpack returns a tensor that shares the memory [17].

Business Logic Scripting (BLS)

  • BLS (since 21.08) lets execute send inference requests to other models served by the same Triton; it is not allowed in initialize or finalize [1]. InferenceRequest(...).exec() blocks; async_exec() returns an awaitable and needs async def execute [1].
  • Optional arguments: request_id, correlation_id, model_version, flags, timeout (microseconds, 0 means none) and preferred_memory (23.04+), which picks CPU or GPU memory and a device for the returned outputs [1]. exec(decoupled=True) (23.03+) returns an iterator over a decoupled model's responses [1].
  • In the source, the server process runs each BLS request through the in-process C API (TRITONSERVER_ServerInferAsync); CPU outputs are allocated in the stub's shared memory and GPU outputs in the CUDA memory pool [6]. A BLS call is thus a full Triton inference request with its own scheduling.
  • Limits in r24.10: a call cycle can deadlock when no instance is free; no async BLS in decoupled mode [1].

Decoupled mode

  • Set model_transaction_policy { decoupled: true }; execute gets each request's InferenceResponseSender, sends any number of responses, marks the last with TRITONSERVER_RESPONSE_COMPLETE_FINAL, and returns None [1, 8]. async def execute is supported for decoupled models since 24.04 [1].
  • Triton's HTTP inference endpoint and the gRPC ModelInfer RPC cannot serve decoupled models; clients must use the gRPC bidirectional streaming RPC [8]. The HTTP generate_stream extension can return several JSON responses as server-sent events [9].

Custom execution environments

  • A conda-pack archive (or unpacked directory) is named by the model parameter EXECUTION_ENV_PATH (may use $$TRITON_MODEL_DIRECTORY); its interpreter must match the stub's Python version, else a custom stub is needed, best compiled inside the NGC container [1].

Running a TensorRT engine inside model.py

  • None of the three images ships the tensorrt Python module, although they ship libnvinfer10 10.5.0.18 (24.10), 10.11.0.33 (25.06) and 10.16.1.11 (26.05) [19]. PyPI has tensorrt-cu12-bindings 10.5.0 with cp310 manylinux_2_17_x86_64 wheels [16].
  • TensorRT 10 inference in Python: runtime.deserialize_cuda_engine(plan), engine.create_execution_context(), context.set_input_shape(name, shape) for dynamic inputs, context.set_tensor_address(name, ptr) for every input and output (memory may come from PyTorch), then context.execute_async_v3(stream_ptr) [13, 14]. The pointer of a torch.cuda.Stream is its cuda_stream property [14]; torch.cuda.current_stream() returns the current stream [17]. get_tensor_shape(name) gives output shapes once input shapes are set [13].
  • Registered memory must not be changed or freed before the stream is synchronised (inputs may be freed after the event given to set_input_consumed_event) [13]. Passing the default stream "may lead to performance issues due to additional cudaDeviceSynchronize() calls"; TensorRT asks for a non-default stream [13].
  • Portability: serialised engines are specific to the exact GPU model and the platform [14]. A plan records the TensorRT major, minor, patch and build version and the compute capability; a mismatch fails deserialisation unless the plan was built version-compatible (8.6 or later, with an embedded lean runtime that must be trusted) or hardware-compatible (kAMPERE_PLUS: compute capability 8.0 or higher; kSAME_COMPUTE_CAPABILITY in the 10.16 docs), and such engines can be slower [15].

Unverified

  • Whether the PyPI tensorrt-cu12 10.5.0 wheel has the 24.10 image's build number (10.5.0.18); plans check it [15].
  • The GPU memory taken by one stub process's CUDA context; no source gives a figure.
  • Which TensorRT 10.x release added kSAME_COMPUTE_CAPABILITY, so whether 10.5 (24.10) has it.

What this means for the diarization model

  • diar_nn_g<i> has max_batch_size: 0 and no dynamic batcher, so each execute call carries one block (one OP) and the engine's second in-flight request waits in Triton's queue while the first runs (inference from [10, 11]).
  • One instance per GPU means one stub process and one CUDA context per GPU, which is also what the exact lane needs: one context and one stream (inference).
  • A request's SPAN is about 1.7 MB, above the 1 MB default region, so the first requests grow the region; setting shm-default-byte-size above the largest request plus response avoids growth while serving, and Docker's --shm-size must hold every instance's region (inference from [1]).
  • HTTP inputs arrive in CPU memory, so FORCE_CPU_ONLY_INPUT_TENSORS gains nothing here; each block costs one copy into shared memory and one host-to-device copy in model.py; on the way back the (8, 799, 16) fp32 output (409,088 bytes) is written into shared memory and copied from there into the response (inference from [4, 5]).
  • A TensorRT faster lane inside this model needs the tensorrt Python package added to the image, an engine built for the serving GPU model with the same TensorRT build that runs it, and the torch steps (window rebuild, fbank) and execute_async_v3 on one non-default torch stream, so ordering needs no extra synchronisation (inference).
  • Moving to 25.06 or 26.05 changes the stub to Python 3.12; PyPI has torch 2.1.1 wheels only for cp38 to cp311 [18], so the exact lane would need a custom Python 3.10 stub or would have to stay on 24.10 (inference).

Sources

  1. python_backend README - https://github.com/triton-inference-server/python_backend/blob/r24.10/README.md - applies to r24.10.
  2. python_backend README - https://github.com/triton-inference-server/python_backend/blob/r26.05/README.md - applies to r26.05 (same text on main).
  3. python_backend README - https://github.com/triton-inference-server/python_backend/blob/r25.06/README.md - applies to r25.06.
  4. python_backend src/python_be.cc - https://github.com/triton-inference-server/python_backend/blob/r24.10/src/python_be.cc - applies to r24.10.
  5. python_backend src/infer_response.cc - https://github.com/triton-inference-server/python_backend/blob/r24.10/src/infer_response.cc - applies to r24.10.
  6. python_backend src/request_executor.cc - https://github.com/triton-inference-server/python_backend/blob/r24.10/src/request_executor.cc - applies to r24.10.
  7. python_backend instance-kind example - https://github.com/triton-inference-server/python_backend/blob/r24.10/examples/instance_kind/model.py - applies to r24.10.
  8. Decoupled Backends and Models - https://github.com/triton-inference-server/server/blob/r24.10/docs/user_guide/decoupled_models.md - applies to r24.10.
  9. Generate extension - https://github.com/triton-inference-server/server/blob/r24.10/docs/protocol/extension_generate.md - applies to r24.10.
  10. Triton core backend_model.cc (scheduler choice) and dynamic_batch_scheduler.cc (no batching: one request per payload) - https://github.com/triton-inference-server/core/blob/r24.10/src/backend_model.cc and https://github.com/triton-inference-server/core/blob/r24.10/src/dynamic_batch_scheduler.cc - applies to r24.10.
  11. Triton backend README, threading section - https://github.com/triton-inference-server/backend/blob/r24.10/README.md - applies to r24.10.
  12. CUDA C++ Programming Guide, section 3.2.1 Initialization - https://docs.nvidia.com/cuda/archive/12.6.2/cuda-c-programming-guide/index.html - applies to CUDA 12.6.2.
  13. TensorRT Python API, IExecutionContext - https://docs.nvidia.com/deeplearning/tensorrt/10.x.x/_static/python-api/infer/Core/ExecutionContext.html - applies to TensorRT 10.16.1 (10.x docs).
  14. TensorRT, The Python API (inference walkthrough) - https://docs.nvidia.com/deeplearning/tensorrt/10.x.x/inference-library/python-api-docs.html - applies to TensorRT 10.x docs.
  15. TensorRT, Version Compatibility (and hardware compatibility) - https://docs.nvidia.com/deeplearning/tensorrt/10.x.x/inference-library/version-compatibility.html - applies to TensorRT 10.x docs.
  16. PyPI tensorrt-cu12-bindings 10.5.0 files - https://pypi.org/project/tensorrt-cu12-bindings/10.5.0/#files - checked 9 October 2026.
  17. PyTorch torch.utils.dlpack and torch.cuda.current_stream - https://pytorch.org/docs/2.1/dlpack.html and https://pytorch.org/docs/2.1/generated/torch.cuda.current_stream.html - applies to PyTorch 2.1.
  18. PyPI torch 2.1.1 files - https://pypi.org/project/torch/2.1.1/#files - checked 9 October 2026.
  19. Contents of the images nvcr.io/nvidia/tritonserver:{24.10,25.06,26.05}-py3, 9 October 2026 (python3 --version, import tensorrt, ldd on the stub, dpkg -l): read from the images, not documentation.