네. 앞에서 정리한 3개 핵심 Case 기준으로 바로 실행할 수 있는 vllm bench serve 명령어를 정리하면 아래와 같습니다.
목적은 동일한 32K prefix가 실제로 반복되어 캐시 hit 되는지 확인하는 것입니다.
vllm bench serve \
--backend openai-chat \
--endpoint /v1/chat/completions \
--host 127.0.0.1 \
--port 8000 \
--model llama-3.1-70b \
--tokenizer "${MODEL_70B}" \
--dataset-name prefix_repetition \
--prefix-repetition-prefix-len 32768 \
--prefix-repetition-suffix-len 1024 \
--prefix-repetition-num-prefixes 1 \
--prefix-repetition-output-len 256 \
--num-prompts 30 \
--max-concurrency 8 \
--request-rate inf
Request 1: [32K 동일 prefix] + [1K suffix]
Request 2: [32K 동일 prefix] + [1K suffix]
Request 3: [32K 동일 prefix] + [1K suffix]
...
Request 30
즉:
APC OFF APC ON
│ │
32K prefix 계산 첫 요청 계산
│ │
1K suffix 계산 계산
│ │
다음 요청 32K 재계산 32K cache hit
APC 자체가 제대로 동작하는지 확인하려면 이 테스트가 가장 중요합니다.
현재 사용하신 command입니다.
vllm bench serve \
--backend openai-chat \
--endpoint /v1/chat/completions \
--host 127.0.0.1 \
--port 8000 \
--model llama-3.1-70b \
--tokenizer "${MODEL_70B}" \
--dataset-name random \
--random-input-len 32768 \
--random-output-len 256 \
--random-prefix-len 32000 \
--num-prompts 30 \
--max-concurrency 8 \
--request-rate inf
여기서 중요한 것은:
random-prefix-len = 32000
random-input-len = 32768
이므로 전체 input은 약 64K입니다.
┌─────────────── 32K ───────────────┐┌────────────── 32K ──────────────┐
│ Fixed Prefix ││ Random Suffix │
│ 동일 ││ 요청마다 다름 │
└────────────────────────────────────┘└─────────────────────────────────┘
│
└── APC 대상
따라서 APC ON에서 최대 약 32K prefill을 절약할 수 있고, 나머지 32K는 여전히 계산해야 합니다.
이 테스트는 실제 RAG/Agent에서 공통 system prompt + 공통 문서 + request-specific query 같은 형태를 보는 데 적합합니다.
이것도 반드시 한 번 해보는 것을 권합니다.
vllm bench serve \
--backend openai-chat \
--endpoint /v1/chat/completions \
--host 127.0.0.1 \
--port 8000 \
--model llama-3.1-70b \
--tokenizer "${MODEL_70B}" \
--dataset-name random \
--random-input-len 32768 \
--random-output-len 256 \
--random-prefix-len 0 \
--num-prompts 30 \
--max-concurrency 8 \
--request-rate inf
이것을
APC OFF
APC ON
두 번 실행합니다.
즉:
| Test | Prefix | Suffix | APC | 목적 |
|---|---|---|---|---|
| A | 0 | 32K | OFF | baseline |
| B | 0 | 32K | ON | APC overhead |
| C | 32K | 1K | OFF | pure APC baseline |
| D | 32K | 1K | ON | pure APC 효과 |
| E | 32K | 32K | OFF | Agent/RAG baseline |
| F | 32K | 32K | ON | Agent/RAG APC 효과 |
이 6개를 돌리면 APC 문제를 상당히 명확하게 분리할 수 있습니다.
B300 8 GPU + Llama 3.1 70B라면 다음 순서가 좋습니다.
# A
--random-prefix-len 0
--no-enable-prefix-caching
# B
--random-prefix-len 0
--enable-prefix-caching
여기서는 A와 B가 거의 비슷해야 정상입니다.
B가 오히려 크게 느려진다면 APC lookup/hash/cache 관리 overhead를 의심할 수 있습니다.
# C
--dataset-name prefix_repetition
--prefix-repetition-prefix-len 32768
--prefix-repetition-suffix-len 1024
--prefix-repetition-num-prefixes 1
--prefix-repetition-output-len 256
--no-enable-prefix-caching
# D
동일
--enable-prefix-caching
이게 가장 중요한 검증 케이스입니다.
여기서 ON/OFF 차이가 거의 없다면 먼저 다음을 확인해야 합니다.
1. 실제 prefix cache hit 여부
2. cached tokens
3. prefix cache block 수
4. KV cache capacity
5. prefix_match_unit
6. benchmark가 실제 동일 prefix를 생성했는지
현재 사용하신 조건 그대로:
--dataset-name random \
--random-input-len 32768 \
--random-prefix-len 32000 \
--random-output-len 256 \
--num-prompts 30 \
--max-concurrency 8 \
--request-rate inf
OFF:
--no-enable-prefix-caching
ON:
--enable-prefix-caching
이것이 실제 업무 workload에 APC를 적용했을 때의 효과를 보는 테스트입니다.
현재 --max-concurrency 8만 보면 APC 효과가 scheduler에 묻힐 수 있습니다.
그래서 Case C/D와 E/F에 대해서는 가능하면:
concurrency = 1
concurrency = 2
concurrency = 4
concurrency = 8
으로 돌리는 것을 추천합니다.
예를 들어 Pure APC의 핵심 테스트:
for C in 1 2 4 8; do
vllm bench serve \
--backend openai-chat \
--endpoint /v1/chat/completions \
--host 127.0.0.1 \
--port 8000 \
--model llama-3.1-70b \
--tokenizer "${MODEL_70B}" \
--dataset-name prefix_repetition \
--prefix-repetition-prefix-len 32768 \
--prefix-repetition-suffix-len 1024 \
--prefix-repetition-num-prefixes 1 \
--prefix-repetition-output-len 256 \
--num-prompts 30 \
--max-concurrency ${C} \
--request-rate inf
done
다만 ON/OFF 각각 별도 서버 재기동해서 실행해야 합니다.
Llama 3.1 70B / TP=8
│
┌────────────┴────────────┐
│ │
APC OFF APC ON
│ │
┌───────┼────────┐ ┌───────┼────────┐
│ │ │ │ │ │
0K 32K 32K 0K 32K 32K
+32K +1K +32K +32K +1K +32K
│ │ │ │ │ │
A C E B D F
│ │ │ │ │ │
└───────┴────────┴────────┴───────┴────────┘
│
APC 효과 분리 분석
특히 C/D가 APC 기능 자체를 검증하는 기준선, E/F가 실제 Agent/RAG 적용성 검증이라고 생각하시면 됩니다.
그리고 현재 스크립트의 5~10x면 정상이라는 판정 기준은 E/F에는 적용하지 않는 것이 좋습니다. 64K 중 32K만 캐싱되는 구조이기 때문입니다. C/D에서 먼저 실제 cache hit을 확인한 다음 E/F의 개선폭을 해석하는 것이 맞습니다.
==
===
코드를 보니 앞서 제가 의심했던 것 중 하나는 제외해도 됩니다. 이 스크립트는 prefix_cache_on과 prefix_cache_off를 각각 별도 서버로 기동하고 동일한 benchmark를 실행하므로, 단순히 "ON 테스트가 먼저/나중이라 기존 cache가 남았다"는 문제는 없습니다.
다만 코드를 자세히 보면 현재 A/B 테스트의 핵심 전제인 "동일한 32K 고정 prefix"는 맞습니다. vLLM의 현재 random dataset 구현도 random-prefix-len을 고정 prefix로 사용하고, 나머지 context를 생성합니다. (vLLM)
그런데 현재 스크립트에는 결과를 잘못 해석하게 만들 수 있는 부분이 몇 군데 있습니다.
현재 테스트:
--random-input-len 32768
--random-prefix-len 32000
--random-output-len 256
--num-prompts 30
--max-concurrency 8
은 실제로:
32,000 fixed prefix
+
32,768 random suffix
+
256 output
입니다.
즉 약:
64,768 input tokens/request
입니다. vLLM 현재 문서도 random-prefix-len은 random context 앞에 고정 prefix를 붙이며 total input = prefix + random context라고 명시합니다. (vLLM)
따라서 "32K prefix caching benchmark"라기보다는 "64K context 중 앞 32K를 공유하는 workload"입니다.
이 자체는 잘못된 게 아닙니다. 오히려 실제 Agent/RAG workload에는 상당히 좋은 테스트입니다.
현재:
speedup = ttft_off / ttft_on
그리고:
5~10배 이상 단축되면 정상
2배 미만이면 cache 문제
라고 되어 있는데, 이것을 Prefix Cache의 정상/비정상 판정 기준으로 쓰면 안 됩니다.
왜냐하면 현재 workload에서는 32K만 cache되고 나머지 32K는 매번 새로 prefill되기 때문입니다.
개념적으로:
APC OFF
64K prefill
████████████████████████████████████████
APC ON
32K cache hit
████████████████
32K prefill
████████████████
입니다.
따라서 이론적으로 APC ON이 얻을 수 있는 효과는 64K 전체 prefill을 32K로 줄이는 것이지, 64K prefill을 0으로 만드는 것이 아닙니다.
따라서 5~10× TTFT 개선을 기대하는 것은 이 workload의 구조와 맞지 않을 수 있습니다.
median_ttft_ms만 보면 안 됩니다현재 결과 비교는:
ttft_on = on.get("median_ttft_ms", 0)
ttft_off = off.get("median_ttft_ms", 0)
뿐입니다.
이것으로는:
Prefix caching이 실제로 hit되었는가?
를 알 수 없습니다.
RCA처럼 생각하면:
TTFT 개선
≠
Cache hit 증명
입니다.
반드시:
cache queries
cache hits
cached tokens
같은 정보를 확인해야 합니다.
vLLM은 prefix cache 관련 metric을 제공하고, 현재 버전에서는 prefix-cache hash algorithm도 설정할 수 있습니다. 기본 hash algorithm은 sha256입니다. (vLLM)
예를 들어 30 request라면 이상적인 상황은:
첫 번째 request:
32K prefix → MISS
나머지 request:
32K prefix → HIT
입니다.
그러면 대략:
30 × 32K = 960K prefix tokens
중 대부분이 cache hit 대상이 됩니다.
하지만 실제로:
Request 1 → 32K HIT
Request 2 → 32K MISS
Request 3 → 32K MISS
...
라면 APC 효과가 거의 없습니다.
따라서 제가 가장 먼저 보고 싶은 것은:
Prefix cache queries
Prefix cache hits
Number of cached blocks
Cached tokens
입니다.
이 부분:
vllm bench serve ... || true
입니다.
즉 benchmark가 실패해도:
|| true
때문에 workflow가 계속 진행됩니다.
그리고 Python 부분에서는:
if not os.path.exists(fp): return {}
이므로 benchmark 결과가 없으면:
ttft_on = 0
ttft_off = 0
이 될 수 있습니다.
그러면:
단축 배율: 0.0x
같은 결과가 나와도 실제로는 benchmark 실패일 수 있습니다.
A/B validation script에서는 이 부분을 제거하는 것을 강하게 권합니다.
현재:
until curl -s "http://${SERVER_HOST}:${VLLM_PORT}/v1/models" ...
이면 HTTP server가 올라온 것만 확인합니다.
즉:
API server ready
이지:
model fully loaded
KV cache initialized
scheduler ready
까지 benchmark 시작 조건으로 명확히 보장하는 것은 아닙니다.
다만 /v1/models가 실제 모델 서비스가 가능한 상태에서 응답한다면 실무적으로는 상당 부분 괜찮습니다.
그래도 B300 성능 검증이라면 server startup log에서 GPU KV cache size, GPU blocks 등이 출력된 이후 benchmark를 시작하도록 하는 것이 더 좋습니다.
이 부분은 잘 하셨습니다.
run_case "prefix_cache_on" "--enable-prefix-caching"
run_case "prefix_cache_off" "--no-enable-prefix-caching"
각각:
server start
↓
benchmark
↓
server terminate
↓
3 sec
↓
next server
이기 때문에 APC ON 서버의 cache가 APC OFF 테스트에 영향을 주는 문제는 없습니다.
그리고 두 테스트에서 benchmark 명령이 완전히 동일합니다.
따라서 기본적인 A/B 구조는 괜찮습니다.
vLLM이 공식적으로 제공하는 prefix_repetition dataset을 사용합니다.
현재 vLLM 문서에도 prefix caching benchmark 용도로 다음 형태를 제공합니다. (vLLM)
예:
vllm bench serve \
--backend openai-chat \
--endpoint /v1/chat/completions \
--host "${SERVER_HOST}" \
--port "${VLLM_PORT}" \
--model "llama-3.1-70b" \
--tokenizer "${MODEL_70B}" \
--dataset-name prefix_repetition \
--prefix-repetition-prefix-len 32000 \
--prefix-repetition-suffix-len 1024 \
--prefix-repetition-num-prefixes 1 \
--prefix-repetition-output-len 256 \
--num-prompts 30 \
--max-concurrency 8 \
--request-rate inf
여기서는:
32K shared
+
1K unique
이므로 APC 효과가 훨씬 명확합니다.
그 다음 지금 사용하신 것을 그대로 유지합니다.
--random-input-len 32768
--random-prefix-len 32000
--random-output-len 256
이것은 제가 오히려 실제 AI Agent benchmark로는 더 가치 있다고 봅니다.
왜냐하면 실제 RCA Agent라면:
32K
System prompt
+ tool definitions
+ agent instructions
+ RAG knowledge
↓
Shared prefix
32K
Incident evidence
+ retrieved docs
+ user query
↓
Unique suffix
같은 구조가 될 수 있기 때문입니다.
| 테스트 | 목적 | 기대 |
|---|---|---|
prefix_repetition | APC 자체 검증 | 큰 TTFT 감소 |
random + 32K prefix | 실제 RAG/Agent workload | 중간 정도 TTFT 감소 |
| prefix=0, APC ON/OFF | APC overhead 측정 | ON이 같거나 약간 느릴 수 있음 |
이렇게 분리하면 아주 좋습니다.
이것도 한번 해보세요.
--random-input-len 32768
--random-prefix-len 0
APC ON/OFF 비교입니다.
즉:
APC OFF
64K? → 32K input
APC ON
32K input
인데 shared prefix가 없습니다.
이때:
APC ON > APC OFF
가 나온다면 APC 자체의 overhead를 측정할 수 있습니다.
즉:
APC overhead
를 별도로 분리할 수 있습니다.
B300 8장 / Llama 3.1 70B / TP=8에서는 우선 이것만 해도 상당히 많은 것을 알 수 있습니다.
| Case | Prefix | Suffix | APC |
|---|---|---|---|
| A | 0 | 32K | OFF |
| B | 0 | 32K | ON |
| C | 32K | 1K | OFF |
| D | 32K | 1K | ON |
| E | 32K | 32K | OFF |
| F | 32K | 32K | ON |
그리고 concurrency:
1
2
4
8
입니다.
특히:
C vs D
가 순수 Prefix Cache 효과
E vs F
가 실제 RAG/Agent workload에서의 효과
A vs B
가 APC 자체 overhead
입니다.
현재 vLLM 기본값은:
sha256
입니다. 현재 vLLM은:
sha256
sha256_cbor
xxhash
xxhash_cbor
를 지원합니다. (vLLM)
따라서 APC가 실제로 hit되고 있는데도 overhead가 크다면:
--prefix-caching-hash-algo xxhash
같은 비교도 해볼 수 있습니다.
다만 지금 당장 이것부터 바꾸지는 마세요.
먼저:
1. Cache hit이 실제 발생하는가?
2. 얼마만큼의 token이 cache hit되는가?
3. APC OFF 대비 실제 prefill time이 얼마나 줄었는가?
를 확인해야 합니다.
현재:
vllm bench serve \
...
--dataset-name random \
--random-input-len 32768 \
--random-output-len 256 \
--random-prefix-len 32000 \
...
|| true
를 최소한:
vllm bench serve \
--backend openai-chat \
--endpoint /v1/chat/completions \
--host "${SERVER_HOST}" \
--port "${VLLM_PORT}" \
--model "llama-3.1-70b" \
--tokenizer "${MODEL_70B}" \
--dataset-name random \
--random-input-len 32768 \
--random-output-len 256 \
--random-prefix-len 32000 \
--num-prompts 30 \
--max-concurrency 8 \
--request-rate inf \
--save-result \
--result-dir "${LOG_DIR}" \
--result-filename "${tag}.json"
처럼 바꾸겠습니다.
그리고 benchmark가 실패하면 전체 테스트를 실패시키는 게 맞습니다.
현재:
5~10배 = 정상
2배 미만 = cache 문제
보다는:
┌─────────────────────────────────────────────┐
│ Prefix Cache Validation │
├─────────────────────────────────────────────┤
│ Cache hit observed? │
│ Cached tokens │
│ Cache hit ratio │
│ Median TTFT OFF │
│ Median TTFT ON │
│ TTFT improvement │
│ Input token throughput │
│ Output token throughput │
│ E2E latency │
└─────────────────────────────────────────────┘
로 보는 게 훨씬 정확합니다.
특히 TTFT만으로 APC 성능을 판정하지 않는 것을 추천합니다.
현재 실제로:
APC ON이 APC OFF보다 느렸다
는 결과가 나온 상태라면, 다음 실행을 하기 전에 server_prefix_cache_on.log와 prefix_cache_on.json, prefix_cache_off.json의 실제 내용을 보면 원인을 훨씬 빨리 좁힐 수 있습니다.
특히 server log에서:
GPU KV cache size
GPU blocks
KV cache
prefix cache
관련 부분과 benchmark 결과의:
median_ttft_ms
mean_ttft_ms
median_tpot_ms
mean_tpot_ms
request_throughput
input_throughput
output_throughput
를 같이 보면 됩니다.
가능하면 이 스크립트가 생성한 server_prefix_cache_on.log, server_prefix_cache_off.log, prefix_cache_on.json, prefix_cache_off.json 4개를 올려주세요. 그러면 지금 결과를 추측하지 않고 실제 B300 8장 실행 결과를 기준으로 APC가 왜 느렸는지 계산해서, 필요하면 이 Stage3a-8 스크립트 자체를 수정해드릴 수 있습니다.