# Непрерывная пакетная обработка

Continuous batching максимизирует использование GPU, динамически пересобирая батч на каждом шаге генерации. По мере завершения запросов новые добавляются в батч немедленно, а не ждут завершения всего батча. GPU остаётся загруженным, а пропускная способность — высокой.

> [!TIP]
> Для производственных развёртываний используйте [transformers serve](./serve-cli/serving_optims#continuous-batching). Он основан на [ContinuousBatchingManager](/docs/transformers/main/ru/main_classes/text_generation#transformers.ContinuousBatchingManager) и предоставляет HTTP-эндпоинт, совместимый с OpenAI.

## generate_batch

Поддержка непрерывного батчинга реализована через [generate_batch()](/docs/transformers/main/ru/main_classes/text_generation#transformers.ContinuousMixin.generate_batch). Передайте список токенизированных промптов и получите результаты по всем из них после завершения обработки. `generate_batch` управляет планированием внутри и блокируется до завершения всех запросов.

Для сценариев обслуживания и потоковой передачи используйте [ContinuousBatchingManager](#continuousbatchingmanager) напрямую для управления запросами.

```py
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from transformers.generation import ContinuousBatchingConfig, GenerationConfig

model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen3-4B",
    attn_implementation="flash_attention_2",
    device_map="auto",
    dtype=torch.bfloat16,
)
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen3-4B")

prompts = [
    "What's up?",
    "Name a cat breed.",
    "Write a detailed history of quantum mechanics.",
]
inputs = [tokenizer.encode(p) for p in prompts]

generation_config = GenerationConfig(
    max_new_tokens=64,
    eos_token_id=tokenizer.eos_token_id,
)

outputs = model.generate_batch(inputs=inputs, generation_config=generation_config)

for request_id, output in outputs.items():
    text = tokenizer.decode(output.generated_tokens, skip_special_tokens=True)
    print(f"[{request_id}] {text}")
```

## ContinuousBatchingManager

[ContinuousBatchingManager](/docs/transformers/main/ru/main_classes/text_generation#transformers.ContinuousBatchingManager) запускает фоновый поток и позволяет отправлять запросы и получать результаты независимо друг от друга. На каждом шаге генерации он проверяет завершённые запросы и планирует новые для добавления в батч. Это полезно для потоковой передачи, обслуживания в реальном времени или отправки запросов по мере их поступления.

Используйте [continuous_batching_context_manager()](/docs/transformers/main/ru/main_classes/text_generation#transformers.ContinuousMixin.continuous_batching_context_manager) для безопасного запуска и остановки менеджера. В примере ниже используются входные данные переменной длины. Как только самый короткий промпт завершается, он покидает батч, а более длинные промпты продолжают генерацию. При статическом батчинге пришлось бы дополнять все промпты до одинаковой длины. Continuous batching освобождает завершённый промпт, позволяя сразу начать обработку следующего.

```py
with model.continuous_batching_context_manager(generation_config=generation_config) as manager:
    manager.add_request(
        input_ids=tokenizer.encode("Write a detailed history of quantum mechanics."),
        request_id="long",
        max_new_tokens=512,
    )
    manager.add_request(
        input_ids=tokenizer.encode("What's up?"),
        request_id="short_0",
        max_new_tokens=32,
    )
    manager.add_request(
        input_ids=tokenizer.encode("Name a cat breed."),
        request_id="short_1",
        max_new_tokens=32,
    )

    for result in manager:
        text = tokenizer.decode(result.generated_tokens, skip_special_tokens=True)
        print(f"[{result.request_id}] {text}")
```

Вы также можете вызвать [init_continuous_batching()](/docs/transformers/main/ru/main_classes/text_generation#transformers.ContinuousMixin.init_continuous_batching), чтобы самостоятельно управлять жизненным циклом.

```py
manager = model.init_continuous_batching(generation_config=generation_config)
manager.start()

# submit and retrieve requests...
```

### Остановка менеджера

Менеджер запускает фоновый поток и управляет распределёнными ресурсами. Завершение работы происходит в два этапа, что позволяет выбрать, как обрабатывать незавершённые задачи.

Вызовите [stop()](/docs/transformers/main/ru/main_classes/text_generation#transformers.ContinuousBatchingManager.stop), чтобы остановить фоновый поток. По умолчанию менеджер прекращает приём новых запросов и ожидает завершения всех запросов в очереди и активных запросов перед выходом из потока.

```py
manager.stop()
```

Передайте `hard_stop=True`, чтобы немедленно прекратить выполнение ожидающих задач. Очередные и активные запросы завершаются с ошибкой `RuntimeError` вместо нормального завершения.

```py
manager.stop(hard_stop=True)
```

После вызова `stop` методы [add_request()](/docs/transformers/main/ru/main_classes/text_generation#transformers.ContinuousBatchingManager.add_request) и [add_requests()](/docs/transformers/main/ru/main_classes/text_generation#transformers.ContinuousBatchingManager.add_requests) отклоняют новые запросы и записывают предупреждение в журнал. Вы можете снова вызвать `start`, чтобы запустить новую сессию генерации с тем же менеджером.

Вызовите [destroy()](/docs/transformers/main/ru/main_classes/text_generation#transformers.ContinuousBatchingManager.destroy) для освобождения распределённых ресурсов. `destroy` сначала останавливает менеджер, если он ещё работает, после чего перезапустить его невозможно. Используйте этот метод, когда работа с непрерывным батчингом завершена на всё время жизни процесса.

```py
manager.destroy()
```

[continuous_batching_context_manager()](/docs/transformers/main/ru/main_classes/text_generation#transformers.ContinuousMixin.continuous_batching_context_manager) управляет этим процессом. При выходе он вызывает `stop`, а `destroy` — только если вы не передали `persistent_manager=True` для кэширования менеджера в модели для следующей сессии.

### Добавление запросов

[add_request()](/docs/transformers/main/ru/main_classes/text_generation#transformers.ContinuousBatchingManager.add_request) отправляет один запрос. Укажите `request_id` или позвольте менеджеру сгенерировать его автоматически.

```py
manager.add_request(input_ids=input_ids, request_id="my_request")
```

[add_requests()](/docs/transformers/main/ru/main_classes/text_generation#transformers.ContinuousBatchingManager.add_requests) отправляет пакет запросов сразу. Он автоматически сортирует входные данные, чтобы максимизировать попадания в кэш префиксов, когда включено разделение блоков.

```py
manager.add_requests(inputs=inputs)
```

Отмените запрос с помощью [cancel_request()](/docs/transformers/main/ru/main_classes/text_generation#transformers.ContinuousBatchingManager.cancel_request).

```py
manager.cancel_request(request_id="my_request")
```

### Параметры выборки для каждого запроса

Включите `per_request_processors`, чтобы применять `temperature`, `top_k` и `top_p` независимо для каждого запроса в рамках одного прохода, что позволяет использовать разные параметры выборки для разных запросов (например, креативные выводы с высокой температурой и точные выводы с низкой температурой).

```py
cb_config = ContinuousBatchingConfig(per_request_processors=True)

# each request gets its own sampling parameters
manager.add_request(input_ids=inputs_a, temperature=0.9, top_p=0.95)
manager.add_request(input_ids=inputs_b, temperature=0.1, top_k=10)
```

Каждый параметр в [GenerationConfig](/docs/transformers/main/ru/main_classes/text_generation#transformers.GenerationConfig) должен иметь значение, отличное от значения по умолчанию, чтобы соответствующий logits-процессор был создан во время выполнения. Например, установите `temperature` в значение, отличное от `None` или `1`, чтобы обеспечить управление температурой для каждого запроса. Запросы с температурой `1` по-прежнему можно создавать впоследствии.

### Получение результатов

Итерируйте по менеджеру, чтобы получать результаты по мере их поступления.

```py
for result in manager:
    print(tokenizer.decode(result.generated_tokens, skip_special_tokens=True))
```

[get_result()](/docs/transformers/main/ru/main_classes/text_generation#transformers.ContinuousBatchingManager.get_result) извлекает следующий результат из очереди вывода. Передайте `request_id`, чтобы отфильтровать конкретный запрос. Если следующий результат в очереди не соответствует, он возвращается в очередь, а метод возвращает `None`.

```py
# next available result
result = manager.get_result()

# filter for a specific request
result = manager.get_result(request_id="my_request")
```

### Потоковая передача

Установите `streaming=True` в запросе, затем используйте [request_id_iter()](/docs/transformers/main/ru/main_classes/text_generation#transformers.ContinuousBatchingManager.request_id_iter), чтобы перебирать частичные результаты по мере генерации токенов.

```py
from transformers.generation.continuous_batching import RequestStatus

manager.add_request(input_ids=input_ids, request_id="streamed", streaming=True)

for chunk in manager.request_id_iter(request_id="streamed"):
    token = tokenizer.decode(chunk.generated_tokens[-1:], skip_special_tokens=True)
    print(token, end="", flush=True)
    if chunk.status == RequestStatus.FINISHED:
        break
```

## ContinuousBatchingConfig

[ContinuousBatchingConfig](/docs/transformers/main/ru/main_classes/continuous_batching#transformers.ContinuousBatchingConfig) управляет KV-кэшем, планированием, CUDA-графиками, использованием памяти и другими параметрами. Передайте его в `generate_batch` или [init_continuous_batching()](/docs/transformers/main/ru/main_classes/text_generation#transformers.ContinuousMixin.init_continuous_batching) с аргументом `continuous_batching_config`, отдельно от [GenerationConfig](/docs/transformers/main/ru/main_classes/text_generation#transformers.GenerationConfig). Эти два конфигурационных объекта описывают разные вещи. [GenerationConfig](/docs/transformers/main/ru/main_classes/text_generation#transformers.GenerationConfig) ориентирован на модель и содержит параметры выборки и остановки, тогда как [ContinuousBatchingConfig](/docs/transformers/main/ru/main_classes/continuous_batching#transformers.ContinuousBatchingConfig) ориентирован на аппаратное обеспечение и содержит параметры памяти и планирования.

> [!WARNING]
> Назначение `continuous_batching_config` в [GenerationConfig](/docs/transformers/main/ru/main_classes/text_generation#transformers.GenerationConfig) устарело, вызывает `FutureWarning` и будет удалено в v5.19.

По умолчанию `max_batch_tokens` равно `8192`, ограничено доступной памятью GPU и никогда не опускается ниже `256`, а `num_blocks` заполняет оставшуюся память. Используйте таблицу ниже, чтобы подобрать подходящие параметры.

| Возможность | Память | Пропускная способность | Задержка |
|---|---|---|---|
| `max_memory_percent` / `page_size` | ✓ управляет бюджетом KV | | |
| `max_batch_tokens` | ↑ более крупные входные буферы | ✓ более крупные батчи префилла | ✓ TTFT при ограничении на prefill |
| `scheduler` | | ✓ политика планирования | ✓ TTFT |
| CUDA-графы | ↑ хранилище графа | ✓ меньшие накладные расходы на диспетчеризацию | ✓ |
| Асинхронная пакетная обработка | ↑ ~2× буферы ввода-вывода | ✓ перекрывает CPU/GPU | |
| Компиляция | ↑ только warmup-time | ✓ более быстрые прямые проходы | ✓ |
| Быстрый путь декодирования | ↑ таблица блоков на запрос | ✓ более быстрые шаги только декодирования | ✓ |
| Размещение на CPU | ↑ закрепленная память CPU | ✓ пропускает некоторые повторные префиллы | |
| Кэширование префиксов | ↓ общие блоки KV | ✓ пропускает избыточный prefill | ✓ TTFT |
| Страничная аттенция | ↓ без фрагментации | ✓ динамический состав батча | |
| Скользящее окно | ↓ ограниченный KV на слой | | |
| Процессоры для каждого запроса | | ✓ смешанные параметры выборки в рамках одной пачки | |
| `max_requests_per_batch` | ↓ ограничивает буфер логитов | ✓ ограничивает размер батча | |
| `safety_margin` | | | ✓ защищает задержку декодирования |

```py
from transformers.generation import ContinuousBatchingConfig

cb_config = ContinuousBatchingConfig(
    max_memory_percent=0.8,
    page_size=256,           # KV cache page size in tokens
    scheduler_type="fifo",        # "fifo" or "prefill_first"
)

outputs = model.generate_batch(
    inputs=inputs,
    generation_config=generation_config,
    continuous_batching_config=cb_config,
)
```

### Размер страницы кэша KV

`page_size` определяет, сколько токенов KV хранит полная страница внимания для одного слоя. Значение должно быть не менее `4`, иначе при инициализации будет выброшено исключение `ValueError`. Значение по умолчанию `256` подходит для большинства задач.

> [!NOTE]
> Параметр `block_size` устарел и заменён на `page_size`.

Увеличение размера страницы сокращает накладные расходы на управление памятью, но приводит к потере места, если последовательность обрывается посередине страницы. Меньшие страницы тратят меньше места, но требуют больше накладных расходов. Как правило, настраивать `page_size` не нужно. Если в вашей рабочей нагрузке много коротких запросов, попробуйте уменьшить значение. Если же есть несколько очень длинных запросов, попробуйте увеличить значение. О том, как страницы, блоки и фрагменты соотносятся между собой, см. в разделе [Управление памятью](./continuous_batching_architecture#memory-management).

```py
cb_config = ContinuousBatchingConfig(page_size=128)
```

### Размер батча префилла

`max_batch_tokens` задаёт бюджет токенов для одного прохода вперёд, то есть максимальное количество query-токенов, которые модель обрабатывает за один раз. Увеличение бюджета позволяет упаковать больше токенов промпта в каждый prefill, что повышает пропускную способность prefill и снижает время до первого токена в нагрузках с преобладанием промптов. Планировщик распределяет промпты, превышающие бюджет, по нескольким шагам. См. [chunked prefill](./continuous_batching_architecture#chunked-prefill) для описания обработки промптов, превышающих бюджет.

По умолчанию `max_batch_tokens` равно `8192`. Значение ограничивается доступной памятью GPU, чтобы входные тензоры в рамках батча не вытесняли KV-кэш, и никогда не опускается ниже `256`. На GPU с небольшим количеством свободной памяти это ограничение может снизить значение ниже `8192`.

`max_batch_tokens` и `num_blocks` используют один и тот же бюджет памяти, поэтому они находятся в обратной зависимости друг от друга. Увеличение бюджета токенов выделяет более крупные входные буферы и оставляет меньше блоков кэша KV для одновременных или длинных запросов. Увеличивайте `max_batch_tokens`, чтобы повысить пропускную способность префилла, если у вас есть свободная память, и уменьшайте его, чтобы освободить память для дополнительных блоков KV или избежать ошибок нехватки памяти.

```py
cb_config = ContinuousBatchingConfig(max_batch_tokens=16384)
```

### Ограничения по пакетам и планированию

`max_requests_per_batch` ограничивает количество запросов, обрабатываемых за один проход. Модель формирует тензор logits, размер которого определяется числом токенов в батче и размером словаря; для сэмплирования он приводится к `fp32`, что удваивает занимаемую память. Поскольку для каждого запроса требуется предсказание только одного следующего токена, уменьшение этого лимита позволяет сделать тензор компактнее и избежать ошибок нехватки памяти (out-of-memory) при батчах с преобладанием prefill и большими словарями. По умолчанию значение равно числу отправленных промптов, с запасным значением 1024, после чего оно ограничивается в соответствии с `max_batch_tokens` и `num_blocks`.

```py
cb_config = ContinuousBatchingConfig(max_requests_per_batch=256)
```

`safety_margin` резервирует долю свободного кэша для активных запросов. Когда свободная ёмкость падает ниже `safety_margin` (доли от общего размера кэша), планировщик перестаёт принимать новые prefill-запросы и продолжает только декодирование уже запущенных запросов. Это позволяет завершить активную работу в приоритете перед началом новых запросов, что защищает задержку декодирования и откладывает [offloading](./continuous_batching_architecture#offloading). Значение должно находиться в диапазоне от `0` до `1`, где `0` отключает резервирование.

```py
cb_config = ContinuousBatchingConfig(safety_margin=0.15)
```

Значение по умолчанию зависит от планировщика. Для планировщика FIFO оно равно `0.15`, а для `prefill_first` — `0.0`. О том, как этот запас взаимодействует с планированием, см. в разделе [Admission](./continuous_batching_architecture#admission).

### Логарифмические вероятности

[ContinuousBatchingConfig](/docs/transformers/main/ru/main_classes/continuous_batching#transformers.ContinuousBatchingConfig) возвращает лог-вероятность каждого сгенерированного токена, если `return_logprobs=True`. Это полезно для RL, где logprobs используются как вход для некоторых циклов обучения.

```py
cb_config = ContinuousBatchingConfig(return_logprobs=True)

# generate_batch()

for request_id, output in outputs.items():
    for token_id, log_prob in zip(output.generated_tokens, output.logprobs):
        token = tokenizer.decode([token_id])
        print(f"{token} | logprob: {log_prob}")
```

### CUDA-графы

CUDA-графы устраняют накладные расходы на диспетчеризацию на CPU, записывая граф выполнения на GPU один раз и воспроизводя его для батчей с совпадающими формами. Включите их явно с помощью `use_cuda_graph=True`.

```py
cb_config = ContinuousBatchingConfig(use_cuda_graph=True)
```

Когда менеджер активен, он дополняет длины запросов и KV до фиксированных интервалов, чтобы формы повторялись, а графы переиспользовались. Меньшие значения `q_padding_interval_size` и `kv_padding_interval_size` снижают потери вычислительных ресурсов на дополнения, но это означает, что графу приходится записывать и хранить больше уникальных форм, что требует больше памяти.

```py
cb_config = ContinuousBatchingConfig(
    use_cuda_graph=True,
    q_padding_interval_size=64,
    kv_padding_interval_size=16384,
)
```

### Асинхронная пакетная обработка

Асинхронное батчинг (Async batching) перекрывает планирование следующего батча на CPU с вычислениями текущего батча на GPU. Для этого требуются CUDA-графы, а объём VRAM, используемый для входных тензоров, примерно удваивается.

```py
cb_config = ContinuousBatchingConfig(
    use_cuda_graph=True,
    use_async_batching=True,
)
```

### Компиляция

`default_compile_level` применяет `torch.compile` к forward-проходам модели. Компиляция обменивает разовые затраты на прогрев на ускорение forward-проходов во время генерации. Более высокие уровни запускают более агрессивную оптимизацию, что повышает пропускную способность, но увеличивает время прогрева. Для тестов и итераций бенчмарков держите уровень низким, а для длительных сценариев обслуживания, где затраты на прогрев окупаются за множество запросов, повышайте его.

Значение уровня варьируется от `0` до `3`. Уровень `0` является значением по умолчанию и полностью пропускает компиляцию.

| Уровень | `mode` | `dynamic` | Компромисс |
|---|---|---|---|
| 0 | — | — | Компиляция не выполняется (по умолчанию), самый быстрый запуск |
| 1 | `default` | `True` | Небольшое ускорение, короткая разминка |
| 2 | `max-autotune-no-cudagraphs` | `True` | Большее ускорение, более длительная разминка |
| 3 | `max-autotune-no-cudagraphs` | `False` | Максимальная пропускная способность, самая длительная разминка |

```py
cb_config = ContinuousBatchingConfig(default_compile_level=1)
```

Уровень предоставляет конфигурацию по умолчанию [CompileConfig](/docs/transformers/main/ru/internal/generation_utils#transformers.CompileConfig) для путей выполнения varlen и decode. Она применяется только к тому пути, для которого не задана явная конфигурация, поэтому `varlen_compile_config` и `decode_compile_config` имеют приоритет, если они установлены. В случае использования FlashAttention путь varlen пропускает компиляцию, так как `max_length_k` вызывает частую перекомпиляцию, поэтому в этом случае уровень влияет только на путь decode.

### Быстрый путь декодирования

Если пакет содержит только запросы декодирования (один токен запроса на последовательность), менеджер может направить выполнение в ядро `flash_attn_with_kvcache` вместо ядра переменной длины. Это быстрее, чем путь varlen, поскольку ядро читает и записывает постраничную KV-кэш на месте через таблицу блоков, а не через ручное обновление. Подробности на уровне ядра см. в разделе [Paged attention](./paged_attention).

Быстрый путь определяется размером `max_blocks_per_request`, который задаёт размеры таблицы блоков для каждого запроса. По умолчанию это значение выводится автоматически. Если в менеджере заданы `max_prompt_length` и `max_generated_length`, таблица блоков рассчитывается так, чтобы вместить максимальную длину последовательности. В противном случае используется значение по умолчанию (32 блока на запрос).

Установите `max_blocks_per_request` в конкретное значение, чтобы явно задать размер таблицы блоков. Это полезно, если вам известна максимальная длина последовательности для каждого запроса и вы хотите ограничить затраты памяти на таблицу блоков.

```py
cb_config = ContinuousBatchingConfig(max_blocks_per_request=64)
```

Установите `max_blocks_per_request=0`, чтобы отключить быстрый путь и заставить каждый батч проходить через varlen-ядро. Это восстанавливает поведение по умолчанию до его изменения и полезно, когда быстрый путь недоступен для вашей реализации внимания (менеджер также отключает его автоматически, если базовое ядро использовать нельзя).

```py
cb_config = ContinuousBatchingConfig(max_blocks_per_request=0)
```

Быстрый путь опирается на ядро `flash_attn_with_kvcache`, которое доступно для двух комбинаций устройства и реализации внимания.

| Устройство | `attn_implementation` |
|---|---|
| CUDA | `flash_attention_2` или `flash_attention_3` |
| XPU | [flash_attention_2](https://huggingface.co/kernels-community/flash-attn2) |

Для любой другой комбинации или в случае, когда ядро не может быть импортировано, менеджер переключается на путь varlen. Предупреждение записывается в журнал только при явном задании `max_blocks_per_request`.

Full attention поддерживает таблицы блоков ядра, но sliding attention — нет. Если в модели есть хотя бы один слой sliding attention, включая гибридные модели, которые также используют full attention, при инициализации значение `max_blocks_per_request` принудительно устанавливается в `0`, а любое ненулевое значение, заданное вами, будет переопределено. В этом случае менеджер записывает в журнал, что быстрый путь декодирования недоступен, поскольку некоторые типы внимания в модели не поддерживают таблицы блоков ядра.

### Размещение на CPU

CPU offloading копирует вытесненные блоки KV-кэша в предварительно выделенный закреплённый буфер CPU, когда GPU KV-кэш заполнен. После того, как в кэше освобождается место, менеджер копирует блоки обратно на GPU и возобновляет запрос без повторного вычисления его промпта и сгенерированных токенов.

Установите `cpu_offload_space` в размер области подкачки на CPU в ГиБ. Значение по умолчанию, `0.0`, отключает выгрузку на CPU.

```py
cb_config = ContinuousBatchingConfig(cpu_offload_space=8.0)
```

По умолчанию `cpu_offload_space_safety_threshold=0.8` ограничивает запрашиваемое пространство 80% доступной системной оперативной памяти, если установлен `psutil`. Установите `cpu_offload_space=None`, чтобы размер пула подкачки определялся на основе порога безопасности.

### Тайм-аут параллелизма тензоров

При тензорном параллелизме менеджер создаёт группу CPU-коммуникации для координации отправки запросов, их отмены и завершения работы между рангами. `cpu_group_timeout` ограничивает время, в течение которого коллективная операция в этой группе может блокироваться, прежде чем процесс завершится с ошибкой. Если один ранг зависает, таймаут не даёт остальным ждать бесконечно.

Установите более длительный тайм-аут для рабочих нагрузок, которые редко выполняют коллективные операции, или передайте `None`, чтобы отключить его.

```py
cb_config = ContinuousBatchingConfig(cpu_group_timeout=600.0)
```

### Кэширование префиксов

Когда несколько запросов имеют общий префикс, например системный промпт, менеджер повторно использует их блоки KV-кэша вместо повторного вычисления. `allow_block_sharing` по умолчанию установлено в `True`. Разделение префикса активно только тогда, когда все типы аллокаторов внимания поддерживают его, поэтому его используют только модели с полным вниманием. Чисто скользящие и гибридные модели отключают его, даже если `allow_block_sharing` равно `True`.

```py
cb_config = ContinuousBatchingConfig(
    allow_block_sharing=True,  # default
)
```

## Выберите бэкенд внимания

Continuous batching поддерживает FlashAttention, SDPA и `"paged|eager"`. Укажите `attn_implementation` при загрузке модели.

| Бэкенд | `attn_implementation` | Требования |
|---|---|---|
| FlashAttention 2 | "flash_attention_2" | пакет `flash-attn` или `kernels` |
| FlashAttention 3 | "flash_attention_3" | `flash-attn-3` или пакет `kernels` |
| SDPA (нативная реализация PyTorch) | "sdpa" | None |
| Eager | "paged&#124;eager" | None |

```py
model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen3-4B",
    attn_implementation="flash_attention_2",
    device_map="auto",
    dtype=torch.bfloat16,
)
```

Continuous batching работает быстрее с FlashAttention, чем с eager или SDPA, в основном потому, что FlashAttention не требует attention mask. Если модель поддерживает FlashAttention, то модель, загруженная с `"eager"` или `"sdpa"`, переключается на FlashAttention 3, а если FlashAttention 3 не установлен — на FlashAttention 2, и выводится предупреждение. Если ни одна из версий не установлена, backend не меняется.

Чтобы сохранить eager или SDPA, например при тестировании, установите `auto_switch_to_flash=False` в [ContinuousBatchingConfig](/docs/transformers/main/ru/main_classes/continuous_batching#transformers.ContinuousBatchingConfig). Если бэкенд остаётся `"eager"`, continuous batching выполняется в режиме `"paged|eager"`.

## Параллелизм тензоров

Для моделей, которые слишком велики, чтобы поместиться на одном GPU, распределите веса по устройствам с помощью тензорного параллелизма. Укажите количество устройств с помощью `DistributedConfig(tp_size=N)`. Непрерывная пакетная обработка (continuous batching) читает размер тензорного параллелизма из модели, чтобы определить размер страничной KV-кэши для каждого шарда. Список поддерживаемых архитектур и описание работы шардирования приведены в разделе [Тензорный параллелизм](./tensor_parallelism).

```py
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, DistributedConfig
from transformers.generation import ContinuousBatchingConfig, GenerationConfig

distributed_config = DistributedConfig(tp_size=4)
model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen3-32B",
    attn_implementation="flash_attention_2",
    distributed_config=distributed_config,
)
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen3-32B")

inputs = [tokenizer.encode(p) for p in ["What's up?", "Name a cat breed."]]
generation_config = GenerationConfig(max_new_tokens=64, eos_token_id=tokenizer.eos_token_id)

outputs = model.generate_batch(inputs=inputs, generation_config=generation_config)
```

Запустите скрипт с помощью `torchrun`, установив `--nproc-per-node` в количество GPU, на которые вы хотите распределить шарды.

```shell
torchrun --nproc-per-node 4 cb_tp.py
```

Размер тензорного параллелизма должен делить `num_key_value_heads` модели (проверьте конфигурацию модели). В противном случае paged cache будет вызывать ошибку при запуске, поэтому выберите подходящее значение `--nproc-per-node`.

> [!WARNING]
> Не задавайте `device_map` вместе с `distributed_config`. Эти параметры конфликтуют, так как `device_map` размещает целые модули на конкретных GPU, тогда как тензорный параллелизм распределяет те же параметры по всем GPU.

## Внимание со скользящим окном

Модели со скользящим окном внимания (Mistral, Gemma 2) работают с непрерывным батчингом. Скользящие слои используют `SlidingAttentionCacheAllocator`, который хранит только фиксированное окно последних токенов, перерабатывая небольшой набор страниц. Тип внимания определяется по `layer_types` или `sliding_window` модели, а не по [ContinuousBatchingConfig](/docs/transformers/main/ru/main_classes/continuous_batching#transformers.ContinuousBatchingConfig).

```py
from transformers import AutoConfig, AutoModelForCausalLM

config = AutoConfig.from_pretrained("google/gemma-2-2b")
config.sliding_window = 4096

model = AutoModelForCausalLM.from_pretrained(
    "google/gemma-2-2b",
    config=config,
    attn_implementation="sdpa",
    device_map="auto",
    dtype=torch.bfloat16,
)
```

[Кэширование префиксов](#prefix-caching) и [быстрый путь декодирования](#decode-fast-path) отключаются при наличии скользящего внимания, включая гибридные модели, которые также содержат слои полного внимания.

## Дальнейшие шаги

- Статья [Continuous batching blog post](https://huggingface.co/blog/continuous_batching) рассматривает кэширование KV, chunked prefill и динамическое планирование с приведением цифр бенчмарков производительности.
- Для более подробного изучения работы системы непрерывного батчинга см. документацию [Архитектура непрерывного батчинга](./continuous_batching_architecture).

