Perf Analyzer 벤치마킹
Perf Analyzer 벤치마킹
Perf Analyzer는 Triton의 성능을 측정·벤치마킹하는 핵심 도구예요. 여기서는 HTTP/gRPC 엔드포인트, OpenAI API 호환 서버, Triton C API, Dynamic gRPC, TorchServe 등 다양한 대상을 벤치마킹하는 방법을 정리합니다.
HTTP 또는 gRPC 엔드포인트로 Triton 벤치마킹
이것은 Perf Analyzer의 기본 모드예요.
OpenAI 벤치마킹
GenAI-Perf가 OpenAI API 호환 서버에 배포된 모델의 벤치마킹에 권장되지만, Perf Analyzer를 직접 사용할 수도 있어요. 다만 기능이 더 적습니다.
# get chat template required for facebook/opt-125m model
curl -o template_chatml.jinja https://raw.githubusercontent.com/vllm-project/vllm/refs/heads/main/examples/template_chatml.jinja
# start vllm, an OpenAI API-compatible server
vllm serve facebook/opt-125m --chat-template=template_chatml.jinja > server.log 2>&1 &
# wait for server to be ready
while [ "$(curl -s -o /dev/null -w "%{http_code}" localhost:8000/v1/models)" != "200" ]; do sleep 1; done
# create simple input data JSON for Perf Analyzer
cat > input_data.json <<EOF
{
"data": [
{
"payload": [
{
"model": "facebook/opt-125m",
"messages": [
{"role": "user", "content": "Who wrote the play Romeo and Juliet?"}
],
"max_tokens": 32
}
]
}
]
}
EOF
# run Perf Analyzer
perf_analyzer \
-m facebook/opt-125m \
--input-data=input_data.json \
--service-kind=openai \
--endpoint=v1/chat/completions \
--async
# Successfully read data for 1 stream/streams with 1 step/steps.
# *** Measurement Settings ***
# Service Kind: OPENAI
# Using "time_windows" mode for stabilization
# Stabilizing using average throughput
# Measurement window: 5000 msec
# Using asynchronous calls for inference
# Request concurrency: 1
# Client:
# Request count: 89
# Throughput: 4.94426 infer/sec
# Avg latency: 200467 usec (standard deviation 17124 usec)
# p50 latency: 204443 usec
# p90 latency: 205549 usec
# p95 latency: 205706 usec
# p99 latency: 206259 usec
# Avg HTTP time: 200461 usec (send/recv 169 usec + response wait 200292 usec)
# Inferences/Second vs. Client Average Batch Latency
# Concurrency: 1, throughput: 4.94426 infer/sec, latency 200467 usec
OpenAI 멀티턴 채팅 벤치마킹
OpenAI API 호환 서버에서 멀티턴 채팅 모델을 벤치마킹하는 예시입니다.
[!NOTE] 멀티턴 채팅 벤치마킹은 어떤 통계도 보고하지 않아요. 벤치마크 통계를 계산하는 GenAI-Perf가 사용하는 벤치마크 트레이스(
--profile-export-file)만 출력합니다.
See instructions
# get chat template required for facebook/opt-125m model
curl -o template_chatml.jinja https://raw.githubusercontent.com/vllm-project/vllm/refs/heads/main/examples/template_chatml.jinja
# start vllm, an OpenAI API-compatible server
vllm serve facebook/opt-125m --chat-template=template_chatml.jinja > server.log 2>&1 &
# wait for server to be ready
while [ "$(curl -s -o /dev/null -w "%{http_code}" localhost:8000/v1/models)" != "200" ]; do sleep 1; done
# create simple input data JSON for Perf Analyzer
cat > input_data.json <<EOF
{
"data": [
{
"payload": [
{
"model": "facebook/opt-125m",
"messages": [{ "role": "user",
"content": "Who wrote the play Romeo and Juliet?" }],
"max_tokens": 32
}
],
"session_id": ["16d63027-f8d8-4a2d-83ac-0cde28f5a431"],
"delay": [3000]
},
{
"payload": [
{
"model": "facebook/opt-125m",
"messages": [{ "role": "user",
"content": "What is it about?" }],
"max_tokens": 32
}
],
"session_id": ["16d63027-f8d8-4a2d-83ac-0cde28f5a431"]
},
{
"payload": [
{
"model": "facebook/opt-125m",
"messages": [{ "role": "user",
"content": "What is 2+2?" }],
"max_tokens": 32
}
],
"delay": [2000],
"session_id": ["d1ed35c3-2a3a-444e-8e0a-6f0714202cb1"]
},
{
"payload": [
{
"model": "facebook/opt-125m",
"messages": [{ "role": "user",
"content": "What the square root of that?" }],
"max_tokens": 32
}
],
"session_id": ["d1ed35c3-2a3a-444e-8e0a-6f0714202cb1"]
}
]
}
EOF
# run Perf Analyzer
perf_analyzer \
-m facebook/opt-125m \
--session-concurrency=2 \
--input-data=input_data.json \
--profile-export-file=profile_export.json \
--service-kind=openai \
--endpoint=v1/chat/completions \
--async
jq . profile_export.json | head -n 21
# {
# "experiments": [
# {
# "experiment": {
# "mode": "request_rate",
# "value": 0.0
# },
# "requests": [
# {
# "timestamp": 1741134590669919999,
# "request_inputs": {
# "delay": 2000,
# "session_id": "d1ed35c3-2a3a-444e-8e0a-6f0714202cb1",
# "payload": "{\"model\":\"facebook/opt-125m\",\"messages\":[{\"role\":\"user\",\"content\":\"What is 2+2?\"}],\"max_tokens\":32}"
# },
# "response_timestamps": [
# 1741134590728534586
# ],
# "response_outputs": [
# {
# "response": "{\"id\":\"chatcmpl-dc4644927240492485f8e053c6092a9c\",\"object\":\"chat.completion\",\"created\":1741134590,\"model\":\"facebook/opt-125m\",\"choices\":[{\"index\":0,\"message\":{\"role\":\"assistant\",\"reasoning_content\":null,\"content\":\"What is \\\"user Education\\\"?<|im_end|>\n<n>Assessment\n<n>Assessment\n[edit]\n\\+\\",\"tool_calls\":[]},\"logprobs\":null,\"finish_reason\":\"length\",\"stop_reason\":null}],\"usage\":{\"prompt_tokens\":33,\"total_tokens\":65,\"completion_tokens\":32,\"prompt_tokens_details\":null},\"prompt_logprobs\":null}"
C API로 Triton 직접 벤치마킹
HTTP·gRPC 서버 엔드포인트로 Triton과 통신하는 것 외에도, Perf Analyzer는 C API로 Triton을 직접 벤치마킹할 수 있게 해줘요. HTTP·gRPC 엔드포인트는 애플리케이션 안에서 C API로 Triton을 사용하는 사용자에게는 관심이 없을 수 있는 추가 지연시간을 파이프라인에 도입합니다. 특히 이 기능은 HTTP/gRPC 통신의 추가 오버헤드 없이 최소한의 Triton을 벤치마킹하는 데 유용해요.
전제 조건
대상 머신에서 Triton SDK와 Triton Server 컨테이너 이미지를 가져옵니다. tritonserver 설치에 접근해야 하므로, perf_analyzer 바이너리를 Inference Server 컨테이너로 복사하는 것이 더 쉬울 수 있어요.
필수 파라미터
지원되는 명령줄 인자의 전체 목록은 --help 옵션을 사용하세요. 기본적으로 Perf Analyzer는 Triton 인스턴스가 이미 실행 중일 것을 기대합니다. C API 모드는 --service-kind 옵션으로 구성할 수 있어요. 또한 --triton-server-directory 옵션으로 Perf Analyzer를 Triton 서버 라이브러리 경로로, --model-repository 옵션으로 모델 저장소 경로를 가리켜야 합니다.
예시 실행은 다음과 같아요.
$ perf_analyzer -m my_model --service-kind=triton_c_api --triton-server-directory=/opt/tritonserver --model-repository=/my/model/repository
...
*** Measurement Settings ***
Service Kind: Triton C-API
Using "time_windows" mode for stabilization
Measurement window: 5000 msec
Using synchronous calls for inference
Stabilizing using average latency
Request concurrency: 1
Client:
Request count: 353
Throughput: 19.6095 infer/sec
Avg latency: 50951 usec (standard deviation 2265 usec)
p50 latency: 50833 usec
p90 latency: 50923 usec
p95 latency: 50940 usec
p99 latency: 50985 usec
Server:
Inference count: 353
Execution count: 353
Successful request count: 353
Avg request latency: 50841 usec (overhead 20 usec + queue 63 usec + compute input 35 usec + compute infer 50663 usec + compute output 59 usec)
Inferences/Second vs. Client Average Batch Latency
Concurrency: 1, throughput: 19.6095 infer/sec, latency 50951 usec
지원되지 않는 기능
C API 모드에서 빠진 기능이 몇 가지 있습니다.
- 비동기 모드(
--async)
Dynamic gRPC로 gRPC 서비스 벤치마킹
참고
Dynamic gRPC 서비스 종류는 현재 비동기 gRPC API를 지원하지 않아요.
mock gRPC 서비스 설정
Dynamic gRPC 서비스 종류로 Perf Analyzer를 실행하는 방법을 자세히 보기 전에, Dynamic gRPC 서비스 종류를 테스트하는 데 쓸 mock gRPC 서비스를 설정해 봅시다. 별도의 터미널 세션에서 다음 단계를 따르세요.
# Define simple gRPC service
cat <<EOF > simple.proto
syntax = "proto3";
package v1;
service Simple {
// Simple bidirectional streaming RPC that echoes request message
rpc Echo(stream Request) returns (stream Response) {}
}
message Request {
string message = 1;
}
message Response {
string message = 1;
}
EOF
# compile protobuf and generate gRPC stubs
pip install grpcio-tools
python -m grpc_tools.protoc --proto_path=. \
--python_out=. --grpc_python_out=. \
simple.proto
# run gRPC server
cat <<EOF > simple_server.py
from concurrent import futures
import grpc
import simple_pb2
import simple_pb2_grpc
class SimpleServicer(simple_pb2_grpc.SimpleServicer):
def Echo(self, request_iterator, context):
for request in request_iterator:
print(f"Received: {request.message}")
yield simple_pb2.Response(message=request.message)
if __name__ == "__main__":
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
simple_pb2_grpc.add_SimpleServicer_to_server(SimpleServicer(), server)
server.add_insecure_port(f"[::]:8001")
server.start()
print(f"Started gRPC service at 127.0.0.1:8001")
server.wait_for_termination()
EOF
python simple_server.py
# Output: Started gRPC service at 127.0.0.1:8001
이제 Dynamic gRPC 서비스 종류로 Perf Analyzer를 실행하는 방법으로 돌아와서, 사용자는 Perf Analyzer에 두 가지를 제공해야 합니다.
- 메시지 프레이밍 프로토콜을 따르는 직렬화된 Protobuf 메시지를 생성하는 스크립트.
- 스크립트를 어떻게 실행할지 지정하는 입력 JSON 파일.
메시지 프레이밍 프로토콜
직렬화된 Protobuf 메시지를 생성하는 스크립트를 작성할 때, 스크립트는 Perf Analyzer가 기대하는 다음 프로토콜로 파일 스트림에 바이트를 반드시 써야 합니다.
- 메시지 길이: 다음 메시지의 길이를 나타내는 4바이트 정수(시스템 바이트 순서 사용).
- 메시지 내용: 직렬화된 Protobuf 메시지 자체.
아래 Python 예시(example.py)는 메시지 프레이밍 프로토콜을 따르는 Protobuf 메시지 시퀀스를 생성합니다.
import sys
import simple_pb2
# Generate and yield your protobuf messages here.
def generate_msgs():
for i in range(10):
yield simple_pb2.Request(message=f"Message-{i}")
for msg in generate_msgs():
serialized = msg.SerializeToString()
# Write the message length as 4-byte integer (using system byte order)
sys.stdout.buffer.write(len(serialized).to_bytes(4, byteorder=sys.byteorder))
# Write the serialized message itself
sys.stdout.buffer.write(serialized)
sys.stdout.buffer.flush()
스크립트를 실행하기 위해 필요한 의존성(예: protobuf 패키지)을 설치했는지 확인하세요.
입력 JSON 파일
다음으로, Perf Analyzer에 스크립트 실행을 지시하는 입력 JSON 파일(예: inputs.json)을 만듭니다.
{
"data": [
{
"message_generator": "python3 example.py"
}
]
}
message_generator 필드는 Perf Analyzer가 읽을 직렬화된 Protobuf 메시지를 생성하기 위해 실행할 명령을 담고 있습니다(Dynamic gRPC 입력 JSON에 대한 자세한 내용은 Dynamic gRPC Input JSON 참고). 생성된 메시지는 --grpc-method 인자로 지정된 gRPC 서비스에 추론 요청을 보내는 데 사용됩니다.
생성기 스크립트와 JSON 구성이 모두 갖춰졌으면, Dynamic gRPC 서비스 종류로 Perf Analyzer를 실행하세요. <URL>, <package>, <service>, <method>를 gRPC 서비스에 맞는 값으로 바꿉니다.
perf_analyzer --service-kind=dynamic_grpc -u=localhost:8001 --input-data=inputs.json --grpc-method=v1.Simple/Echo
# Successfully read data for 1 stream/streams with 1 step/steps.
# *** Measurement Settings ***
# Service Kind: DYNAMIC_GRPC
# Using "time_windows" mode for stabilization
# Stabilizing using average latency and throughput
# Measurement window: 5000 msec
# Using synchronous calls for inference
#
# Request concurrency: 1
# Client:
# Request count: 7390
# Throughput: 407.866 infer/sec
# Avg latency: 2417 usec (standard deviation 559 usec)
# p50 latency: 2251 usec
# p90 latency: 3283 usec
# p95 latency: 3893 usec
# p99 latency: 4172 usec
# Avg gRPC time: 2133 usec ((un)marshal request/response 2132 usec + response wait 1 usec)
# Inferences/Second vs. Client Average Batch Latency
# Concurrency: 1, throughput: 407.866 infer/sec, latency 2417 usec
TorchServe 벤치마킹
Perf Analyzer는 --service-kind=torchserve 옵션으로 TorchServe를 벤치마킹하는 데도 쓸 수 있어요. HTTP 프로토콜만 지원되며, 입력은 JSON 파일로 제공해야 합니다.
다음 호출은 kitten_small.jpg를 담고 있다고 가정하는 위치에서 실행 중인 torchserve 인스턴스에 요청을 보내도록 Perf Analyzer를 구성하는 방법을 보여줘요.
$ perf_analyzer -m resnet50 --service-kind torchserve -i http -u localhost:8080 -b 1 -p 5000 --input-data data.json
Successfully read data for 1 stream/streams with 1 step/steps.
*** Measurement Settings ***
Batch size: 1
Using "time_windows" mode for stabilization
Measurement window: 5000 msec
Using synchronous calls for inference
Stabilizing using average latency
Request concurrency: 1
Client:
Request count: 799
Throughput: 159.8 infer/sec
Avg latency: 6259 usec (standard deviation 397 usec)
p50 latency: 6305 usec
p90 latency: 6448 usec
p95 latency: 6494 usec
p99 latency: 7158 usec
Avg HTTP time: 6272 usec (send/recv 77 usec + response wait 6195 usec)
Inferences/Second vs. Client Average Batch Latency
Concurrency: 1, throughput: 159.8 infer/sec, latency 6259 usec
data.json의 내용:
{
"data" :
[
{
"TORCHSERVE_INPUT" : ["kitten_small.jpg"]
}
]
}
서버가 실행 중인 위치에 접근하려면 다른 url(-u)을 지정해야 할 수 있어요. Perf Analyzer의 보고서에는 클라이언트 측에서 측정한 통계만 포함됩니다.
참고: 이 지원은 여전히 베타입니다. Perf Analyzer는 TorchServe에 대한 최적 튜닝을 보장하지 않아요. 하지만 추론 서버를 동일한 방식으로 부하를 걸 수 있는 단일 벤치마킹 도구는 성능 분석에 중요합니다.
제3자 벤치마크 스위트 대비 Perf Analyzer의 장점
Triton Inference Server는 Triton에 최적화된 클라이언트 라이브러리를 포함한 전체 서빙 솔루션을 제공해요. jmeter 같은 제3자 벤치마크 스위트는 최적화된 라이브러리를 활용하지 못합니다. 이러한 최적화에는 다음이 포함되지만 여기서 끝나는 것은 아닙니다.
- HTTP 요청에서 이진 텐서 데이터 확장 사용.
- 후속 요청에서 gRPC 메시지 할당의 효과적 재사용.
- libcurl 인터페이스를 통한 추가 메모리 복사 회피.
이 최적화들은 전체 성능에 지대한 영향을 줄 수 있어요. 벤치마킹에 Perf Analyzer를 직접 쓰면 사용자는 연구에서 이 최적화들에 접근할 수 있습니다.
뿐만 아니라 Perf Analyzer는 매우 커스터마이즈 가능하고 이 문서에 기술된 많은 Triton 기능을 지원해요. 이는 상세 보고서와 함께, 사용자가 성능 병목을 식별하고 무엇이 자신에게 가장 잘 맞는지 결정하기 전에 다양한 기능을 실험할 수 있게 해줍니다.