26S29b

QK·4일 전

네. 앞에서 정리한 3개 핵심 Case 기준으로 바로 실행할 수 있는 vllm bench serve 명령어를 정리하면 아래와 같습니다.

1. Pure Prefix Caching 검증 — 가장 중요한 APC 테스트

목적은 동일한 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 자체가 제대로 동작하는지 확인하려면 이 테스트가 가장 중요합니다.


2. 실제 Agent/RAG 형태 — 현재 사용 중인 Case

현재 사용하신 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 같은 형태를 보는 데 적합합니다.


3. Prefix가 전혀 없는 경우 — APC overhead 측정

이것도 반드시 한 번 해보는 것을 권합니다.

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

두 번 실행합니다.

즉:

TestPrefixSuffixAPC목적
A032KOFFbaseline
B032KONAPC overhead
C32K1KOFFpure APC baseline
D32K1KONpure APC 효과
E32K32KOFFAgent/RAG baseline
F32K32KONAgent/RAG APC 효과

이 6개를 돌리면 APC 문제를 상당히 명확하게 분리할 수 있습니다.


4. 제가 추천하는 실제 실행 순서

B300 8 GPU + Llama 3.1 70B라면 다음 순서가 좋습니다.

Case A/B — APC overhead

# 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를 의심할 수 있습니다.


Case C/D — Pure APC

# 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를 생성했는지

Case E/F — 실제 Agent/RAG

현재 사용하신 조건 그대로:

--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를 적용했을 때의 효과를 보는 테스트입니다.


5. 한 가지 더 중요한 테스트: concurrency를 바꾸기

현재 --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 각각 별도 서버 재기동해서 실행해야 합니다.


6. 전체적으로는 이 구조가 가장 깔끔합니다

                  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)

그런데 현재 스크립트에는 결과를 잘못 해석하게 만들 수 있는 부분이 몇 군데 있습니다.

1. 먼저 결론

현재 테스트:

--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에는 상당히 좋은 테스트입니다.


2. 그런데 현재 판정 기준은 수정해야 합니다

현재:

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의 구조와 맞지 않을 수 있습니다.


3. 더 중요한 문제: 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)


4. 사실 현재 workload에서는 "cache hit rate"보다 "cached token 수"가 더 중요합니다

예를 들어 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

입니다.


5. 그리고 이 스크립트에는 더 큰 문제가 하나 있습니다

이 부분:

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에서는 이 부분을 제거하는 것을 강하게 권합니다.


6. 또 하나: 서버 readiness가 너무 약합니다

현재:

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를 시작하도록 하는 것이 더 좋습니다.


7. 현재 A/B에서 오히려 아주 좋은 점

이 부분은 잘 하셨습니다.

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 구조는 괜찮습니다.


8. 그런데 저는 이 테스트를 2단계로 분리하겠습니다

Test A — 순수 APC 검증

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 효과가 훨씬 명확합니다.


9. Test B — 실제 Agent/RAG workload

그 다음 지금 사용하신 것을 그대로 유지합니다.

--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

같은 구조가 될 수 있기 때문입니다.


10. 두 테스트의 의미가 완전히 다릅니다

테스트목적기대
prefix_repetitionAPC 자체 검증큰 TTFT 감소
random + 32K prefix실제 RAG/Agent workload중간 정도 TTFT 감소
prefix=0, APC ON/OFFAPC overhead 측정ON이 같거나 약간 느릴 수 있음

이렇게 분리하면 아주 좋습니다.


11. 특히 세 번째 테스트가 중요합니다

이것도 한번 해보세요.

--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

를 별도로 분리할 수 있습니다.


12. 제가 추천하는 최종 실험 matrix

B300 8장 / Llama 3.1 70B / TP=8에서는 우선 이것만 해도 상당히 많은 것을 알 수 있습니다.

CasePrefixSuffixAPC
A032KOFF
B032KON
C32K1KOFF
D32K1KON
E32K32KOFF
F32K32KON

그리고 concurrency:

1
2
4
8

입니다.

특히:

C vs D

가 순수 Prefix Cache 효과

E vs F

가 실제 RAG/Agent workload에서의 효과

A vs B

가 APC 자체 overhead

입니다.


13. 그리고 한 가지 더: hash algorithm도 나중에 비교할 가치가 있습니다

현재 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이 얼마나 줄었는가?

를 확인해야 합니다.


14. 현재 스크립트에서 제가 수정할 핵심 부분

현재:

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가 실패하면 전체 테스트를 실패시키는 게 맞습니다.


15. 결과 판정도 이렇게 바꾸는 것이 좋습니다

현재:

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 성능을 판정하지 않는 것을 추천합니다.


16. 그리고 현재 상황에서 제가 가장 보고 싶은 것

현재 실제로:

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 스크립트 자체를 수정해드릴 수 있습니다.

profile
engineer

0개의 댓글