이해를 위한 극단적 예시 (블록사이즈 상관안함)
입력 데이터:
Batch size = 3
[
"SELECT * FROM users" (토큰 길이: 4)
"SELECT name, age FROM customers" (토큰 길이: 7)
"SELECT DISTINCT id FROM orders" (토큰 길이: 10)
]
메모리 할당:
[
[SELECT, *, FROM, users, PAD, PAD, PAD, PAD, PAD, PAD]
[SELECT, name, age, FROM, customers, PAD, PAD, PAD, PAD, PAD]
[SELECT, DISTINCT, id, FROM, orders, PAD, PAD, PAD, PAD, PAD]
]
필요 메모리: 3 × 10 = 30 토큰 공간
실제 데이터: 4 + 7 + 10 = 21 토큰
낭비되는 메모리: 9 토큰 공간
입력 데이터:
max_num_seqs = 3
[
시퀀스 1: "SELECT * FROM users" (토큰 길이: 4)
시퀀스 2: "SELECT name, age FROM customers" (토큰 길이: 7)
시퀀스 3: "SELECT DISTINCT id FROM orders" (토큰 길이: 10)
]
메모리 할당:
시퀀스 1: [SELECT, *, FROM, users]
KV 캐시 1: (4 토큰에 대한 캐시)
시퀀스 2: [SELECT, name, age, FROM, customers]
KV 캐시 2: (7 토큰에 대한 캐시)
시퀀스 3: [SELECT, DISTINCT, id, FROM, orders]
KV 캐시 3: (10 토큰에 대한 캐시)
필요 메모리: 실제 토큰 수만큼만 할당 (4 + 7 + 10 = 21 토큰)
메모리 낭비: 없음
일반 배치:
- 모든 입력을 가장 긴 시퀀스(10)에 맞춰 패딩
- 병렬 처리시 모든 입력이 동시에 처리
- 패딩된 부분도 연산에 포함 (비효율)
VLLM:
시퀀스 1: [실제 4토큰만 처리]
↓ KV 캐시 저장
시퀀스 2: [실제 7토큰만 처리]
↓ KV 캐시 저장
시퀀스 3: [실제 10토큰만 처리]
↓ KV 캐시 저장
배치 처리:
[토큰1] [토큰2] [토큰3] [PAD] [PAD] ... [PAD]
[토큰1] [토큰2] [토큰3] [토큰4] [PAD] ... [PAD]
[토큰1] [토큰2] [토큰3] [토큰4] [토큰5] ... [토큰10]
VLLM:
시퀀스 1: [토큰1] [토큰2] [토큰3] [토큰4]
시퀀스 2: [토큰1] [토큰2] [토큰3] [토큰4] [토큰5] [토큰6] [토큰7]
시퀀스 3: [토큰1] [토큰2] [토큰3] [토큰4] [토큰5] [토큰6] [토큰7] [토큰8] [토큰9] [토큰10]
이처럼 VLLM의 시퀀스 처리는 각 입력의 실제 길이에 맞춰 동적으로 메모리를 할당하고 관리하며, KV 캐시를 효율적으로 활용하여 성능을 최적화합니다.