From c83651aba41684e41f215cf4036278c0d9e739cf Mon Sep 17 00:00:00 2001 From: Wanli Jiang <35160485+Wanli-Jiang@users.noreply.github.com> Date: Tue, 30 Jun 2026 04:22:54 +0000 Subject: [PATCH 1/3] [None][chore] Remove TensorRT examples and docs, add migration guide Remove legacy TensorRT-backend example directories and documentation pages, add docs/source/legacy/tensorrt-backend-removal.md migration guide, and drop TensorRT references from the surviving docs (index, release notes, torch arch/kv-cache overviews, and two blogs). Pure docs/examples change: no Python or C++ source is touched, so the package build and import are unaffected. First of the incremental PRs that split Freddy's TensorRT-removal draft (QiJune/TensorRT-LLM#20). Signed-off-by: Wanli Jiang <35160485+Wanli-Jiang@users.noreply.github.com> --- ...practice_on_DeepSeek-R1_in_TensorRT-LLM.md | 4 +- docs/source/blogs/XQA-kernel.md | 2 +- docs/source/commands/trtllm-build.rst | 7 - .../images/8x_l20_L40S_node_architecture.png | Bin .../lowprecision-allreduce.md} | 21 +- docs/source/index.rst | 6 +- .../legacy/advanced/disaggregated-service.md | 86 - docs/source/legacy/advanced/executor.md | 153 -- .../legacy/advanced/expert-parallelism.md | 30 - docs/source/legacy/advanced/gpt-attention.md | 343 --- docs/source/legacy/advanced/gpt-runtime.md | 202 -- .../source/legacy/advanced/graph-rewriting.md | 197 -- .../images/disaggregated-service_usage.png | Bin 39888 -> 0 bytes .../legacy/advanced/kv-cache-management.md | 75 - docs/source/legacy/advanced/kv-cache-reuse.md | 94 - docs/source/legacy/advanced/lora.md | 142 -- .../advanced/open-sourced-cutlass-kernels.md | 26 - .../legacy/advanced/speculative-decoding.md | 182 -- .../legacy/advanced/weight-streaming.md | 58 - docs/source/legacy/architecture/add-model.md | 95 - docs/source/legacy/architecture/checkpoint.md | 251 --- .../legacy/architecture/core-concepts.md | 389 ---- .../architecture/model-weights-loader.md | 256 --- docs/source/legacy/architecture/workflow.md | 241 -- .../dev-on-cloud/build-image-to-dockerhub.md | 56 - .../legacy/dev-on-cloud/dev-on-runpod.md | 47 - docs/source/legacy/key-features.md | 10 - .../legacy/performance/perf-analysis.md | 91 - .../legacy/performance/perf-benchmarking.md | 871 -------- .../benchmarking-default-performance.md | 243 -- .../deciding-model-sharding-strategy.md | 50 - .../fp8-quantization.md | 217 -- .../performance-tuning-guide/index.rst | 15 - .../performance-tuning-guide/introduction.md | 17 - ...uning-max-batch-size-and-max-num-tokens.md | 126 -- .../useful-build-time-flags.md | 171 -- .../useful-runtime-flags.md | 224 -- .../python-api/tensorrt_llm.functional.rst | 11 - .../legacy/python-api/tensorrt_llm.layers.rst | 69 - .../legacy/python-api/tensorrt_llm.models.rst | 11 - .../legacy/python-api/tensorrt_llm.plugin.rst | 10 - .../python-api/tensorrt_llm.quantization.rst | 10 - .../python-api/tensorrt_llm.runtime.rst | 11 - docs/source/legacy/reference/memory.md | 140 -- .../multimodal-feature-support-matrix.md | 13 - docs/source/legacy/reference/precision.md | 184 -- .../source/legacy/reference/support-matrix.md | 185 -- .../legacy/reference/troubleshooting.md | 328 --- .../source/legacy/tensorrt-backend-removal.md | 115 + docs/source/legacy/tensorrt_quickstart.md | 9 - docs/source/legacy/torch.md | 44 - docs/source/release-notes.md | 1 + docs/source/torch/arch_overview.md | 2 +- docs/source/torch/kv_cache_manager.md | 2 +- examples/draft_target_model/README.md | 364 --- examples/draft_target_model/requirements.txt | 6 - examples/eagle/README.md | 195 -- examples/eagle/convert_checkpoint.py | 493 ----- examples/eagle/requirements.txt | 6 - .../_tensorrt_engine/llm_eagle2_decoding.py | 55 - .../_tensorrt_engine/llm_eagle_decoding.py | 60 - .../llm_inference_customize.py | 55 - .../llm_inference_kv_events.py | 49 - .../llm_lookahead_decoding.py | 38 - .../_tensorrt_engine/llm_medusa_decoding.py | 93 - .../_tensorrt_engine/llm_quantization.py | 80 - .../_tensorrt_engine/quickstart_example.py | 39 - examples/lookahead/README.md | 108 - examples/lookahead/requirements.txt | 6 - examples/medusa/README.md | 232 -- examples/medusa/convert_checkpoint.py | 487 ---- examples/medusa/requirements.txt | 6 - examples/models/contrib/baichuan/README.md | 314 --- .../contrib/baichuan/convert_checkpoint.py | 263 --- .../models/contrib/baichuan/requirements.txt | 8 - examples/models/contrib/bloom/.gitignore | 2 - examples/models/contrib/bloom/README.md | 242 -- .../contrib/bloom/convert_checkpoint.py | 962 -------- .../models/contrib/bloom/requirements.txt | 6 - .../contrib/cogvlm/convert_checkpoint.py | 501 ----- examples/models/contrib/dbrx/README.md | 238 -- .../models/contrib/dbrx/convert_checkpoint.py | 654 ------ examples/models/contrib/dbrx/requirements.txt | 6 - examples/models/contrib/deepseek_v1/README.md | 103 - .../models/contrib/deepseek_v1/__init__.py | 14 - .../contrib/deepseek_v1/convert_checkpoint.py | 210 -- .../contrib/deepseek_v1/requirements.txt | 5 - examples/models/contrib/deepseek_v2/README.md | 267 --- .../contrib/deepseek_v2/convert_checkpoint.py | 231 -- .../contrib/deepseek_v2/requirements.txt | 3 - examples/models/contrib/dit/README.md | 133 -- .../models/contrib/dit/convert_checkpoint.py | 334 --- examples/models/contrib/dit/diffusion.py | 116 - .../models/contrib/dit/figs/sample.fp8.png | Bin 772223 -> 0 bytes examples/models/contrib/dit/figs/sample.png | Bin 821852 -> 0 bytes examples/models/contrib/dit/sample.py | 268 --- examples/models/contrib/dit/utils_modelopt.py | 186 -- .../models/contrib/dit/vae_decoder_trt.py | 139 -- examples/models/contrib/falcon/README.md | 364 --- .../contrib/falcon/convert_checkpoint.py | 202 -- .../models/contrib/falcon/requirements.txt | 8 - examples/models/contrib/gptj/.gitignore | 4 - examples/models/contrib/gptj/README.md | 269 --- .../models/contrib/gptj/convert_checkpoint.py | 166 -- examples/models/contrib/gptj/requirements.txt | 5 - examples/models/contrib/gptneox/.gitignore | 3 - examples/models/contrib/gptneox/README.md | 235 -- .../contrib/gptneox/convert_checkpoint.py | 732 ------ .../models/contrib/gptneox/gptq_convert.sh | 9 - .../models/contrib/gptneox/requirements.txt | 5 - examples/models/contrib/grok/README.md | 92 - .../models/contrib/grok/convert_checkpoint.py | 366 --- examples/models/contrib/grok/requirements.txt | 10 - examples/models/contrib/mmdit/README.md | 95 - .../contrib/mmdit/assets/sd3.5-mmdit.png | Bin 1569467 -> 0 bytes .../contrib/mmdit/convert_checkpoint.py | 123 -- .../models/contrib/mmdit/requirements.txt | 2 - examples/models/contrib/mmdit/sample.py | 266 --- examples/models/contrib/mpt/.gitignore | 3 - examples/models/contrib/mpt/README.md | 182 -- .../models/contrib/mpt/convert_checkpoint.py | 923 -------- examples/models/contrib/mpt/requirements.txt | 5 - examples/models/contrib/opt/README.md | 252 --- .../models/contrib/opt/convert_checkpoint.py | 361 --- examples/models/contrib/opt/requirements.txt | 5 - examples/models/contrib/stdit/README.md | 94 - examples/models/contrib/stdit/aspect.py | 526 ----- .../stdit/assets/a_beautiful_waterfall.mp4 | Bin 418644 -> 0 bytes .../contrib/stdit/convert_checkpoint.py | 252 --- .../models/contrib/stdit/pipeline_tllm.py | 607 ----- .../models/contrib/stdit/requirements.txt | 13 - examples/models/contrib/stdit/sample.py | 170 -- examples/models/contrib/stdit/scheduler.py | 101 - examples/models/contrib/stdit/text_encoder.py | 445 ---- examples/models/contrib/stdit/utils.py | 997 --------- examples/models/contrib/stdit/vae.py | 802 ------- .../models/contrib/stdit/video_transforms.py | 581 ----- examples/models/core/bert/.gitignore | 2 - examples/models/core/bert/README.md | 117 - examples/models/core/bert/__init__.py | 0 .../models/core/bert/convert_checkpoint.py | 192 -- examples/models/core/bert/run.py | 338 --- examples/models/core/bert/utils.py | 217 -- examples/models/core/commandr/README.md | 213 -- .../core/commandr/convert_checkpoint.py | 187 -- .../models/core/commandr/requirements.txt | 5 - examples/models/core/enc_dec/README.md | 444 ---- examples/models/core/enc_dec/__init__.py | 0 .../models/core/enc_dec/convert_checkpoint.py | 1958 ----------------- examples/models/core/enc_dec/helper.py | 121 - examples/models/core/enc_dec/run.py | 512 ----- examples/models/core/gemma/README.md | 835 +------ .../models/core/gemma/convert_checkpoint.py | 290 --- examples/models/core/gemma/requirements.txt | 14 - examples/models/core/glm-4-9b/README.md | 292 --- .../core/glm-4-9b/convert_checkpoint.py | 271 --- .../models/core/glm-4-9b/requirements.txt | 8 - .../core/glm-4-9b/tokenization_chatglm.py | 343 --- examples/models/core/gpt/.gitignore | 5 - examples/models/core/gpt/README.md | 786 ------- .../models/core/gpt/convert_checkpoint.py | 343 --- .../models/core/gpt/merge_ptuning_tables.py | 44 - examples/models/core/gpt/nemo_lora_convert.py | 206 -- .../models/core/gpt/nemo_prompt_convert.py | 145 -- examples/models/core/gpt/requirements.txt | 6 - examples/models/core/gpt/run_hf.py | 162 -- examples/models/core/internlm2/.gitignore | 2 - examples/models/core/internlm2/README.md | 207 -- .../core/internlm2/convert_checkpoint.py | 549 ----- .../models/core/internlm2/requirements.txt | 1 - examples/models/core/llama/.gitignore | 5 - examples/models/core/llama/README.md | 1592 -------------- .../models/core/llama/convert_checkpoint.py | 593 ----- examples/models/core/llama/requirements.txt | 7 - examples/models/core/llama/summarize_long.py | 409 ---- examples/models/core/mamba/README.md | 232 -- .../models/core/mamba/convert_checkpoint.py | 227 -- examples/models/core/mamba/requirements.txt | 7 - examples/models/core/mllama/README.md | 3 - .../models/core/mllama/convert_checkpoint.py | 490 ----- examples/models/core/mllama/requirements.txt | 2 - examples/models/core/nemotron_nas/README.md | 143 +- .../core/nemotron_nas/calibration_utils.py | 39 - .../core/nemotron_nas/convert_checkpoint.py | 161 -- examples/models/core/phi/README.md | 195 -- .../models/core/phi/convert_checkpoint.py | 191 -- examples/models/core/phi/requirements.txt | 7 - examples/models/core/qwen/README.md | 607 +---- .../models/core/qwen/convert_checkpoint.py | 347 --- examples/models/core/qwen/requirements.txt | 17 - .../models/core/recurrentgemma/.gitignore | 10 - examples/models/core/recurrentgemma/README.md | 337 --- .../core/recurrentgemma/convert_checkpoint.py | 529 ----- .../core/recurrentgemma/requirements.txt | 12 - examples/models/core/vit/README.md | 91 - .../models/core/vit/convert_checkpoint.py | 149 -- examples/models/core/whisper/README.md | 205 -- .../models/core/whisper/convert_checkpoint.py | 452 ---- .../convert_from_distil_whisper.py | 136 -- examples/models/core/whisper/requirements.txt | 11 - examples/models/core/whisper/run.py | 631 ------ examples/models/core/whisper/tokenizer.py | 182 -- examples/models/core/whisper/whisper_utils.py | 365 --- examples/redrafter/README.md | 129 -- examples/redrafter/convert_checkpoint.py | 459 ---- examples/redrafter/requirements.txt | 6 - 206 files changed, 156 insertions(+), 40632 deletions(-) delete mode 100644 docs/source/commands/trtllm-build.rst rename docs/source/{legacy/advanced => features}/images/8x_l20_L40S_node_architecture.png (100%) rename docs/source/{legacy/advanced/lowprecision-pcie-allreduce.md => features/lowprecision-allreduce.md} (69%) delete mode 100644 docs/source/legacy/advanced/disaggregated-service.md delete mode 100644 docs/source/legacy/advanced/executor.md delete mode 100644 docs/source/legacy/advanced/expert-parallelism.md delete mode 100644 docs/source/legacy/advanced/gpt-attention.md delete mode 100644 docs/source/legacy/advanced/gpt-runtime.md delete mode 100644 docs/source/legacy/advanced/graph-rewriting.md delete mode 100644 docs/source/legacy/advanced/images/disaggregated-service_usage.png delete mode 100644 docs/source/legacy/advanced/kv-cache-management.md delete mode 100644 docs/source/legacy/advanced/kv-cache-reuse.md delete mode 100644 docs/source/legacy/advanced/lora.md delete mode 100644 docs/source/legacy/advanced/open-sourced-cutlass-kernels.md delete mode 100644 docs/source/legacy/advanced/speculative-decoding.md delete mode 100644 docs/source/legacy/advanced/weight-streaming.md delete mode 100644 docs/source/legacy/architecture/add-model.md delete mode 100644 docs/source/legacy/architecture/checkpoint.md delete mode 100644 docs/source/legacy/architecture/core-concepts.md delete mode 100644 docs/source/legacy/architecture/model-weights-loader.md delete mode 100644 docs/source/legacy/architecture/workflow.md delete mode 100644 docs/source/legacy/dev-on-cloud/build-image-to-dockerhub.md delete mode 100644 docs/source/legacy/dev-on-cloud/dev-on-runpod.md delete mode 100644 docs/source/legacy/key-features.md delete mode 100644 docs/source/legacy/performance/perf-analysis.md delete mode 100644 docs/source/legacy/performance/perf-benchmarking.md delete mode 100644 docs/source/legacy/performance/performance-tuning-guide/benchmarking-default-performance.md delete mode 100644 docs/source/legacy/performance/performance-tuning-guide/deciding-model-sharding-strategy.md delete mode 100644 docs/source/legacy/performance/performance-tuning-guide/fp8-quantization.md delete mode 100644 docs/source/legacy/performance/performance-tuning-guide/index.rst delete mode 100644 docs/source/legacy/performance/performance-tuning-guide/introduction.md delete mode 100644 docs/source/legacy/performance/performance-tuning-guide/tuning-max-batch-size-and-max-num-tokens.md delete mode 100644 docs/source/legacy/performance/performance-tuning-guide/useful-build-time-flags.md delete mode 100644 docs/source/legacy/performance/performance-tuning-guide/useful-runtime-flags.md delete mode 100644 docs/source/legacy/python-api/tensorrt_llm.functional.rst delete mode 100644 docs/source/legacy/python-api/tensorrt_llm.layers.rst delete mode 100644 docs/source/legacy/python-api/tensorrt_llm.models.rst delete mode 100644 docs/source/legacy/python-api/tensorrt_llm.plugin.rst delete mode 100644 docs/source/legacy/python-api/tensorrt_llm.quantization.rst delete mode 100644 docs/source/legacy/python-api/tensorrt_llm.runtime.rst delete mode 100644 docs/source/legacy/reference/memory.md delete mode 100644 docs/source/legacy/reference/multimodal-feature-support-matrix.md delete mode 100644 docs/source/legacy/reference/precision.md delete mode 100644 docs/source/legacy/reference/support-matrix.md delete mode 100644 docs/source/legacy/reference/troubleshooting.md create mode 100644 docs/source/legacy/tensorrt-backend-removal.md delete mode 100644 docs/source/legacy/tensorrt_quickstart.md delete mode 100644 docs/source/legacy/torch.md delete mode 100644 examples/draft_target_model/README.md delete mode 100644 examples/draft_target_model/requirements.txt delete mode 100644 examples/eagle/README.md delete mode 100644 examples/eagle/convert_checkpoint.py delete mode 100644 examples/eagle/requirements.txt delete mode 100755 examples/llm-api/_tensorrt_engine/llm_eagle2_decoding.py delete mode 100644 examples/llm-api/_tensorrt_engine/llm_eagle_decoding.py delete mode 100644 examples/llm-api/_tensorrt_engine/llm_inference_customize.py delete mode 100644 examples/llm-api/_tensorrt_engine/llm_inference_kv_events.py delete mode 100644 examples/llm-api/_tensorrt_engine/llm_lookahead_decoding.py delete mode 100644 examples/llm-api/_tensorrt_engine/llm_medusa_decoding.py delete mode 100644 examples/llm-api/_tensorrt_engine/llm_quantization.py delete mode 100644 examples/llm-api/_tensorrt_engine/quickstart_example.py delete mode 100644 examples/lookahead/README.md delete mode 100644 examples/lookahead/requirements.txt delete mode 100644 examples/medusa/README.md delete mode 100644 examples/medusa/convert_checkpoint.py delete mode 100644 examples/medusa/requirements.txt delete mode 100644 examples/models/contrib/baichuan/README.md delete mode 100644 examples/models/contrib/baichuan/convert_checkpoint.py delete mode 100644 examples/models/contrib/baichuan/requirements.txt delete mode 100644 examples/models/contrib/bloom/.gitignore delete mode 100644 examples/models/contrib/bloom/README.md delete mode 100644 examples/models/contrib/bloom/convert_checkpoint.py delete mode 100644 examples/models/contrib/bloom/requirements.txt delete mode 100644 examples/models/contrib/cogvlm/convert_checkpoint.py delete mode 100644 examples/models/contrib/dbrx/README.md delete mode 100644 examples/models/contrib/dbrx/convert_checkpoint.py delete mode 100644 examples/models/contrib/dbrx/requirements.txt delete mode 100755 examples/models/contrib/deepseek_v1/README.md delete mode 100644 examples/models/contrib/deepseek_v1/__init__.py delete mode 100644 examples/models/contrib/deepseek_v1/convert_checkpoint.py delete mode 100644 examples/models/contrib/deepseek_v1/requirements.txt delete mode 100644 examples/models/contrib/deepseek_v2/README.md delete mode 100755 examples/models/contrib/deepseek_v2/convert_checkpoint.py delete mode 100644 examples/models/contrib/deepseek_v2/requirements.txt delete mode 100644 examples/models/contrib/dit/README.md delete mode 100644 examples/models/contrib/dit/convert_checkpoint.py delete mode 100644 examples/models/contrib/dit/diffusion.py delete mode 100644 examples/models/contrib/dit/figs/sample.fp8.png delete mode 100644 examples/models/contrib/dit/figs/sample.png delete mode 100644 examples/models/contrib/dit/sample.py delete mode 100644 examples/models/contrib/dit/utils_modelopt.py delete mode 100644 examples/models/contrib/dit/vae_decoder_trt.py delete mode 100644 examples/models/contrib/falcon/README.md delete mode 100644 examples/models/contrib/falcon/convert_checkpoint.py delete mode 100644 examples/models/contrib/falcon/requirements.txt delete mode 100644 examples/models/contrib/gptj/.gitignore delete mode 100644 examples/models/contrib/gptj/README.md delete mode 100644 examples/models/contrib/gptj/convert_checkpoint.py delete mode 100644 examples/models/contrib/gptj/requirements.txt delete mode 100644 examples/models/contrib/gptneox/.gitignore delete mode 100644 examples/models/contrib/gptneox/README.md delete mode 100644 examples/models/contrib/gptneox/convert_checkpoint.py delete mode 100644 examples/models/contrib/gptneox/gptq_convert.sh delete mode 100644 examples/models/contrib/gptneox/requirements.txt delete mode 100644 examples/models/contrib/grok/README.md delete mode 100644 examples/models/contrib/grok/convert_checkpoint.py delete mode 100644 examples/models/contrib/grok/requirements.txt delete mode 100644 examples/models/contrib/mmdit/README.md delete mode 100644 examples/models/contrib/mmdit/assets/sd3.5-mmdit.png delete mode 100644 examples/models/contrib/mmdit/convert_checkpoint.py delete mode 100644 examples/models/contrib/mmdit/requirements.txt delete mode 100644 examples/models/contrib/mmdit/sample.py delete mode 100644 examples/models/contrib/mpt/.gitignore delete mode 100644 examples/models/contrib/mpt/README.md delete mode 100644 examples/models/contrib/mpt/convert_checkpoint.py delete mode 100644 examples/models/contrib/mpt/requirements.txt delete mode 100644 examples/models/contrib/opt/README.md delete mode 100644 examples/models/contrib/opt/convert_checkpoint.py delete mode 100644 examples/models/contrib/opt/requirements.txt delete mode 100644 examples/models/contrib/stdit/README.md delete mode 100644 examples/models/contrib/stdit/aspect.py delete mode 100644 examples/models/contrib/stdit/assets/a_beautiful_waterfall.mp4 delete mode 100644 examples/models/contrib/stdit/convert_checkpoint.py delete mode 100644 examples/models/contrib/stdit/pipeline_tllm.py delete mode 100644 examples/models/contrib/stdit/requirements.txt delete mode 100644 examples/models/contrib/stdit/sample.py delete mode 100644 examples/models/contrib/stdit/scheduler.py delete mode 100644 examples/models/contrib/stdit/text_encoder.py delete mode 100644 examples/models/contrib/stdit/utils.py delete mode 100644 examples/models/contrib/stdit/vae.py delete mode 100644 examples/models/contrib/stdit/video_transforms.py delete mode 100644 examples/models/core/bert/.gitignore delete mode 100644 examples/models/core/bert/README.md delete mode 100644 examples/models/core/bert/__init__.py delete mode 100644 examples/models/core/bert/convert_checkpoint.py delete mode 100644 examples/models/core/bert/run.py delete mode 100644 examples/models/core/bert/utils.py delete mode 100644 examples/models/core/commandr/README.md delete mode 100644 examples/models/core/commandr/convert_checkpoint.py delete mode 100644 examples/models/core/commandr/requirements.txt delete mode 100644 examples/models/core/enc_dec/README.md delete mode 100644 examples/models/core/enc_dec/__init__.py delete mode 100755 examples/models/core/enc_dec/convert_checkpoint.py delete mode 100755 examples/models/core/enc_dec/helper.py delete mode 100644 examples/models/core/enc_dec/run.py delete mode 100644 examples/models/core/gemma/convert_checkpoint.py delete mode 100644 examples/models/core/gemma/requirements.txt delete mode 100644 examples/models/core/glm-4-9b/README.md delete mode 100644 examples/models/core/glm-4-9b/convert_checkpoint.py delete mode 100644 examples/models/core/glm-4-9b/requirements.txt delete mode 100644 examples/models/core/glm-4-9b/tokenization_chatglm.py delete mode 100644 examples/models/core/gpt/.gitignore delete mode 100644 examples/models/core/gpt/README.md delete mode 100644 examples/models/core/gpt/convert_checkpoint.py delete mode 100644 examples/models/core/gpt/merge_ptuning_tables.py delete mode 100644 examples/models/core/gpt/nemo_lora_convert.py delete mode 100755 examples/models/core/gpt/nemo_prompt_convert.py delete mode 100644 examples/models/core/gpt/requirements.txt delete mode 100644 examples/models/core/gpt/run_hf.py delete mode 100644 examples/models/core/internlm2/.gitignore delete mode 100644 examples/models/core/internlm2/README.md delete mode 100644 examples/models/core/internlm2/convert_checkpoint.py delete mode 100644 examples/models/core/internlm2/requirements.txt delete mode 100644 examples/models/core/llama/.gitignore delete mode 100644 examples/models/core/llama/README.md delete mode 100644 examples/models/core/llama/convert_checkpoint.py delete mode 100644 examples/models/core/llama/requirements.txt delete mode 100644 examples/models/core/llama/summarize_long.py delete mode 100644 examples/models/core/mamba/README.md delete mode 100644 examples/models/core/mamba/convert_checkpoint.py delete mode 100644 examples/models/core/mamba/requirements.txt delete mode 100644 examples/models/core/mllama/README.md delete mode 100644 examples/models/core/mllama/convert_checkpoint.py delete mode 100644 examples/models/core/mllama/requirements.txt delete mode 100644 examples/models/core/nemotron_nas/calibration_utils.py delete mode 100644 examples/models/core/nemotron_nas/convert_checkpoint.py delete mode 100644 examples/models/core/phi/README.md delete mode 100644 examples/models/core/phi/convert_checkpoint.py delete mode 100644 examples/models/core/phi/requirements.txt delete mode 100644 examples/models/core/qwen/convert_checkpoint.py delete mode 100644 examples/models/core/qwen/requirements.txt delete mode 100644 examples/models/core/recurrentgemma/.gitignore delete mode 100644 examples/models/core/recurrentgemma/README.md delete mode 100644 examples/models/core/recurrentgemma/convert_checkpoint.py delete mode 100644 examples/models/core/recurrentgemma/requirements.txt delete mode 100644 examples/models/core/vit/README.md delete mode 100644 examples/models/core/vit/convert_checkpoint.py delete mode 100755 examples/models/core/whisper/README.md delete mode 100644 examples/models/core/whisper/convert_checkpoint.py delete mode 100644 examples/models/core/whisper/distil_whisper/convert_from_distil_whisper.py delete mode 100644 examples/models/core/whisper/requirements.txt delete mode 100755 examples/models/core/whisper/run.py delete mode 100644 examples/models/core/whisper/tokenizer.py delete mode 100644 examples/models/core/whisper/whisper_utils.py delete mode 100644 examples/redrafter/README.md delete mode 100644 examples/redrafter/convert_checkpoint.py delete mode 100644 examples/redrafter/requirements.txt diff --git a/docs/source/blogs/Best_perf_practice_on_DeepSeek-R1_in_TensorRT-LLM.md b/docs/source/blogs/Best_perf_practice_on_DeepSeek-R1_in_TensorRT-LLM.md index 22d4688c503d..18c5ce06c0f2 100644 --- a/docs/source/blogs/Best_perf_practice_on_DeepSeek-R1_in_TensorRT-LLM.md +++ b/docs/source/blogs/Best_perf_practice_on_DeepSeek-R1_in_TensorRT-LLM.md @@ -414,13 +414,13 @@ Note that, `max_batch_size` and `max_num_tokens` can easily affect the performan Generally, you should make sure that `max_batch_size` is not too low to bottleneck the throughput, and `max_num_tokens` needs to be large enough so that it covers the max input sequence length of the samples in dataset, as mentioned in below section "WIP: Chunked context support on DeepSeek models". -For more details on `max_batch_size` and `max_num_tokens`, refer to [Tuning Max Batch Size and Max Num Tokens](../legacy/performance/performance-tuning-guide/tuning-max-batch-size-and-max-num-tokens.md). +For more details on `max_batch_size` and `max_num_tokens`, refer to the performance tuning guidance in the developer documentation. ### MLA chunked context MLA currently supports the chunked context feature on both Hopper and Blackwell GPUs. You can use `--enable_chunked_context` to enable it. This feature is primarily designed to reduce TPOT (Time Per Output Token). The default chunk size is set to `max_num_tokens`. If you want to achieve a lower TPOT, you can appropriately reduce the chunk size. However, please note that this will also decrease overall throughput. Therefore, a trade-off needs to be considered. -For more details on `max_num_tokens`, refer to [Tuning Max Batch Size and Max Num Tokens](../legacy/performance/performance-tuning-guide/tuning-max-batch-size-and-max-num-tokens.md). +For more details on `max_num_tokens`, refer to the performance tuning guidance in the developer documentation. ### Out of memory issues diff --git a/docs/source/blogs/XQA-kernel.md b/docs/source/blogs/XQA-kernel.md index d4018966cd2d..456024a10ed5 100644 --- a/docs/source/blogs/XQA-kernel.md +++ b/docs/source/blogs/XQA-kernel.md @@ -2,7 +2,7 @@ XQA kernel provides optimization for [MQA](https://arxiv.org/abs/1911.02150) and [GQA](https://arxiv.org/abs/2305.13245v3) during generation phase. It also provides optimization for beam search. Using tensor cores for acceleration, reducing data loading and conversion, it delivers increased throughput within the same latency budget. Increased throughput allows serving greater number of user requests while providing the same experience. -Support matrix and usage flags are described in [docs/source/legacy/advanced/gpt-attention](../legacy/advanced/gpt-attention.md#xqa-optimization). +Support matrix and usage flags are described in the [Attention](../features/attention.md) documentation. **Increased Throughput:** Looking at the Throughput-Latency curves below, we see that the enabling of XQA optimization increases throughput. Higher throughput equates to serving more users, and we can see that TPOT on the Y-axis flattens out when XQA gets enabled. diff --git a/docs/source/commands/trtllm-build.rst b/docs/source/commands/trtllm-build.rst deleted file mode 100644 index d209d3eac9ca..000000000000 --- a/docs/source/commands/trtllm-build.rst +++ /dev/null @@ -1,7 +0,0 @@ -trtllm-build -=========================== - -.. argparse:: - :module: tensorrt_llm.commands.build - :func: parse_arguments - :prog: trtllm-build diff --git a/docs/source/legacy/advanced/images/8x_l20_L40S_node_architecture.png b/docs/source/features/images/8x_l20_L40S_node_architecture.png similarity index 100% rename from docs/source/legacy/advanced/images/8x_l20_L40S_node_architecture.png rename to docs/source/features/images/8x_l20_L40S_node_architecture.png diff --git a/docs/source/legacy/advanced/lowprecision-pcie-allreduce.md b/docs/source/features/lowprecision-allreduce.md similarity index 69% rename from docs/source/legacy/advanced/lowprecision-pcie-allreduce.md rename to docs/source/features/lowprecision-allreduce.md index b7ab50703701..54eddafffb7a 100644 --- a/docs/source/legacy/advanced/lowprecision-pcie-allreduce.md +++ b/docs/source/features/lowprecision-allreduce.md @@ -5,8 +5,7 @@ Note: This feature is optimized for PCIe-based GPU topologies and may affect model accuracy. Please evaluate precision impact for your specific workload. ``` - -TRT-LLM supports `low-precision-allreduce`, a communication optimization that accelerates AllReduce operations in PCIe-based GPU environments. This feature quantizes FP16/BF16 data to FP8 during network transmission, reducing communication volume and improving performance. +TensorRT LLM supports `low-precision-allreduce`, a communication optimization that accelerates AllReduce operations in PCIe-based GPU environments. This feature quantizes FP16/BF16 data to FP8 during network transmission, reducing communication volume and improving performance. It is available on the PyTorch backend through the `allreduce_strategy` field of `LlmArgs`. ## Algorithm @@ -35,20 +34,16 @@ Low-Precision-AllReduce is specifically designed for the topology shown above, w ## Usage -The Low-Precision-AllReduce algorithm can be enabled in two ways: +Enable the Low-Precision-AllReduce algorithm by setting the `allreduce_strategy` field in `LlmArgs`: -1. **Direct specification** in your code: -``` -AllReduce allreduce(mapping=mapping, strategy=AllReduceStrategy.LOWPRECISION); -``` +```python +from tensorrt_llm import LLM -2. Enable by LlmArgs -``` -Set allreduce_strategy field in LlmArgs. -Candidates of strategies are "AUTO", "NCCL", "UB", "MINLATENCY", "ONESHOT", "TWOSHOT", "LOWPRECISION" and "MNNVL". -If no strategy is set, AUTO will be set. +llm = LLM(model="", allreduce_strategy="LOWPRECISION") ``` +The candidate strategies are `"AUTO"`, `"NCCL"`, `"UB"`, `"MINLATENCY"`, `"ONESHOT"`, `"TWOSHOT"`, `"LOWPRECISION"` and `"MNNVL"`. If no strategy is set, `AUTO` is used. + ## Performance and Accuracy Considerations Low-Precision-AllReduce reduces communication volume by using FP8 data format for transmission. This optimization: @@ -58,4 +53,4 @@ Low-Precision-AllReduce reduces communication volume by using FP8 data format fo Users should evaluate the precision impact on their specific models and workloads. -**Note**: When compiling TensorRT-LLM without enabling the `ENABLE_FP8` option, setting Low Precision allreduce will not take effect. +**Note**: When TensorRT LLM is built without the `ENABLE_FP8` option, setting the low-precision AllReduce strategy will not take effect. diff --git a/docs/source/index.rst b/docs/source/index.rst index 9d92a8770148..b65f8a62b3d8 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -72,6 +72,7 @@ Welcome to TensorRT LLM's Documentation! features/overlap-scheduler.md features/paged-attention-ifb-scheduler.md features/parallel-strategy.md + features/lowprecision-allreduce.md features/quantization.md features/sampling.md features/additional-outputs.md @@ -127,10 +128,9 @@ Welcome to TensorRT LLM's Documentation! .. toctree:: :maxdepth: 2 - :caption: Use TensorRT Engine - :hidden: + :caption: Migration - legacy/tensorrt_quickstart.md + legacy/tensorrt-backend-removal.md Indices and tables ================== diff --git a/docs/source/legacy/advanced/disaggregated-service.md b/docs/source/legacy/advanced/disaggregated-service.md deleted file mode 100644 index ac6cbad472e9..000000000000 --- a/docs/source/legacy/advanced/disaggregated-service.md +++ /dev/null @@ -1,86 +0,0 @@ -(disaggregated-service)= - -# Disaggregated-Service (Prototype) - -```{note} -Note: -This feature is currently in prototype, and the related API is subject to change in future versions. -``` -Currently TRT-LLM supports `disaggregated-service`, where the context and generation phases of a request can run on different executors. TRT-LLM's disaggregated service relies on the executor API, please make sure to read the [executor page](executor.md) before reading the document. - -For more information on disaggregated service in LLM inference, one can refer to papers such as [DistServe](https://arxiv.org/abs/2401.09670), [SplitWise](https://arxiv.org/abs/2311.18677). - -An [architectural and performance overview](../../../docs/source/blogs/tech_blog/blog05_Disaggregated_Serving_in_TensorRT-LLM.md), as well as [usage examples](../../../examples/disaggregated/README.md), are provided. - -## Environment Variables - -TRT-LLM uses some environment variables to control the behavior of disaggregated service. - - -* `TRTLLM_DISABLE_KV_CACHE_TRANSFER_OVERLAP`: If set to `1`, generationExecutor will not overlap KV cache transfer with model inference. The default value is `0`. - -* `TRTLLM_ENABLE_KVCACHE_RECEIVE_PARALLEL`: When the generation rank receives KV cache from multiple context ranks within a single context instance, it will receive KV cache from each rank sequentially. If set to `1`, the generation rank will receive KV cache from each rank within one context instance in parallel. The default value is `0`. - -* `TRTLLM_REQUEST_KV_CACHE_CONCURRENT`: If set to `1`, generationExecutor prepares independent resources for each context executor to receive KV cache, requests whose KV cache are received from different context executors will be processed concurrently. If set to `0`, the generation executor will reuse the same resource to process KV cache transfer for each request sequentially, reducing the resources used by KV cache transmission and thereby lowering the risk of running out of memory. The default value is `0`. - -* `TRTLLM_TRY_ZCOPY_FOR_KVCACHE_TRANSFER`: TRT-LLM typically copies non-contiguous data into a temporary buffer before sending KV cache. If set to `1`, TRT-LLM will attempt to directly transmit each KV cache block, eliminating extra copies. The default value is `0`. - -* `TRTLLM_KVCACHE_TRANSFER_BUFFER_SIZE`: By default, TRT-LLM uses a `stream-ordered memory allocator` to allocate temporary buffers. If this environment variable is set to #Size, TRT-LLM will use `cudaMalloc` to allocate buffer of size #Size for KV cache transmission. The default value is `512MB`. Users can set `TRTLLM_KVCACHE_TRANSFER_BUFFER_SIZE=1GB` to allocate a 1 GB buffer with `cudaMalloc` for KV cache transmission. - -* `TRTLLM_KVCACHE_TRANSFER_USE_ASYNC_BUFFER`: If set to `1`, TRT-LLM will use `cudaMallocAsync` to allocate buffers for KV cache transmission. The default value is `0`. This environment variable only takes effect when `TRTLLM_KVCACHE_TRANSFER_BUFFER_SIZE` is greater than 0. - -* `TRTLLM_KVCACHE_SEND_MAX_CONCURRENCY_NUM`: The maximum number of concurrent KV cache sends. The default value is `1`. This environment variable only takes effect when `TRTLLM_KVCACHE_TRANSFER_BUFFER_SIZE` is greater than 0. - -There are some other useful environment variables that may help when encountering failures or performance issues. - -* `NCCL_GRAPH_MIXING_SUPPORT`: With the default value `1`, the CUDA driver may create too many CUDA streams while working with one CUDA graph, leading to performance drop. Setting it to `0` will reduce the number of CUDA streams, but please make sure there are no other NCCL ops outside the one CUDA graph, otherwise it's unsafe. - -* `UCX_MAX_RNDV_RAILS`: With the default value `2`, UCX attempts to use two InfiniBand (IB) NIC devices per GPU for Rendezvous (RNDV) transfers. When both the context and generation instances enable tensor- and expert-parallel (TEP), multiple TP ranks may transfer KV cache concurrently. Because each TP rank can use up to two NIC devices, some NIC devices can be shared across GPUs, causing contention and reduced throughput. Setting `UCX_MAX_RNDV_RAILS=1` can reduce contention in this case. - -## Troubleshooting and FAQ - -### General FAQs - -*Q. What are the limitations of disaggregated-service in TRT-LLM?* - -A. Currently, only `decoder-only engine` and `beamWidth=1` are supported, and the KV cache at each layer of the model is required to be homogeneous, with the same data type and the same number of attention heads. - -*Q. Is the engine used by disaggregated-service different from other engines?* - -A. No. There are no special requirements for the arguments to build engine. - -*Q. Do the engines used by the context executor and generation executor need to be the same?* - -A. No. The engines used by context executor and generation executor can be different, and their parallelism can be heterogeneous, i.e., TP,PP can be different, and TRT-LLM will handle the heterogeneity of KV cache. - -*Q. Does TRT-LLM support running multiple context executor instances and generation executor instances?* - -A. Yes. TRT-LLM supports running multiple context executors and generation executors at the same time, and each executor can use different engine, but it is the user's responsibility to route requests to different executors and manage `requestId`. - -*Q. Can an executor handle both context-only requests and generation-only requests?* - -A. Yes, but it's not recommended, TRT-LLM does not implement proper scheduling for the case where the executor handles mixed context-only requests and generation-only requests, it's better to run context-only requests and generation-only requests on different executors. - -*Q. Does disaggregated-service in TRT-LLM support multi-gpu and multi-node?* - -A. Yes, it's recommended that different executor use different GPUs. We support context-only executor and generation-only executor run on same node or different nodes. The `participantIds` and `deviceIds` used by each executor need to be explicitly set by the user, and the `participantIds` of each executor must not be intersecting. - -### Debugging FAQs - -*Q. Does TRT-LLM support using GPU direct RDMA for inter-node KV Cache transfer?* - -A. Yes, TRT-LLM supports using GPU direct RDMA for inter-node KV cache transfer. - -*Q. What causes the substantial bandwidth fluctuations in kvCache transfers, especially during the first few requests following service initialization?* - -A. The communication for kvCache transfer between executors are established dynamically. The connection establishment process incurs significant overhead, which explains the apparently lower kvCache transfer bandwidth observed during the initial requests after service startup. This lower bandwidth reflects the inclusion of connection establishment overhead. When conducting benchmarks, it is recommended to perform a warm-up phase to ensure accurate performance measurements. - -*Q. When my servers are running on different NVLink domains, some servers hang or have a lower performance. How to fix that?* - -A. NVLink domain can be found with `nvidia-smi -q` in the `Fabric.ClusterUUID` field. A few UCX environment variables can be adjusted when your servers have different NVLink domains: - -* `UCX_CUDA_IPC_ENABLE_MNNVL`: Set to `n`. This also can reduce UCX timeout error messages like `UCX ERROR cuMemImportFromShareableHandle failed: invalid resource handle`, although these errors don't necessarily cause your trtllm-serve to fail. - -* `UCX_NET_DEVICES`: Check if this is set correctly, or unset this variable to allow UCX to use all possible devices. - -* `UCX_RNDV_SCHEME`: Set to `get_zcopy` or `put_zcopy` on GB200 for better performance. The default value is `auto`. diff --git a/docs/source/legacy/advanced/executor.md b/docs/source/legacy/advanced/executor.md deleted file mode 100644 index 1f59d0802a16..000000000000 --- a/docs/source/legacy/advanced/executor.md +++ /dev/null @@ -1,153 +0,0 @@ -(executor)= - -# Executor API - -TensorRT-LLM includes a high-level C++ API called the Executor API which allows you to execute requests -asynchronously, with in-flight batching, and without the need to define callbacks. - -A software component (referred to as "the client" in the text that follows) can interact -with the executor using the API defined in the [`executor.h`](source:cpp/include/tensorrt_llm/executor/executor.h) file. -For details about the API, refer to the {ref}`_cpp_gen/executor.rst`. - -The following sections provide an overview of the main classes defined in the Executor API. - -## API - -### The Executor Class - -The `Executor` class is responsible for receiving requests from the client, and providing responses for those requests. The executor is constructed by providing a path to a directory containing the TensorRT-LLM engine or buffers containing the engine and the model JSON configuration. The client can create requests and enqueue those requests for execution using the `enqueueRequest` or `enqueueRequests` methods of the `Executor` class. Enqueued requests will be scheduled for execution by the executor, and multiple independent requests can be batched together at every iteration of the main execution loop (a process often referred to as continuous batching or iteration-level batching). Responses for a particular request can be awaited for by calling the `awaitResponses` method, and by providing the request id. Alternatively, responses for any requests can be awaited for by omitting to provide the request id when calling `awaitResponses`. The `Executor` class also allows to cancel requests using the `cancelRequest` method and to obtain per-iteration and per-request statistics using the `getLatestIterationStats`. - -### The Request Class - -The `Request` class is used to define properties of the request, such as the input token ids and the maximum number of tokens to generate. The `streaming` parameter can be used to indicate if the request should generate a response for each new generated tokens (`streaming = true`) or only after all tokens have been generated (`streaming = false`). Other mandatory parameters of the request include the sampling configuration (defined by the `SamplingConfig` class) which contains parameters controlling the decoding process and the output configuration (defined by the `OutputConfig` class) which controls what information should be included in the `Result` for a particular response. - -Optional parameters can also be provided when constructing a request such as a list of bad words, a list of stop words, a client id, or configurations objects for prompt tuning, LoRA, or speculative decoding, or a number of sequences to generate for example. - -### The Response Class - -The `awaitResponses` method of the `Executor` class returns a vector of responses. Each response contains the request id associated with this response, and also contains either an error or a `Result`. Check if the response has an error by using the `hasError` method before trying to obtain the `Result` associated with this response using the `getResult` method. - -### The Result Class - -The `Result` class holds the result for a given request. It contains a Boolean parameter called `isFinal` that indicates if this is the last `Result` that will be returned for the given request id. It also contains the generated tokens. If the request is configured with `streaming = false` and `numReturnSequences = 1`, a single response will be returned, the `isFinal` Boolean will be set to `true` and all generated tokens will be included in the `outputTokenIds`. If `streaming = true` and `numReturnSequences = 1` is used, a `Result` will include one or more tokens (depending on the request `returnAllGeneratedTokens` parameter) except the last result and the `isFinal` flag will be set to `true` for the last result associated with this request. - -The request `numReturnSequences` parameter controls the number of output sequences to generate for each prompt. When this option is used, the Executor will return at least `numReturnSequences` responses for each request, each containing one Result. In beam search (`beamWidth > 1`), the number of beams to be returned will be limited by `numReturnSequences` and the `sequenceIndex` attribute of the `Result` class will always be zero. Otherwise, in sampling (`beamWidth = 1`), the `sequenceIndex` attribute indicates the index of the generated sequence in the result (`0 <= sequenceIndex < numReturnSequences`). It contains a Boolean parameter called `isSequenceFinal` that indicates if this is the last result for the sequence and also contains a Boolean parameter `isFinal` that indicates when all sequences for the request have been generated. When `numReturnSequences = 1`, `isFinal` is identical to `isSequenceFinal`. - -Here is an example that shows how a subset of 3 responses might look like for `numReturnSequences = 3`: - -``` -Response 1: requestId = 1, Result with sequenceIndex = 0, isSequenceFinal = false, isFinal = false -Response 2: requestId = 1, Result with sequenceIndex = 1, isSequenceFinal = true, isFinal = false -Response 3: requestId = 1, Result with sequenceIndex = 2, isSequenceFinal = false, isFinal = false -``` - -In this example, each response contains one result for different sequences. The `isSequenceFinal` flag of the second Result is set to true, indicating that it is the last result for `sequenceIndex = 1`, however, the isFinal flag of each Response is set to false because sequences 0 and 2 are not completed. - -### Sending Requests with Different Beam Widths - -The executor can process requests with different beam widths if the following conditions are met: - -- The model was built with a `max_beam_width > 1`. -- The executor is configured with a `maxBeamWidth > 1` (the configured `maxBeamWidth` must be less than or equal to the model's `max_beam_width`). -- The requested beam widths are less than or equal to the configured `maxBeamWidth`. - -The executor may schedule successive requests with the same beam width at the same time. For successive requests with two different beam widths, `x` and `y`, requests with beam width `y` are not scheduled until all requests with beam width `x` have been processed. -This allows the runtime to reconfigure itself for a new beam width when no requests are in flight. The reconfiguration happens automatically each time requests with a different beam width than currently configured are detected. Waiting for previous requests to finish and reconfiguring the runtime may cause significant overhead and reduce overall throughput. - -### Controlling output with Logits Post-Processor - -Optionally, you can alter the logits produced by the network by providing an instance of `Executor::LogitsPostProcessorConfig`. For instance, this feature can be used to generate JSON formatted output. {cpp:class}`Executor::LogitsPostProcessorConfig ` specifies a map of named callbacks in the following form - -```cpp -std::unordered_map)>> -``` - -The map key is the name associated with that logits post-processing callback. Each request can then specify the name of the logits post-processor to use for that particular request, if any. - -The first argument to the callback is the request id, second is the logits tensor, third are the tokens produced by the request so far, fourth is the operation stream used by the logits tensor, and last one is an optional client id. The callback returns a modified tensor of logits. Multiple requests can share same client id and callback can use different logic based on client id. - -You must use the stream to access the logits tensor. For example, to perform an addition with a bias tensor, the addition operation is enqueued on that stream. Alternatively, you can call `stream->synchronize()`, however, that will slow down the entire execution pipeline. - -The executor also includes a {cpp:class}`LogitsPostProcessorBatched ` method that enables altering logits of multiple requests in a batch. The batched method allows further optimizations and reduces callback overheads. - -```cpp -std::function const&, std::vector&, std::vector> const&, StreamPtr const&, std::vector> const&)> -``` - -A single batched callback can be specified in `LogitsPostProcessorConfig`. Each request can opt to apply this callback by specifying the name of the logits post-processor as `Request::kBatchedPostProcessorName`. - -Note: Neither callback variant is supported with the `STATIC` batching type for the moment. - -In a multi-GPU run, the callback is invoked on all ranks in the first tensor-parallel group, by default. To ensure correct execution, replicate the client-side state that is accessed by the callback on these ranks. If replication is expensive or infeasible, use `LogitsPostProcessorConfig::setReplicate(false)` to invoke the callback only on rank 0. The executor broadcasts the sampled tokens internally to ensure correct execution. - -### Structured output with guided decoding -Guided decoding controls the generation outputs to be amenable to pre-defined structured formats, e.g., JSON or XML. Currently, guided decoding is supported with the [XGrammar](https://github.com/mlc-ai/xgrammar) backend. - -To enable guided decoding, a valid instance of `GuidedDecodingConfig` must be provided when constructing `Executor`. `GuidedDecodingConfig` should be constructed with some tokenizer information, including `encodedVocab`, `tokenizerStr` (optional) and `stopTokenIds` (optional). Given a Hugging Face tokenizer, these can be extracted by: - -```python -encoded_vocab = tokenizer.get_vocab() -encoded_vocab = [token for token, _ in sorted(encoded_vocab.items(), key=lambda x: x[1])] -tokenizer_str = tokenizer.backend_tokenizer.to_str() -stop_token_ids = [tokenizer.eos_token_id] -``` - -Refer to [`tensorrt_llm/llmapi/tokenizer.py`](source:tensorrt_llm/llmapi/tokenizer.py) for more details. You may dump these materials to disk, and reload them to C++ runtime for use. - -Each request can be optionally specified with a `GuidedDecodingParams`, which defines the desired structured format. Currently, it supports four types: -* `GuidedDecodingParams::GuideType::kJSON`: The generated text is amenable to JSON format; -* `GuidedDecodingParams::GuideType::kJSON_SCHEMA`: The generated text is amenable to JSON format with additional restrictions; -* `GuidedDecodingParams::GuideType::kREGEX`: The generated text is amenable to regular expression; -* `GuidedDecodingParams::GuideType::kEBNF_GRAMMAR`: The generated text is amenable to the extended Backus-Naur form (EBNF) grammar. - -The latter three types should be used with the schema/regex/grammar provided to `GuidedDecodingParams`. - -### Obtaining Arbitrary Output Tensors -The executor API gives the user the possibility to read the arbitrary outputs from the model. For example, it is possible to obtain hidden states or logits. - -#### Mark Tensors As Output -For a tensor to be obtainable using this feature, it needs to be marked as an output in the model definition (e.g. add `topk_logits.mark_output("TopKLogits")`) before building the TRT engine. - -#### Configure The Executor -Assuming the TensorRT engine you are planning to use has a tensor named `TopKLogits` marked as output, you should then configure the `Executor` to read from this output tensor by passing its name to the `ExecutorConfig` configuration object: -```cpp -auto const executorConfig = ExecutorConfig{}; - -std::vector additionalOutputs{ - executor::AdditionalModelOutput{"TopKLogits", /*whether or not to get the output for the context too */ true}}; -executorConfig.setAdditionalModelOutputs(additionalOutputs); - -// ... set more configuration options if needed -// ... create the `Executor` instance -``` - -### Request Additional Output -Construct a request to enqueue in the executor to query this tensor output: -```cpp -executor::Request request{requestTokens, parameters.maxOutputLength, true, executor::SamplingConfig{}, - executor::OutputConfig{false, false, false, true, false, false, additionalOutputs}}; -executor.enqueueRequest(request); -``` - -The output can be found at the `additionalOutputs` property of each response. - -#### Note on context outputs - -If KV cache reuse is enabled, context outputs will not contain outputs for the part of the context that has been reused. This part of the outputs can only be obtained from the prior request with the same prefix that generated this part of the KV cache. - -## C++ Executor API Example - -Two C++ examples are provided that show how to use the Executor API and can be found in the [`examples/cpp/executor`](source:examples/cpp/executor/) folder. - -## Python Bindings for the Executor API - -Python bindings for the Executor API are also available to use the Executor API from Python. The Python bindings are defined in [bindings.cpp](source:cpp/tensorrt_llm/nanobind/executor/bindings.cpp) and once built, are available in package `tensorrt_llm.bindings.executor`. Running `help('tensorrt_llm.bindings.executor')` in a Python interpreter will provide an overview of the classes available. - -In addition, three Python examples are provided to demonstrate how to use the Python bindings to the Executor API for single and multi-GPU models. They can be found in [`examples/bindings`](source:examples/bindings). - -## In-flight Batching with the Triton Inference Server - -A Triton Inference Server C++ [backend](https://github.com/triton-inference-server/tensorrtllm_backend) is provided with TensorRT-LLM that -includes the mechanisms needed to serve models using in-flight batching. That -backend is also a good starting example of how to implement in-flight batching using -the TensorRT-LLM C++ Executor API. diff --git a/docs/source/legacy/advanced/expert-parallelism.md b/docs/source/legacy/advanced/expert-parallelism.md deleted file mode 100644 index 55d948a80cae..000000000000 --- a/docs/source/legacy/advanced/expert-parallelism.md +++ /dev/null @@ -1,30 +0,0 @@ -(expert-parallelism)= - -# Expert Parallelism in TensorRT-LLM - -## Mixture of Experts (MoE) - -Mixture of Experts (MoE) architectures have become widespread, with models such as [Mistral Mixtral 8×7B](https://huggingface.co/mistralai/Mixtral-8x7B-v0.1). Specifically, MoE’s structure supports multiple parallel feed-forward neural-network (FFN) layers (called experts) in place of the single FFN layer in a dense model. When tokens arrive, the router layer selects the top-k experts for each token, and the corresponding hidden state of each token is dispatched to those experts. As a result, there are multiple tokens’ hidden states that are dispatched to each expert. - -moe_structure - -the MOE structure in Switch Transformer: [https://arxiv.org/pdf/2101.03961.pdf](https://arxiv.org/pdf/2101.03961.pdf) - -## Tensor Parallel vs Expert Parallel - -Parallelism on multi-GPUs is necessary if the MoE model cannot be accommodated by a single GPU’s memory. We support three kinds of parallel patterns for MoE structure: Tensor Parallel (default pattern), Expert Parallel, and a hybrid of the two. - -tensor parallel vs expert parallel - -Tensor Parallel evenly splits each expert’s weight and distributes them to different GPUs, which means each GPU holds partial weight of all experts, While Expert Parallel evenly distributes some of the experts’ full weight to different GPUs, which means each GPU holds part of the experts’ full weight. As a result, each GPU rank in the Tensor Parallel group receives all tokens’ hidden states for all experts, then computes using the partial weights, while for Expert Parallel, each GPU rank only receives part of tokens’ hidden states for experts on this rank, then computes using the full weights. - -When both Tensor Parallel and Expert Parallel are enabled, each GPU handles a portion of the expert weights matrices (as in EP mode) and these weights are further sliced across multiple GPUs (as in TP mode). This hybrid approach aims to balance the workload more evenly across GPUs, enhancing efficiency and reducing the likelihood of bottlenecks associated with EP mode alone. - - -## How to Enable - -The default parallel pattern is Tensor Parallel. You can enable Expert Parallel or hybrid parallel by setting `--moe_tp_size` and `--moe_ep_size` when calling `convert_checkpoint.py`. If only `--moe_tp_size` is provided, TRT-LLM will use Tensor Parallel for the MoE model; if only `--moe_ep_size` is provided, TRT-LLM will use Expert Parallel; if both are provided, the hybrid parallel will be used. - -Ensure the product of `moe_tp_size` and `moe_ep_size` is equal to `tp_size`, since the total number of MoE parallelism across all GPUs must match the total number of parallelism in other parts of the model. - -The other parameters related to the MoE structure, such as `num_experts_per_tok` (TopK in previous context) and `num_local_experts`, can be found in the model’s configuration file, such as the one for [Mixtral 8x7B model](https://huggingface.co/mistralai/Mixtral-8x7B-v0.1/blob/main/config.json). diff --git a/docs/source/legacy/advanced/gpt-attention.md b/docs/source/legacy/advanced/gpt-attention.md deleted file mode 100644 index d5b4d29718bf..000000000000 --- a/docs/source/legacy/advanced/gpt-attention.md +++ /dev/null @@ -1,343 +0,0 @@ -(gpt-attention)= - -# Multi-Head, Multi-Query, and Group-Query Attention - -This document details the implementation of multi-head attention (MHA), -multi-query attention (MQA) and group-query attention (GQA) for auto-regressive -GPT-like models in TensorRT-LLM. As a quick reminder, the multi-head attention -is the sequence of a batched matmul, a softmax and another batched matmul -described in the -[Attention Is All You Need](https://arxiv.org/abs/1706.03762) article. [Multi-query Attention (MQA)](https://arxiv.org/abs/1911.02150) and [Group-query Attention (GQA)](https://arxiv.org/abs/2307.09288) are variants of MHA that use fewer, so-called, K/V head than the number of query heads. TensorRT-LLM, MHA, MQA and GQA are implemented by the operator [`tensorrt_llm.functional.gpt_attention`](https://github.com/NVIDIA/TensorRT-LLM/blob/main/tensorrt_llm/functional.py). - -## Important Note - -As discussed below, the current implementation supports two input modes: Padded -and packed (non-padded). As the packed mode is always more memory-efficient and -faster than the padded mode, ***support for padded mode may be removed in the -future***. - -## Padded and Packed Tensors - -In TensorRT-LLM, the GPT attention operator supports two different types -of QKV inputs: Padded and packed (i.e. non padded) inputs. The mode is -determined by the global configuration parameter `remove_input_padding` defined -in [`tensorrt_llm.plugin`](https://github.com/NVIDIA/TensorRT-LLM/blob/main/tensorrt_llm/plugin/plugin.py). - -When padding is enabled (that is, `remove_input_padding` is `False`), the sequences -that are shorter than the `max_sequence_length` are padded to that maximum -length. It may result in excessive memory consumption as well as unneeded -computations on padding tokens (in the various matrix multiplications that -surround the MHA block). - -To overcome that problem, TensorRT-LLM supports a mode without padding where -the different tokens are packed together and the user provides the operator -with a 1D tensor containing the lengths of the different sequences. It is -recommended that users to always use packed mode (and support for the padded -mode may be removed in the future). - -## Context and Generation Phases - -The GPT attention operator encapsulates different implementations for both -context and generation phases in auto-regressive models like GPT. - -### Context Phase - -If the `context_fmha_type` is set to `disabled` (refer to -[`tensorrt_llm.plugin`](https://github.com/NVIDIA/TensorRT-LLM/blob/main/tensorrt_llm/plugin/plugin.py)), -the implementation maps to a sequence of GPU kernels that will store the -intermediate `Q*K^T` tensor in memory before calling the softmax operator. It -is the slowest method and the memory footprint is significant (quadratically -depends on the sequence length). - -Otherwise, if `context_fmha_type` is set to a `enabled` or -`enabled_with_fp32_acc` (accumulation in the first batched matmul is forced to -FP32), that function will trigger a kernel that performs the MHA/MQA block -using a single kernel. For short sequences, that kernel uses a vanilla -implementation of MHA/MQA. For larger sequences, this kernel uses the Flash -Attention algorithm as described in -[FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness](https://arxiv.org/abs/2205.14135) -and -[FlashAttention-2: Faster Attention with Better Parallelism and Work Partitioning](https://arxiv.org/abs/2307.08691). - -Currently, the implementation triggers extra kernels that apply pre-processing -to the elements (like RoPE) and populate the KV cache (see below). In a future -release, the number of such kernels is planned on being reduced in order to -improve the overall performance. - -#### FP8 Context FMHA - -When FP8 quantization is activated, the attention can be further accelerated by -enabling FP8 Context FMHA (`use_fp8_context_fmha = enable`). - -FP8 Paged Context FMHA is also supported with the fp8 quantization workflow. -You need to specify `use_fp8_context_fmha = enable` and -`use_paged_context_fmha = enable` at the same time. - -Please be aware that this feature is only supported on Ada and Hopper. - -### Generation Phase - -The generation phase is implemented using a single kernel called the masked -multi-head attention in TensorRT-LLM. That kernel is able to apply -pre-processing on the Q, K, and V elements on-the-fly: adds the QKV bias, applies -RoPE, and performs dequantization and quantization. TensorRT-LLM will continue to add (or -enable) additional features in future releases. For example, enable the support -for IA3. - -_The masked MHA kernel has a special version that distributes the work across -multiple CUDA thread-blocks on the GPU for cases where the GPU occupancy is -low. That mode called multi-block is turned on by default starting from TRT-LLM 0.13, -and can be disabled using `--multi_block_mode=False` during runtime. -Users are recommended to test that mode in scenarios where both the batch -size and the number of heads in the model are relatively small. The exact -definition of small in that context will depend on the model of the GPU and is -hard to predict but to provide with a rule of thumb, it is worth testing that -mode when `batch_size * num_heads` is less than the number of multi-processors -on the GPU (that suggestion may evolve in the future as more research is -conducted and the software improves)_. - -_Note that even if the multi-block mode is enabled, the attention operator will -not immediately trigger the multi-block version of the GPU kernel. There is a -minimum number of tokens (input + generated) that are required for the -multi-block version to become more efficient than the "vanilla" implementation -that uses a single CUDA thread-block per head. It is controlled by an internal -heuristic._ - -Another note is that as the masked MHA kernels use shared memory size -proportional to sequence length, so there can be some cases that GPU's shared -memory is not enough when multi-block mode is not enabled. To get masked MHA -kernel work in these cases, multi-block mode is forced on and a warning log is -printed. - -#### XQA Optimization - -Another optimization for MQA/GQA in the generation phase is called XQA optimization. - -Support matrix of the XQA optimization: - - FP16 / BF16 compute data type. - - FP16 / BF16 / FP8 / INT8 KV cache data type. - - Paged KV cache (8 / 16 / 32 / 64 / 128 tokens per block). - -By default, this is enabled. Note that a heuristic algorithm -is also used to decide whether to use XQA kernel or masked MHA kernel to get -better performance. -If you want to use that kernel whenever possible, set `TRTLLM_FORCE_XQA=1` to force use of the XQA kernel when the model config is supported. -Supported configurations can be found using the `shouldUse` function of the `DecoderXQARunner` class in -`cpp/tensorrt_llm/kernels/decoderMaskedMultiheadAttention/decoderXQARunner.h`. - - -(inflight-batching)= - -## In-flight Batching - -TensorRT-LLM supports in-flight batching of requests (also known as continuous -batching or iteration-level batching) for higher serving throughput. With this feature, -sequences in context phase can be processed together with sequences in -generation phase. The purpose of that technique is to better interleave -requests to reduce latency as well as make better use of the GPUs. -For efficiency reasons (1), the support for inflight batching ***requires the -input tensors to be packed (no padding)***. - -***In the current implementation, the sequences that are going through the -context phase must be before the sequences in the generation phase in the input -tensor. For example, for sequences `S0`, `S1` and `S2`, if `S0` and `S2` are in -context phase (and `S1` in generation), tokens from `S0` and `S2` must appear -before the tokens of `S1` in the input tensor***. The constraint may or may not -be relaxed in a future version. - -_(1) Padding sequences in the generation phase, that contain a single token, to -the length of the maximum input sequence is inefficient use of resources_. - - - -## Chunked Context - -In the original state, the common behavior was to process all context tokens at -once. This feature splits the context into several chunks. In this way, the -context chunks can be batched with more tokens during the generation phase, -which is expected to increase the total throughput. Chunking contexts also removes -constraints on input length. To enable this feature, the FMHA paged kv-cache also -needs to be enabled. Except for the last one, the size of the context chunk needs -to be an integer multiple of the kv-cache block size. Refer to -[the chunked context feature](../../features/long-sequence.md) for usage. - -## KV Cache - -In the generation phase, a common optimization is to provide the MHA kernel -with a cache containing the values of the past K and V elements that have -already been computed. That cache is known as the KV cache. TensorRT-LLM uses -that technique to accelerate its generation phase. In TensorRT-LLM, there is -one KV cache per Transformer layer, which means that there are as many KV -caches as layers in a model. The current version of TensorRT-LLM supports two -different types of KV caches: **contiguous** and **paged** KV caches. - -### Contiguous KV Cache - -The contiguous KV cache is a monolithic tensor. Its shape is: -``` -[max_batch_size * max_beam_width, 2, num_heads, max_seqlen, hidden_dim_per_head]. -``` - -That implementation uses a lot more memory than needed when the sequences are -shorter than the maximum sequence length (even if they end up close to the -limit after the generation of many output tokens, it may take a lot of steps to -reach that point). - -### Paged KV Cache - -The paged KV cache decomposes the KV cache into blocks that are distributed to -the different requests by a cache manager during processing. That cache manager -keeps track of the sequences, allocate new blocks from a pool and recycle those -blocks when required. See the simplified implementation of -[`tensorrt_llm.runtime.KVCacheManager`](source:tensorrt_llm/runtime/kv_cache_manager.py). -A more efficient C++ implementation is included in the -[Batch Manager](source:cpp/include/tensorrt_llm/batch_manager). - -## INT8/FP8 KV Caches - -In its current implementation, even if the rest of the network runs in INT8 or -FP8, the GPT attention operator works with FP32, FP16, and BFloat16 inputs and -outputs. However, TensorRT-LLM supports INT8 and FP8 -(`kv_cache_quant_mode=QuantMode.INT8_KV_CACHE` and -`kv_cache_quant_mode=QuantMode.FP8_KV_CACHE`) KV caches. - -The GPT attention operator populates the KV cache. When INT8 or FP8 KV caches -are enabled, the input values have to be quantized to 8 bits using a scaling -factor. For quantization, the scaling factor is stored in the -`kv_cache_scaling_factor` tensor. Its shape is `[1]` and only per-tensor -quantization is supported in the current version. Quantization uses inversed scale -since it does multiply as `fp_value * (1.0 / kv_cache_scaling_factor)` in plugin. - -During generation, the values read from the cache are dequantized on-the-fly in -the MHA/MQA kernel, dequantization can be described as -`quantized_value * kv_cache_scaling_factor`. - - -## Sliding Window Attention, Cyclic (Rolling Buffer) KV Cache - -TensorRT-LLM has a feature called `Cyclic KV Cache`, which treats the kv cache -as a circular buffer. This means that it only stores the kv cache for the last N -tokens, where N is determined by the `max_attention_window_size` parameter in -`GenerationSession.setup`. You can see examples of this in the `run.py` or -`summarize.py` files. When the cache is full, new tokens’ kv cache will -overwrite the "least recently used" caches. - -In the context phase, if the input length surpasses the `max_attention_window_size`, -`Sliding Window Attention` will be activated. This serves the same function as -the `sliding window_size`. - -This feature helps to reduce the memory footprint of the kv cache when -dealing with very long sequences. - -The feature, which allows different `max_attention_window_size` values -for each layer, is also supported. To utilize this feature, simply provide an -`int32 torch.Tensor` or `list` to the `GenerationSession.setup` when using python -runtime session, or provide a vector to the `KvCacheConfig` when using cpp runtime. -If the number of the provided elements is less than the number of layers, the provided -tensor/list/vector will be repeated multiple times to the number of layers and then be -saved as a new tensor. This tensor will serve as the buffer for `max_attention_window_size`, -setting unique values for each layer. However, it’s important to note that the -memory allocation for the kv cache still relies on the buffer’s maximum value. - -_Note that the cyclic kv cache feature doesn't work with beam searching currently as -the context kv cache are shared across beams. - -## StreamingLLM - -The StreamingLLM feature uses a window attention to perform efficient and stable LLM -on long texts, which means that only `N` tokens need to be stored in the KV cache. -Similar to the cyclic KV cache feature in TensorRT-LLM, `max_attention_window_size` -parameter is used to determine `N`. Different from the cyclic KV cache feature, -the first `S` tokens, called sink tokens, are always kept in the attention window, -where `S` is determined by `sink_token_length` parameter in `GenerationSession.setup`. -But in context phase, the self-attention is dense in the official implementation of -StreamingLLM, and it uses all of the tokens for computation and only saves `N` tokens -to the KV cache. - -In addition, the relative position embedding is also changed in StreamingLLM. -When determining the relative distance and adding positional information to tokens, -StreamingLLM uses the positions within the cache rather than those in the original text. - -`streamingllm` flag is used to enable this feature. - -## Beam-Search - -The GPT attention operator supports beam-search. In the context phase, a single -beam is computed per input sequence. In the generation phase, the MHA/MQA/GQA -kernel uses an additional tensor to reconstruct the correct path for each beam. -That tensor is called the `cache_indirection`. Its shape is `[batch_size, -beam_width, max_seqlen]`. - -For a sequence `si`, a beam `bi` and a token `ti`, the element -`cache_indirection[si][bi][ti]` is an integer between `0` and `beam_width-1` -that indicates which path in the beam to read the K and V elements from in the -KV cache. This tensor is populated in the sampling stage. - -## Input QKV tensor - -The input QKV tensor packs the Q, K and V tensors (concatenated along the last -dimension) after the projection of the hidden states. It is a 3D tensor. RoPE -and quantization to INT8 or FP8 (when needed) are performed by the GPT -attention operator. - -In padded mode, its shape is `[batch_beam_size, max_seqlen, 3 * hidden_dim]` -where `batch_beam_size` is the batch size (number of sequences) for the context -phase and the batch size multiplied by the beam width for the generation phase. -Having different beam widths per sequence in padded mode is not supported. - -In packed mode, its shape is `[num_tokens, 3 * hidden_dim]` where -`num_tokens` is the total number of tokens in the batch. For the sequences in -context phase, the number of tokens of a sequence corresponds to its input -length (even if the beam width is greater than `1` for beam search). For the -sequences in generation phase, there are `beam_width` tokens per sequence. The -beam width can be different for each sequence. - -In other words, the pseudo-code to compute the number of tokens is: - -```python -num_tokens = 0 - -# Add the length of each sequence in context phase. -for seq in context_phase: - num_tokens += seq.length - -# Add the width of the beam for each sequence in generation phase. -for seq in generation_phase: - num_tokens += seq.beam_width -``` - -### Rotary Positional Embedding (RoPE) - -The GPT attention operation can perform the computation of the Rotary -Positional Embedding (RoPE). When that operation is enabled, -`rotary_embedding_dim` is set to a value greater than 0, it is fused with other -operations. The GPT operator supports GPT-NeoX and GPT-J forms of RoPE by -setting `position_embedding_type` to `PositionEmbeddingType.rope_gpt_neox` -or `PositionEmbeddingType.rope_gptj`. - -### ALiBi - -The GPT attention operator can apply ALiBi to the result of the `Q*K^T` -product. The bias is computed on-the-fly from the ALiBi slopes in the optimized -kernel. - -### Scaling factor(s) - -In MHA, the output of the `Q*K^T` product is scaled by a constant value that -is computed as: - -``` -norm_factor = 1.f / (q_scaling * sqrt(head_size)). -``` - -### Cross Attention - -On top of the MHA as self attention needed by GPT-style decoder-only models, `gpt_attention` also supports cross attention. - -This enables using `gpt_attention` in a broader aspect as a generic decoder component. For example, the Encoder-Decoder model uses `gpt_attention` to issue both the self attention and cross attention modules in its Decoder. - -### Relative Attention Bias (RAB) - -Relative attention bias (RAB) is a kind of relative position modeling, adding an attention bias (`Q*K^T+bias`) according to relative positions. RAB is a lightweight method to include the information of relative positions, and is used in the popular Encoder-Decoder model [T5](https://huggingface.co/docs/transformers/model_doc/t5) and also other models in the T5 family. - -RAB is supported in two modes: i) regular mode which user passes in relative attention bias computed ahead of MHA. ii) implicit mode which computes the relative attention bias on the fly in MHA. The implicit mode suits the case when the relative attention bias is too large to fit in memory and can be turned on by passing in `max_distance`. diff --git a/docs/source/legacy/advanced/gpt-runtime.md b/docs/source/legacy/advanced/gpt-runtime.md deleted file mode 100644 index e384c45175e4..000000000000 --- a/docs/source/legacy/advanced/gpt-runtime.md +++ /dev/null @@ -1,202 +0,0 @@ -(gpt-runtime)= - -# C++ GPT Runtime - -TensorRT-LLM includes a C++ component to execute TensorRT engines built with -the Python API as described in the {ref}`architecture-overview` section. -That component is called the C++ runtime. - -The API of the C++ runtime is composed of the classes declared in -[`cpp/include/tensorrt_llm/runtime`](https://github.com/NVIDIA/TensorRT-LLM/tree/main/cpp/include/tensorrt_llm/runtime) and -implemented in [`cpp/tensorrt_llm/runtime`](https://github.com/NVIDIA/TensorRT-LLM/tree/main/cpp/tensorrt_llm/runtime). - -Even if the different components described in that document mention GPT in -their name, they are not restricted to this specific model. Those classes can -be used to implement auto-regressive models like BLOOM, GPT-J, GPT-NeoX or -LLaMA, for example. - -Complete support of encoder-decoder models, like T5, will be added to -TensorRT-LLM in a future release. An experimental version, only in Python for -now, can be found in the [`examples/models/core/enc_dec`](https://github.com/NVIDIA/TensorRT-LLM/tree/main/examples/models/core/enc_dec) folder. - -## Overview - -Runtime models are described by an instance of the -[`ModelConfig`](https://github.com/NVIDIA/TensorRT-LLM/blob/main/cpp/include/tensorrt_llm/runtime//modelConfig.h) -class and a pointer to the TensorRT engine that must be -executed to perform the inference. -The environment is configured through the -[`WorldConfig`](https://github.com/NVIDIA/TensorRT-LLM/blob/main/cpp/include/tensorrt_llm/runtime/worldConfig.h) -(that name comes from -[MPI](https://en.wikipedia.org/wiki/Message_Passing_Interface) and its "famous" -`MPI_COMM_WORLD` default communicator). -The [`SamplingConfig`](https://github.com/NVIDIA/TensorRT-LLM/blob/main/cpp/include/tensorrt_llm/runtime/samplingConfig.h) -class encapsulates parameters that control the -[generation](https://huggingface.co/blog/how-to-generate) of new tokens. - -### Model Configuration - -The model configuration is an instance of the -[`ModelConfig`](https://github.com/NVIDIA/TensorRT-LLM/blob/main/cpp/include/tensorrt_llm/runtime//modelConfig.h) class. -That class encapsulates the following parameters (they are declared as private -member variables and exposed through getters and setters): - - * `vocabSize`, the size of the vocabulary, - * `numLayers`, the number of layers in the model, - * `numHeads`, the number of heads in the attention block, - * `numKvHeads`, the number of heads for K and V in the attention component. - When the number of K/V heads is the same as the number of (Q) heads, the - model uses multi-head attention. When the number of K/V heads is 1, it uses - multi-query attention. Otherwise, it uses group-query attention. Refer to {ref}`gpt-attention` for more information, - * `hiddenSize`, the size of the hidden dimension, - * `dataType`, the datatype that was used to build the TensorRT engine and that - must be used to run the model during inference, - * `useGptAttentionPlugin`, indicates if the {ref}`gpt-attention` operator was compiled using the - [GPT Attention plugin](https://github.com/NVIDIA/TensorRT-LLM/tree/main/cpp/tensorrt_llm/plugins/gptAttentionPlugin), - * `inputPacked`, indicates that the input must be packed (or padded when set - to `false`). For performance reasons, it is recommended to always use packed, - even if its default is set to `false` (will be changed in a future release). - Refer to {ref}`gpt-attention` for more information, - * `pagedKvCache`, indicates if the K/V cache uses paging. - Refer to {ref}`gpt-attention` for more information, - * `tokensPerBlock`, is the number of tokens in each block of the K/V cache. - It's relevant when the paged K/V cache is enabled. By default, the value is - 64. Refer to {ref}`gpt-attention` for more information, - * `quantMode`, controls the quantization method. Refer to {ref}`precision` for more information. - * `maxBatchSize`, indicates the maximum batch size that the TensorRT engine - was built for, - * `maxInputLen`, the maximum size of the input sequences, - * `maxSequenceLen`, the maximum total size (input+output) of the sequences. - -### World Configuration - -Familiarity with -[MPI](https://en.wikipedia.org/wiki/Message_Passing_Interface), is not required -to utilize the TensorRT-LMM C++ runtime. There are two main things -you need to know: -* The C++ Runtime in TensorRT-LLM uses -[processes](https://en.wikipedia.org/wiki/Process_(computing)) to execute -TensorRT engines on the different GPUs. Those GPUs can be located on a single -node as well as on different nodes in a cluster. Each process is called a -*rank* in MPI. -* The ranks are grouped in communication groups. The -TensorRT-LLM C++ Runtime calls that group the *world*. - -The world configuration is an instance of the -[`WorldConfig`](https://github.com/NVIDIA/TensorRT-LLM/blob/main/cpp/include/tensorrt_llm/runtime/worldConfig.h) -class, which encapsulates the following parameters: - -* `tensorParallelism`, the number of ranks that collaborate together to - implement Tensor Parallelism (TP). With TP, each GPU performs computations for - all the layers of the model. Some of those computations are distributed - across the GPU. TP is more balanced than Pipeline Parallelism (PP), in most cases, but - requires higher bandwidth between the GPUs. It is the recommended setting in - the presence of NVLINK between GPUs, -* `pipelineParallelism`, the number of ranks that collaborate together to - implement Pipeline Parallelism (PP). With PP, each GPU works on a subset of - consecutive layers. Communications between the GPUs happen only at the - boundaries of the subsets of layers. It is harder to guarantee the full - utilization of the GPUs with PP but it requires less memory bandwidth. It - is the recommended setting in the absence of NVLINK between GPUs, -* `rank`, the unique identifier of the rank, -* `gpusPerNode`, indicates the number of GPUs on each node. Having that - information allows the C++ runtime to optimize communications between GPUs in - a node (like taking advantage of the - [NVLINK](https://www.nvidia.com/en-us/data-center/nvlink/) - interconnect between GPUs of an A100 - [DGX](https://www.nvidia.com/en-us/data-center/dgx-platform/) - node). - -### Sampling Parameters - -The [`SamplingConfig`](https://github.com/NVIDIA/TensorRT-LLM/blob/main/cpp/include/tensorrt_llm/runtime/samplingConfig.h) -class encapsulates parameters that control the -[generation](https://huggingface.co/blog/how-to-generate) of new tokens. -A comparison of selecting decoding method is listed as the table below (`X` means it is not supported yet). -Except for the `beamWidth` parameter, all the fields are optional and the -runtime will use a default value if no values are provided by the user. For -vector fields, the TensorRT-LLM runtime supports one value per sequence (that is, -the vector contains `batchSize` values). If all the sequences use the same -value for a given parameter, the vector can be limited to a single element -(that is, `size() == 1`). - -| Method name in HF | Condition in HF | Method name in TRT-LLM | Condition in TRT-LLM | -| :------------------------------: | :---------------------------------------------------: | :--------------------: | :--------------------------------------------: | -| assisted decoding | `assistant_model` or `prompt_lookup_num_tokens!=None` | X | | -| beam-search decoding | `num_beams>1` and `do_sample=False` | beam search | `beamWidth > 1` | -| beam-search multinomial sampling | `num_beams>1` and `do_sample=True` | X | | -| constrained beam-search decoding | `constraints!=None` or `force_words_ids!=None` | X | | -| contrastive search | `penalty_alpha>0` and `top_k>1` | X | | -| diverse beam-search decoding | `num_beams>1` and `num_beam_groups>1` | X | | -| greedy decoding | `num_beams=1` and `do_sample=False` | sampling | `beamWidth == 1` and `topK=0` and `topP=0.0f` | -| multinomial sampling | `num_beams=1` and `do_sample=True` | sampling | `beamWidth == 1` and (`topK>0` or `topP>0.0f`) | - -***General*** - -| Name in TRT-LLM | Description | Data type | Range of value | Default value | Name in HF | -| :-----------------: | :-------------------------------------------------------------------------------: | :-----------: | :---------------------------------------------------------------------------------------: | :---------------------------------------------------: | :--------------------: | -| `temperature` | modulation of logits in sampling workflow | List\[Float\] | \[0.0f, $+\infty$\) | `1.0f` (no modulation) | `temperature` | -| `minLength` | lower-bound on the number of tokens generated | List\[Int\] | \[0, $+\infty$\) | `0` (no effect (the first generated token can be EOS) | `min_length` | -| `repetitionPenalty` | penalize repetitive tokens
multiplicative, irrespective of appearances count | List\[Float\] | \[0.0f, $+\infty$\)
`< 1.0f` encourages repetition
`> 1.0f` discourages it | `1.0f` (no effect) | `repetition_penalty` | -| `presencePenalty` | penalize existed tokens
additive, irrespective of appearances count | List\[Float\] | \($-\infty$, $+\infty$\)
`< 0.0f` encourages repetition
`> 0.0f` discourages it | `0.0f` (no effect) | no | -| `frequencyPenalty` | penalize existed tokens
additive, dependent on appearances count | List\[Float\] | \($-\infty$, $+\infty$\)
`< 0.0f` encourages repetition
`> 0.0f` discourages it | `0.0f` (no effect) | no | -| `noRepeatNgramSize` | | List\[Int\] | \[0, $+\infty$\)
`> 0` all ngrams of that size can only occur once | `0` (no effect) | `no_repeat_ngram_size` | - -* The tokens of input prompt are included during adopting `repetitionPenalty`, `presencePenalty`, and `frequencyPenalty` onto logits. - -* The parameters `repetitionPenalty`, `presencePenalty`, and `frequencyPenalty` are not mutually exclusive. - -***Sampling*** - -| Name in TRT-LLM | Description | Data type | Range of value | Default value | Name in HF | -| :-------------: | :---------------------------------------------------------------------: | :-----------: | :---------------: | :--------------: | :--------: | -| `randomSeed` | random seed for random number generator | Int64 | \[0, 2^64-1\] | `0` | no | -| `topK` | the number of logits to sample from | List\[Int\] | \[0, 1024\] | `0` | `top_k` | -| `topP` | the top-P probability to sample from | List\[Float\] | \[0.0f, 1.0f\] | `0.0f` | `top_p` | -| `topPDecay` | the decay in the `topP` algorithm | List\[Float\] | \(0.0f, 1.0f\] | `1.0f` | no | -| `topPMin` | the decay in the `topP` algorithm | List\[Float\] | \(0.0f, 1.0f\] | `1.0e-6,f` | no | -| `topPResetIds` | the decay in the `topP` algorithm | List\[Int\] | \[-1, $+\infty$\) | `-1` (no effect) | no | -| `minP` | scale the most likely token to determine the minimum token probability. | List\[Float\] | \[0.0f, 1.0f\] | `0.0` (no effect) | `min_p` | - - * If setting `topK = 0` and `topP = 0.0f`, greedy search is performed. - * If setting `topK > 0` and `topP = 0.0f`, `topK` tokens of highest probabilities will become the candidates of sampling (named `TopK sampling` in TRT-LLM). - * If setting `topK = 0` and `topP > 0.0f`, tokens will be sorted with probability descendly, then the tokens with highest probabilities which the accumulated probability larger than `topP` will become the candidates of sampling (named `TopP sampling` in TRT-LLM). - * If setting `topK > 0` and `topP > 0.0f`, `topK` tokens of highest probabilities will be selected, then those selected tokens will be sorted with probability descendly and their probability will be normalized, then the tokens with highest normalized probabilities which the accumulated probability larger than `topP` will become the candidates of sampling (named `TopKTopP sampling` in TRT-LLM) - - * If different `topK` values are provided for the different sequences in the batch, the performance of the implementation will depend on the largest value. For efficiency reasons, we recommend to batch requests with similar `topK` values together. - - * `topPDecay`, `topPMin` and `topPResetIds` are explained in - [_Factuality Enhanced Language Models for Open-Ended Text Generation_](https://arxiv.org/abs/2206.04624). - `topPDecay` is the decay, `topPMin` is the lower-bound and `topPResetIds` indicates where to reset the decay. - - * `minP` is explained in [_Turning Up the Heat: Min-p Sampling for Creative and Coherent LLM Outputs_](https://arxiv.org/abs/2407.01082). - - * TensorRT-LLM does not generate all possible tokenizations of a word. Therefore, stop words may appear in the output if there are multiple ways to tokenize a stop word and the token sequence in the output differs from the one in `stopWords`. - -***Beam-search*** - -| Name in TRT-LLM | Description | Data type | Range of value | Default value | Name in HF | -| :-----------------------: | :-----------------------------: | :-----------------: | :----------------------: | :-----------------------: | :-----------------: | -| `beamWidth` | width for beam-search algorithm | Int | \[0, 1024\] | `0` (disable beam search) | `beam_width` | -| `beamSearchDiversityRate` | diversity of generated tokens | List\[Float\] | \[0, $+\infty$\) | `0.0f` | `diversity_penalty` | -| `lengthPenalty` | penalize longer sequences | List\[Float\] | \[0, $+\infty$\) | `0.0f` | `length_penalty` | -| `earlyStopping` | see description below | List\[Int\] | \($-\infty$, $+\infty$\) | `0` | `early_stopping` | -| `beamWidthArray` | see description below | List\[List\[Int\]\] | \[0, 1024\] | `` | no | - - * Beam-search algorithm: [beam search](https://en.wikipedia.org/wiki/Beam_search). - * Parameter `diversity_penalty` in HF is only used for `diverse beam-search decoding` (or named `Group-Beam-Search`), which is not supported by TRT-LLM yet. - * If setting `earlyStopping = 1`, decoding will stop once `beamWidth` finished sentences are generated. - * If setting `earlyStopping = 0`, decoding will keep going until no better sentences (with better score) can be generated. - * If setting `earlyStopping` to other values, decoding will stop only depending on `lengthlengthPenalty`. - * `beamWidthArray` is a list of beam width for each step. Using `beamWidthArray = [20,40,80]` as an example, -beam width will be 20 for the first step, 40 for second step, 80 for the later all steps. - * The `beamWidth` parameter is a scalar value. It means that in this release of -TensorRT-LLM, it is not possible to specify a different width for each input -sequence. This limitation is likely to be removed in a future release. - -### Internal Components - -The [`TllmRuntime`](https://github.com/NVIDIA/TensorRT-LLM/blob/main/cpp/tensorrt_llm/runtime/tllmRuntime.h) is in charge of the execution of the TensorRT engine. -The `TllmRuntime` class is an internal component and you are not expected to use that class directly. -The [`GptDecoder`](https://github.com/NVIDIA/TensorRT-LLM/blob/main/cpp/include/tensorrt_llm/runtime/gptDecoder.h) generates tokens from the logits. -The `GptDecoder` can be used directly to implement a custom generation loop and for use cases that cannot be satisfied by the TRT-LLM implementation. diff --git a/docs/source/legacy/advanced/graph-rewriting.md b/docs/source/legacy/advanced/graph-rewriting.md deleted file mode 100644 index 40552a0497d9..000000000000 --- a/docs/source/legacy/advanced/graph-rewriting.md +++ /dev/null @@ -1,197 +0,0 @@ -(graph-rewriting)= - - -# Graph Rewriting Module - -TensorRT-LLM uses a declarative approach to define neural networks and contains -techniques to optimize the underlying graph. It provides a wrapper similar to PyTorch's Module. When a user invokes the `forward` method, the layers are lowered to TensorRT's `ILayer`s and become part of an `INetworkDefinition`. The Graph Rewriting (GW) module can be used to manipulate the network at the `ILayer`/`INetworkDefinition` level. - -## When to Use Graph Rewriting? - -For network manipulation, there are two options in TensorRT-LLM: - -1. **Module Rewriting:** This method modifies the members of `Module` instances before triggering the `forward` method (that is, creating the TensorRT graph). It works on the highest level of the network representation and facilitates the modification of sequences of operations (like modifying the GEMM + activation for SmoothQuant), - -2. **Graph Rewriting:** Graph Rewriting manipulates TensorRT's `INetworkDefinition` after the `forward` method is triggered. It operates at a finer-grained `ILayer` level and can alter the structure across multiple Module instances. It is typically used for layer fusion. - -Graph Rewriting (GW) is ideally used in the following conditions: - -1. When only `ILayer`/`INetworkDefinition` is available, -2. When Module Rewriting would lead to nested control flow or scattered functionality. - -## Graph Rewriting APIs - -Several core APIs are provided for Graph Rewriting: - -### Tensor-Related Methods - -- `Tensor.get_parent`: Get the `ILayer` that produces this tensor, -- `Tensor.get_users`: Get the consumer `ILayer`s of this tensor, -- `replace_all_uses_with`: Replace this tensor with another tensor in all consumer `ILayer`s. - -### FLayerInfo for Retrieving High-Level Information for a Functional - -For all the layers located in `functional.py`, the original input information is missing once lowered to `INetworkDefinition`, especially for TensorRT plugins, which are opaque in the Python world. `FLayerInfo` holds their original information as a high-level signature containing inputs like `Tensor`, Python attributes, and more. There is a Network-wise singleton called `FLayerInfoMemo` to map each `ILayer` to its corresponding `FLayerInfo`. - -For `FLayerInfo`: - -- `FLayerInfo.replace_input_with`: Replace some input tensor with another tensor, -- `FLayerInfo.replace_output_uses_with`: Redirect the usage of the original output tensors to a set of new tensors. - -For `FLayerInfoMemo`: - -- `FLayerInfoMemo.instance()`: Get the singleton instance, -- `FLayerInfoMemo.get`: Get the corresponding `FLayerInfo` for an `ILayer`. - -`FLayerInfo` remains consistent with the actual `ILayer` during GW, making it safe to use. - -### Pattern and Pattern Manager - -There are two kinds of patterns: - -- `PatternRewriter`: Used for defining a rewriting pattern, which actually alters the network. - - `match`: Match the pattern; returns true if a layer is matched, - - `rewrite`: Manipulate a layer, - - `match_and_rewrite`: Combines both `match` and `rewrite`, used for complex states that need to pass from `match` to `rewrite`. - -- `PatternAnalyzer`: Used for defining an analysis pattern, which collects information from the network. - - `match`: Match the pattern, - - `analyze`: Perform analysis on a list of layers. - -There are two managers for managing multiple `PatternRewriter` or `PatternAnalyzer`: - -- `RewritePatternManager`: - - `add`: Add a pattern with its label and benefit; the benefit specifies its privilege, - - `get`: Get a pattern by label, - - `rewrite`: Apply the rewriting patterns contained to a network. - -- `AnalysisPatternManager`: - - `add`: Add a pattern with its label and benefit; the benefit specifies its privilege, - - `get`: Get a pattern by label, - - `analyze`: Apply the analysis patterns contained to a network. - -### @record_signature to Decorate Functionals Requiring FLayerInfo - -The `@record_signature` decorator is used to record the `FLayerInfo` for a functional. While FLayerInfo is vital for GW when analyzing or rewriting certain functionals, it is used in an "add as needed" manner. If you are adding GW patterns, ensure that the functional requires the `@record_signature` decorator. - -## Classical Workflow - -There are specific routines for defining a GW pattern. Let's start with a simple example: replacing a sum layer with a subtract layer, which can also be found in the `test_graph_rewriting.py` file. - -```python -class NaivePatternRewriter_ReplaceAddWithSub(PatternRewriter): - - def __init__(self): - super().__init__('replace_add_with_sub', - root_layer={trt.LayerType.ELEMENTWISE}, - separate_match_rewrite=True) - - def match(self, layer: Layer): - # The rewriter will stop at the first matched layer, and then the Rewriter will enter the rewrite() to do the rewriting. - return layer.as_layer().op == trt.ElementWiseOperation.SUM - - def rewrite(self, layer: Layer) -> None: - # The layer here should be an Elementwise_SUM layer. - with net_guard(layer.network): - # There are several stages to replace some subgraph with another subgraph: - - # Stage 1: Get the input tensors and output tensors of the subgraph to replace. - # - For Elementwise_SUM, there are two inputs and one output. - a, b = layer.get_inputs(0, 1) - o = layer.get_outputs(0)[0] - - # Stage 2: Create a new subgraph that takes the old one's inputs. - # - Here we insert an Elementwise_SUB layer, and 'c' is the output. - c = a - b - - # Stage 3: Redirect all the layers depending on the outputs of the old subgraph to the new subgraph's. - # - After this, the SUM becomes dangling and will be pruned by TensorRT when building the engine. - # - Note that there is no API in TensorRT python to remove a layer explicitly; `replace_all_uses_with` is the only way to "remove" a layer. - o.replace_all_uses_with(c) - - # Stage 4: Mark all the layers in the old subgraph as removed. - # - This helps the PatternRewriter to skip the removed layers. - layer.mark_as_removed() -``` - -In this example, we deal with `ILayer` rather than Plugins, so `FLayerInfo` is unnecessary. As illustrated in the `rewrite` method, there are four stages that are shared across nearly all rewrite patterns. - -Note that in GW, we **NEVER** rewrite a layer directly. Instead, we do it in two steps: first, create another layer with the same input and deprive all the users of the original outputs, redirecting them to the outputs of the new layers. In this way, the old layer will be dangling and pruned automatically by TensorRT during the engine building phase. This is a limitation of TensorRT since remove-layer-like APIs are not available in Python. - -In Stage 2, we rely on operators and layers commonly used during the network building phase. Ideally, you can replace them with any network structure during GW. - -For the usage of `FLayerInfo`, let's rewrite the `gpt_attention` to enable the `remove-padding` feature. `gpt_attention` is actually - - a TensorRT plugin, so we need `FLayerInfo` to hold the original Tensor-wise inputs to help create new `gpt_attention` layers. - -```python -class GPTAttentionPluginRemovePaddingRewritePass(PatternRewriter): - - def __init__(self): - super().__init__('gpt_attention_plugin_remove_padding', - root_layer={trt.LayerType.PLUGIN_V2}) - - def match_and_rewrite(self, layer: Layer) -> bool: - if layer.as_layer().type != trt.LayerType.PLUGIN_V2 or \ - layer.as_layer().plugin.plugin_namespace != 'tensorrt_llm' or \ - layer.as_layer().plugin.plugin_type != 'GPTAttention': - return False - - # Retrieve the FLayerInfo - flayer = FLayerInfoMemo.instance().get(layer.name) - assert flayer - # Although the layer is a plugin, which is a black box, we get some high-level input information from the FLayerInfo. - tensor_input: Tensor = flayer.get_input('qkv') - if tensor_input.shape[0] == 1: # Already in remove-padding mode - return False - - # Some information could be passed in from external - assert self.args is not None, "args should be passed in from RewritePatternManager.rewrite()" - batch_size, in_len, hidden_size = self.args['batch_size'], self.args['in_len'], self.args['hidden_size'] - - with net_guard(layer.network): - new_inputs = flayer.clone_inputs() - - # Step 1: Create new inputs and replace the original arglist. - input = Tensor( - name='qkv', - dtype=trt.float16, - shape=(1, batch_size * in_len, hidden_size), - ) - new_inputs['qkv'] = input - - # Step 2: Create a new plugin instance. - new_outs = gpt_attention(**new_inputs) - - # Step 3: Deprive all the users of the old plugin instance. - flayer.replace_outputs_uses_with(layer.network, new_outs) - - # Step 4: Remove the old plugin instance. - layer.mark_as_removed() - - return True -``` - -This is quite similar to the first example, with the focus on the `FLayerInfo` part. Through the code below, we can get the original inputs of this layer, enabling us to alter the inputs related to remove-padding and create a new layer to replace it. - -```python -flayer = FLayerInfoMemo.instance().get(layer.name) -assert flayer -``` - -```python -new_inputs = flayer.clone_inputs() - -# Step 1: Create new inputs and replace the original arglist. -input = Tensor( - name='tensor', - dtype=trt.float16, - shape=(1, batch_size * in_len, hidden_size), -) -new_inputs['tensor'] = input - -# Step 2: Create a new plugin instance. -new_outs = gpt_attention(**new_inputs) -``` - -For real examples, please refer to the `FuseAttentionWithBiasPass` in the `graph_rewriting.py`. diff --git a/docs/source/legacy/advanced/images/disaggregated-service_usage.png b/docs/source/legacy/advanced/images/disaggregated-service_usage.png deleted file mode 100644 index 6b98a223322753da05e305f51497fd3bea2ce4ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39888 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