这是 LLM 基准测试系列 的第二篇文章,介绍了在使用 NVIDIA NIM 部署 Meta Llama 3 模型 时,如何使用 GenAI-Perf 对其进行基准测试。
在构建基于 LLM 的应用时,了解这些模型在给定硬件上的性能特征至关重要。这有多种用途:
- 识别瓶颈和潜在优化机会
- 确定服务质量和吞吐量 tradeoff
- 基础架构配置
作为以客户端 LLM 为中心的基准测试工具, NVIDIA GenAI-Perf 提供了以下关键指标:
- 首次令牌生成时间 (TTFT)
- 令牌间延迟 (ITL)
- 每秒令牌数 (TPS)
- 每秒请求数 (RPS)
- … 等等
GenAI-Perf 还支持任何符合 OpenAI API 规范 (业内广泛接受的默认标准)的 LLM 推理服务。
在本基准测试指南中,我们使用了 NVIDIA NIM ,这是一系列推理微服务,可为基础 LLM 和微调 LLM 提供高吞吐量和低延迟推理。NIM 具有易用性、企业级安全性和可管理性。
为优化您的 AI 应用,本文将介绍如何为 Llama 3 设置 NIM 推理微服务,使用 GenAI-Perf 来衡量性能并分析输出。
为何使用 GenAI-Perf 对模型性能进行基准测试?
NVIDIA NIM 微服务是经过优化的预封装容器,支持在云、数据中心和 NVIDIA RTX AI PC 等各种平台上轻松部署和运行 AI 模型,从而加速任何领域的 生成式 AI 开发。NIM LLM 由行业领先的后端提供支持,包括 TensorRT-LLM 和 vLLM。NIM 不断改进,即使在相同的硬件生成上也能提供最佳性能。
借助 GenAI-Perf,您不仅可以重现和验证这些性能结果,还可以使用自己的硬件进行基准测试。有关此过程的更多信息,请参阅 NVIDIA NIM 性能 。
使用 NIM 进行基准测试的步骤
NIM 性能 页面上的所有性能数据均通过 NVIDIA GenAI-Perf 工具收集。按照本文其余部分中介绍的分步方法,您可以在自己的硬件上重现这些性能数字以及基准测试模型。
使用 NIM 设置兼容 OpenAI 的 Llama-3 推理服务
NIM 为将 LLM 和其他 AI 基础模型投入生产提供了最简单、最快速的方法。有关更多信息,请参阅 使用 NVIDIA NIM 部署生成式 AI 的简单指南 或 适用于大语言模型的最新 NVIDIA NIM 文档 ,该文档为您介绍了硬件要求和前提条件,包括 NVIDIA NGC API 密钥。
使用以下命令部署 NIM 并执行 inference:
export NGC_API_KEY = <YOUR_NGC_API_KEY> # Choose a container name for bookkeeping export CONTAINER_NAME = llama - 3.1 - 8b - instruct # Choose a LLM NIM Image from NGC export IMG_NAME = "nvcr.io/nim/meta/${CONTAINER_NAME}:latest" # Choose a path on your system to cache the downloaded models export LOCAL_NIM_CACHE = . / cache / nim mkdir - p "$LOCAL_NIM_CACHE" # Start the LLM NIM docker run - it - - rm - - name = $CONTAINER_NAME \ - - gpus all \ - - shm - size = 16GB \ - e NGC_API_KEY \ - v "$LOCAL_NIM_CACHE:/opt/nim/.cache" \ - u $( id - u) \ - p 8000 : 8000 \ $IMG_NAME |
在此代码示例中,我们指定了 Meta Llama-3.1-8b-Instruct 模型,该模型也是容器的名称。我们还提供了一个本地目录并将其挂载为缓存目录,用于保存已下载的模型资产。
在启动期间,NIM 容器会下载所需资源,并开始为 API 端点背后的模型提供服务。以下消息表明成功启动:
INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) |
运行时,NIM 提供了一个兼容 OpenAI 的 API,您可以查询:
from openai import OpenAI prompt = "Once upon a time" response = client.completions.create( model = "meta/llama-3.1-8b-instruct" , prompt = prompt, max_tokens = 16 , stream = False ) completion = response.choices[ 0 ].text print (completion) |
设置 GenAI-Perf 并对单个用例进行基准测试
现在您已运行 NIM Llama-3 推理服务,接下来请设置基准测试工具。
最简单的方法是使用预构建的 Docker 容器。我们建议在与 NIM 相同的服务器上启动 GenAI-Perf 容器,以避免网络延迟,除非您特别希望在测量时考虑网络延迟。
有关入门的更多信息,请参阅 GenAI-Perf 文档。
运行以下命令以使用预构建 container.
export RELEASE = "24.12" # recommend using latest releases in yy.mm format docker run - it - - net = host - - gpus = all - v $PWD: / workdir nvcr.io / nvidia / tritonserver:${RELEASE} - py3 - sdk |
您应将主机目录安装到容器中,以便持久化 benchmarking 资产。在之前的示例中,我们加载了当前目录。
在容器内,您可以按如下所示启动 GenAI-Perf 评估工具,该工具可在 NIM 后端执行变暖负载测试:
export INPUT_SEQUENCE_LENGTH = 200 export INPUT_SEQUENCE_STD = 10 export OUTPUT_SEQUENCE_LENGTH = 200 export CONCURRENCY = 10 export MODEL = meta / llama - 3.1 - 8b - instruct genai - perf profile \ - m $MODEL \ - - endpoint - type chat \ - - service - kind openai \ - - streaming \ - u localhost: 8000 \ - - synthetic - input - tokens - mean $INPUT_SEQUENCE_LENGTH \ - - synthetic - input - tokens - stddev $INPUT_SEQUENCE_STD \ - - concurrency $CONCURRENCY \ - - output - tokens - mean $OUTPUT_SEQUENCE_LENGTH \ - - extra - inputs max_tokens:$OUTPUT_SEQUENCE_LENGTH \ - - extra - inputs min_tokens:$OUTPUT_SEQUENCE_LENGTH \ - - extra - inputs ignore_eos:true \ - - tokenizer meta - llama / Meta - Llama - 3.1 - 8B - Instruct \ - - \ - v \ - - max - threads = 256 |
在此最小示例中,我们指定了输入和输出序列长度以及要测试的并发性。我们还要求后端忽略特殊的 EOS 令牌,以便输出达到预期长度。
此测试使用 Hugging Face 中的 Llama-3 标记器,这是一个严密保护的存储库。您必须申请访问权限,然后使用 Hugging Face 凭据登录。
pip install huggingface_hub huggingface-cli login |
有关全套选项和参数的更多信息,请参阅 GenAI-Perf 文档。
成功执行后,您应在终端中看到类似于以下内容的结果:

