Anuvani-Diarization / src /triton_inference /docs /reference /03-dynamic-batching.md
StephennFernandes's picture
Anuvani-Diarization 1.0.0
63dd946
|
Raw History Blame Contribute Delete
11.3 kB
# Dynamic batching, ragged inputs and client-side packing
This note covers what Triton's dynamic batcher needs, what it does to a model with `max_batch_size: 0`, how ragged
inputs and batch inputs work, how the Python backend receives a dynamic batch, and the alternative of packing
blocks in the client. The facts apply to Triton 24.10 (the `r24.10` branches of `server`, `core`, `common` and
`python_backend`); where core `main` or `r25.06` was checked, the text says so. Behaviour read from source code is
marked as such. Checked 9 October 2026.
## What the dynamic batcher requires
- Batching by the dynamic or sequence batcher needs the model's batch dimension to be the first dimension of every
input and output; `max_batch_size` is then set to 1 or more. Models that do not support batching "in the specific
ways described above" must set `max_batch_size` to zero [1].
- Dynamic batching combines requests in the server. It is enabled per model with `dynamic_batching { }`, and its
settings control the preferred batch sizes, how long a request may wait for others, and queue properties such as
queue size, priorities and time-outs [1].
- "By default, the requests can be dynamically batched only if each input has the same shape across the requests" [2].
The proto says the same of `allow_ragged_batch`: false means two requests batch only if the tensor has the same
shape in both [3]. In core, every variable-size input (any `-1`) that is not marked `allow_ragged_batch`, and every
shape tensor, is put in an "enforce equal shape" set used when forming a batch [4].
- `preferred_batch_size`: "should not be used for most models"; the batcher forms the largest preferred size it can,
otherwise the largest size up to `max_batch_size` [1]. If none is given, core fills in `max_batch_size`, but only
when `max_batch_size > 0` [5].
- `max_queue_delay_microseconds` (default 0) lets a batcher hold a batch that is not of a maximum or preferred size
until the oldest request has waited that long [1, 3].
- `preserve_ordering` returns responses in request order [1, 3]. Validation rejects it together with more than one
priority level, or with a queue time-out whose action is `DELAY` [5].
- Custom batching rules can be added through five `TRITONBACKEND_ModelBatch*` functions in a shared library named by
the `TRITON_BATCH_STRATEGY_PATH` parameter [1].
- Without any scheduling choice the model gets the default scheduler, which "simply distributes inference requests to
all model instances" [1]. In core this is the dynamic batch scheduler with batching switched off; each request goes
straight to the rate limiter as its own payload [4, 6].
## What happens with `max_batch_size: 0`
- Validation in core r24.10 rejects a negative `max_batch_size` and any `preferred_batch_size` larger than
`max_batch_size` ("dynamic batching preferred size must be <= max batch size"), but it does not reject a
`dynamic_batching` block on a model with `max_batch_size: 0` [5].
- When `dynamic_batching` is present, core creates the dynamic batch scheduler with batching enabled and the model's
`max_batch_size` [4]. The scheduler stores `max_batch_size_((size_t)std::max(1, max_batch_size))`, so a model with
`max_batch_size: 0` is given an effective limit of one request per execution [6]. The same line is on core `main` [7].
- Read together (source reading, not a documented contract): such a model gets the batcher's queue (priority levels,
queue policy, time-outs, `max_queue_size`) but never more than one request in an `execute()` call.
## Ragged inputs and batch inputs
- `allow_ragged_batch: true` on an input means two requests may batch even if that tensor's shape differs [3].
- "How ragged input are processed in a batch of requests depends on the backend implementation." The ONNX Runtime,
TensorFlow, PyTorch and TensorRT backends require ragged inputs to be 1-D and concatenate the requests' inputs into
one 1-D tensor [2]. The Python backend is not on that list [2].
- Because the concatenated tensor does not record where each request starts and ends, Triton can generate batch
inputs [2]. The kinds are [3]: `BATCH_ELEMENT_COUNT` (element count of the source input, shape [1]);
`BATCH_ACCUMULATED_ELEMENT_COUNT` (running total); `BATCH_ACCUMULATED_ELEMENT_COUNT_WITH_ZERO` (the same with a
leading 0 on the first request); `BATCH_MAX_ELEMENT_COUNT_AS_SHAPE` (shape [max element count], data
uninitialised); `BATCH_ITEM_SHAPE` and `BATCH_ITEM_SHAPE_FLATTEN` (each request's source shape). The batch input
data type can be `TYPE_INT32` or `TYPE_FP32` [3].
- Worked example from the docs: requests of shapes `[1, 3]`, `[1, 4]` and `[1, 5]` become one tensor of shape `[12]`,
and a `BATCH_ACCUMULATED_ELEMENT_COUNT` input named `INDEX` holds `[3, 7, 12]` [2].
## How the Python backend receives a dynamic batch
- `execute()` "receives a batch of pb_utils.InferenceRequest as a length N array" and must return exactly N
responses, one per request, in the same order [8]. The README's template says "Every Python backend must iterate
through list of requests" [8]. Each request keeps its own input tensors; nothing is concatenated for the model.
- By default the Python backend moves every input tensor to CPU before the model sees it;
`FORCE_CPU_ONLY_INPUT_TENSORS: "no"` lets inputs stay where they were last used [8].
- `python_be.cc` on r24.10 counts the batch size per request (first dimension when `max_batch_size > 0`, otherwise 1
per request) and contains no reference to batch inputs [9]. Inference: batch inputs are not generated for Python
models, so a Python model has to work out the boundaries from the request list itself.
## Why the batcher cannot pack the diarization requests (inference)
The points below are reasoning from the rules above, not documented statements.
- `diar_nn_g<i>` has `max_batch_size: 0`, so there is no batch dimension, and a `dynamic_batching` block would give it
a queue but still one request per execution [5, 6].
- Giving the inputs a batch dimension does not help on its own: `SPAN`, `OFFSETS`, `VALID`, `WEIGHTS` and `IDX` vary
in length, so without `allow_ragged_batch` two requests would batch only when every length matched exactly [2, 4].
- With `allow_ragged_batch`, the Python backend still delivers separate request objects [8]. The model would have
to group requests by `OP`, concatenate spans and rebase `OFFSETS` itself, which is the packing work in a new place.
- The batcher does not look at input values, so one batch could mix `OP` 0 (segmentation) and `OP` 1 or 2
(voiceprints), which cannot be one network call. Keeping them apart would need a custom batching library in C [1]
or one model per `OP`.
- The second call of a block depends on masks the engine computes from the first call, so the two calls of one block
can never share a batch; any `max_queue_delay_microseconds` would add directly to that dependency chain.
## Client-side packing
The alternative keeps `max_batch_size: 0` and lets the Rust engine put several blocks in one request: one longer
`SPAN`, and `OFFSETS`/`VALID` (and `WEIGHTS`/`IDX` for voiceprints) covering 8 x k windows. Trade-offs (inference):
- The engine already chooses which GPU gets each block, so it can also choose k per request; no server-side delay
or batching policy is involved, and batch composition is repeatable from the engine's side.
- Each packed request carries about k x 1.7 MB, holds k blocks of results until the whole request finishes, and uses
more GPU memory per call. Fewer, larger requests reduce per-request HTTP and Python overhead.
- Triton 24.10 has no HTTP size limit. The `--http-max-input-size` option of 25.06 and later (default 64 MiB, about
38 blocks of 1.74 MB) appears, from source, not to cover binary tensors; see 05-protocols-shared-memory.md.
- A packed request is a new batch shape for the networks. The exact lane asks only for the release's batch shapes,
so a packed shape belongs to the faster lane unless it is checked against the release first.
## Unverified
- That `dynamic_batching` on a `max_batch_size: 0` model yields one request per execution is read from core source;
no Triton document states it, so it may change.
- That the Python backend never generates batch inputs is inferred from the absence of any reference in
`python_be.cc` r24.10; no document says so either way.
## What this means for the diarization model
- The dynamic batcher cannot merge the engine's blocks into larger network calls while the model keeps
`max_batch_size: 0`, variable-length 1-D inputs and an `OP` switch; packing has to happen in the engine (inference).
- Adding `dynamic_batching { }` without `preferred_batch_size` to `diar_nn_g<i>` would leave `execute()` unchanged (one
request per call) and would add priority levels, `max_queue_size` and per-request time-outs [5, 6]. With 2
requests in flight per GPU from the engine, a queue limit mainly protects against other clients.
- A packed request of k blocks is one network call of 8 x k windows; for the faster lane this is the route to larger
batches, and its accuracy against the exact lane decides whether a packed shape is acceptable (inference).
- Moving the masks into the model (one request per block running both networks) would remove the second transfer of
the same `SPAN`, which is independent of batching (inference; see 02-ensemble-bls-single-model.md).
## Sources
1. Triton Model Configuration (Maximum Batch Size, Default Scheduler, Dynamic Batcher, Custom Batching) -
https://github.com/triton-inference-server/server/blob/r24.10/docs/user_guide/model_configuration.md - applies to r24.10.
2. Triton Ragged Batching - https://github.com/triton-inference-server/server/blob/r24.10/docs/user_guide/ragged_batching.md - applies to r24.10.
3. `model_config.proto` (ModelInput.allow_ragged_batch, BatchInput, ModelQueuePolicy, ModelDynamicBatching) -
https://github.com/triton-inference-server/common/blob/r24.10/protobuf/model_config.proto - applies to r24.10.
4. Core `TritonModel::SetConfiguredScheduler` -
https://github.com/triton-inference-server/core/blob/r24.10/src/backend_model.cc#L797-L847 - applies to r24.10.
5. Core `NormalizeModelConfig` and `ValidateModelConfig` -
https://github.com/triton-inference-server/core/blob/r24.10/src/model_config_utils.cc#L734-L745 and
https://github.com/triton-inference-server/core/blob/r24.10/src/model_config_utils.cc#L1410-L1498 - applies to r24.10.
6. Core dynamic batch scheduler (constructor at line 92; non-batching path at lines 240-251) -
https://github.com/triton-inference-server/core/blob/r24.10/src/dynamic_batch_scheduler.cc - applies to r24.10.
7. Core dynamic batch scheduler on `main` (GitHub code search hit, commit 00e4e60) -
https://github.com/triton-inference-server/core/blob/main/src/dynamic_batch_scheduler.cc - applies to main, 9 October 2026.
8. Python backend README (`execute`, Default Mode, Input Tensor Device Placement) -
https://github.com/triton-inference-server/python_backend/blob/r24.10/README.md - applies to r24.10.
9. Python backend `CheckIncomingRequests` -
https://github.com/triton-inference-server/python_backend/blob/r24.10/src/python_be.cc#L65-L113 - applies to r24.10.