浏览几个使用案例
使用基准测试时,通常会设置测试来扫描或执行多个用例 (输入和输出长度组合) 和负载场景 (不同的并发值) 。使用以下 BASH 脚本定义参数,以便 GenAI-Perf 执行所有组合。
在进行基准测试扫描之前,我们建议运行热身测试。在本例中,我们在设置 GenAI-Perf 和单个用例时做到了这一点。
在 GenAI-Perf 容器中,运行以下命令:
bash benchmark.sh |
这是 benchmark.sh
脚本:
declare - A useCases # Populate the array with use case descriptions and their specified input/output lengths useCases[ "Translation" ] = "200/200" useCases[ "Text classification" ] = "200/5" useCases[ "Text summary" ] = "1000/200" useCases[ "Code generation" ] = "200/1000" # Function to execute genAI-perf with the input/output lengths as arguments runBenchmark() { local description = "$1" local lengths = "${useCases[$description]}" IFS = '/' read - r inputLength outputLength <<< "$lengths" echo "Running genAI-perf for $description with input length $inputLength and output length $outputLength" #Runs for concurrency in 1 2 5 10 50 100 250 ; do local INPUT_SEQUENCE_LENGTH = $inputLength local INPUT_SEQUENCE_STD = 0 local OUTPUT_SEQUENCE_LENGTH = $outputLength local CONCURRENCY = $concurrency local MODEL = meta / llama - 3.1 - 8b - instruct genai - perf profile \ - m $MODEL \ - - endpoint - type chat \ - - service - kind openai \ - - streaming \ - u localhost: 8000 \ - - synthetic - input - tokens - mean $INPUT_SEQUENCE_LENGTH \ - - synthetic - input - tokens - stddev $INPUT_SEQUENCE_STD \ - - concurrency $CONCURRENCY \ - - output - tokens - mean $OUTPUT_SEQUENCE_LENGTH \ - - extra - inputs max_tokens:$OUTPUT_SEQUENCE_LENGTH \ - - extra - inputs min_tokens:$OUTPUT_SEQUENCE_LENGTH \ - - extra - inputs ignore_eos:true \ - - tokenizer meta - llama / Meta - Llama - 3 - 8B - Instruct \ - - measurement - interval 30000 \ - - profile - export - file ${INPUT_SEQUENCE_LENGTH}_${OUTPUT_SEQUENCE_LENGTH}.json \ - - \ - v \ - - max - threads = 256 done } # Iterate over all defined use cases and run the benchmark script for each for description in "${!useCases[@]}" ; do runBenchmark "$description" done |
“--measurement-interval 30000
参数是每次测量所用的时间间隔 (以毫秒为单位) 。GenAI-Perf 用于测量在指定时间间隔内完成的请求。选择一个足够大的值,以便完成多个请求。对于更大的网络 (例如 Llama-3 70B) 和更多并发 (例如 250) ,请选择更高的值。例如,您可以使用 100000 毫秒,即 100 秒。”
分析输出
测试完成后,GenAI-Perf 会在名为 \artifacts
的默认目录中生成结构化输出,并按模型名称、并发性和输入/输出长度进行组织。
artifacts ├── meta_llama-3.1-8b-instruct-openai-chat-concurrency1 │ ├── 1000_200.json │ ├── 1000_200_genai_perf.csv │ ├── 1000_200_genai_perf.json │ ├── 200_1000.json │ ├── 200_1000_genai_perf.csv │ ├── 200_1000_genai_perf.json │ ├── 200_200.json │ ├── 200_200_genai_perf.csv │ ├── 200_200_genai_perf.json │ ├── 200_5.json │ ├── 200_5_genai_perf.csv │ ├── 200_5_genai_perf.json │ └── inputs.json ├── meta_llama-3.1-8b-instruct-openai-chat-concurrency10 │ ├── 1000_200.json │ ├── 1000_200_genai_perf.csv │ ├── 1000_200_genai_perf.json │ ├── 200_1000.json │ ├── 200_1000_genai_perf.csv │ ├── 200_1000_genai_perf.json │ ├── 200_200.json │ ├── 200_200_genai_perf.csv │ ├── 200_200_genai_perf.json │ ├── 200_5.json │ ├── 200_5_genai_perf.csv │ ├── 200_5_genai_perf.json │ └── inputs.json ├── meta_llama-3.1-8b-instruct-openai-chat-concurrency100 │ ├── 1000_200.json … |
*genai_perf.csv
文件是主要的基准测试结果。您可以使用以下代码在给定用例(例如 200/5 的 ISL/OSL)的所有并发中读取所有每秒请求(RPS)和第一个令牌时间(TTFT)指标:
import os import pandas as pd root_dir = "./artifacts" directory_prefix = "meta_llama-3.1-8b-instruct-openai-chat-concurrency" concurrencies = [ 1 , 2 , 5 , 10 , 50 , 100 , 250 ] RPS = [] TTFT = [] for con in concurrencies: df = pd.read_csv(os.path.join(root_dir, directory_prefix + str (con), f "200_5_genai_perf.csv" )) RPS.append( float (df.iloc[ 8 ][ 'avg' ].replace( ',' , ''))) TTFT.append( float (df.iloc[ 0 ][ 'avg' ].replace( ',' , ''))) |
最后,您可以使用以下代码使用收集的数据绘制和分析 latency-throughput 曲线。在这里,每个数据点都对应一个并发值。
import matplotlib.pyplot as plt plt.plot(TTFT, RPS, 'x-' ) plt.xlabel( 'TTFT(ms)' ) plt.ylabel( 'RPS' ) labels = [ 1 , 2 , 5 , 10 , 50 , 100 , 250 ] # Add labels to each data point for i, label in enumerate (labels): plt.annotate(label, (TTFT[i], RPS[i]), textcoords = "offset points" , xytext = ( 0 , 10 ), ha = 'center' ) |
图 2 显示了使用 GenAI-Perf 测量数据生成的图形。

解释结果
图 2 显示了 x 轴上的 TTFT、y 轴上的系统总吞吐量以及每个点上的并发情况。图形的使用方法有以下几种:
- 拥有延迟预算 (例如可接受的最大 TTFT) 的 LLM 应用所有者可以将该值用于 x,并寻找匹配的 y 值和并发值。这表明使用该延迟预算和相应的并发值可以实现的最高吞吐量。
- LLM 应用所有者可以使用并发值来定位图形上的相应点。点的 x 和 y 值显示该并发级别的延迟和吞吐量。
图 2 还显示了延迟快速增长且吞吐量很少或根本没有提升的并发情况。例如,concurrency=50
就是其中一个值。
类似的图形可以使用 ITL、e2e_latency 或 TPS_per_user 作为 x 轴,显示系统总吞吐量与单个用户延迟之间的权衡。
对定制 LLM 进行基准测试
如今,LLMs 可以很好地满足许多常见任务的需求,例如开放式 QA 或会议总结。
但是,定制 LLM 对某些任务大有益,例如在内部企业知识库上对 LLM 进行训练,使其适应内部工作文化、专业知识和协议、特定产品组合和缩略语,甚至使用内部生产力工具。
NIM 支持提供定制模型。NIM 支持低秩自适应 (LoRA) ,这是一种针对特定领域和用例定制 LLM 的简单高效的方法。要使用 LoRA 自定义 LLM,您可以使用 NVIDIA NeMo ,这是一个用于训练生成式 AI 模型的开源平台。 完成后,您可以使用 NIM 加载和部署多个 LoRA 适配器。
首先,将经过 NVIDIA NeMo 训练的 LoRA 适配器放入特定目录结构中,然后按照 Parameter-Efficient Fine-Tuning 中的说明,将适配器文件夹名称作为环境变量传递给 NIM。NIM 还支持使用 HuggingFace PEFT 库 训练的 LoRA 适配器。
添加 LoRA 模型后,可以通过将 base model ID 替换为 LoRA 模型名称来查询与 base model 类似的 LoRA 模型:
curl - X 'POST' \ - H 'accept: application/json' \ - H 'Content-Type: application/json' \ - d '{ "model" : "llama3-8b-instruct-lora_vhf-math-v1" , "prompt" : "John buys 10 packs of magic cards. Each pack has 20 cards and 1/4 of those cards are uncommon. How many uncommon cards did he get?" , "max_tokens" : 128 }' |
借助 GenAI-Perf,可以通过 -m
参数传递 LoRA 模型的 ID 来对 LoRA 模型的部署指标进行基准测试:
genai - perf profile \ - m llama - 3 - 8b - lora_1 llama - 3 - 8b - lora_2 llama - 3 - 8b - lora_3 \ - - model - selection - strategy random \ - - endpoint - type completions \ - - service - kind openai \ - - streaming \ |
在本例中,测试了三个 LoRA 模型:
- llama-3 -8b-lora_1
- llama-3 -8b-lora_2
- llama-3 -8b-lora_3
--model-selection-strategy {round_robin,random}
参数用于指定应循环调用还是随机调用这些适配器。
总结
NVIDIA GenAI-Perf 工具旨在满足对由大规模 LLM 提供支持的基准测试解决方案的迫切需求。它支持 NVIDIA NIM ,以及其他兼容 OpenAI 的 LLM 服务解决方案。
本文为您提供了最重要、最热门的指标和参数,有助于标准化整个行业的模型基准测试方式。
有关如何选择正确的指标和优化 AI 应用性能的更多信息和专家指导,请参阅 LLM Inference Sizing:Benchmarking End-to-End Inference Systems GTC 会议。