블로그로 돌아가기

문제 해결가이드

llama.cpp 프롬프트 처리가 35배 느리다고? q5_1 KV 캐시를 고치세요

flash attention과 q5_1 KV 캐시를 함께 쓰면 attention이 CPU로 밀릴 수 있습니다. 확인하는 방법, q8_0로 고치는 방법, API가 더 간단한 경우를 정리합니다.

작성자: CheapTokens Team 게시됨읽는 시간 6분

llama.cpp 프롬프트 처리 중 GPU가 0퍼센트, CPU가 930퍼센트인 터미널 화면
이 페이지의 내용
  1. 화면에서 보이는 모습
  2. 더 작은 캐시가 이렇게까지 느려지는 이유
  3. 2분 만에 확인하기
  4. 해결 1: q8_0 사용(또는 재빌드)
  5. 튜닝이 더 이상 가치 없어질 때
  6. 작업 순서

로컬 모델이 답변은 잘 하지만, 이제 긴 프롬프트마다 GPU가 아무 일도 하지 않는 채로 1분 30초씩 멈춰 있습니다. -fa on와 q5_1 KV 캐시로 llama.cpp를 실행 중이라면 그게 원인일 가능성이 큽니다. 기본 CUDA 빌드에는 해당 캐시 타입용 flash-attention 커널이 없어서 attention 단계가 조용히 CPU에서 돌아갑니다. 캐시를 q8_0로 바꾸자 한 사용자의 프롬프트 처리 속도가 38에서 1348 tokens/s로 올랐습니다.

아래 수치는 llama.cpp 이슈 #28455(2026년 9월 5일 개설, 빌드 10791, 2026년 10월 28일 확인)에서 가져왔습니다. 사용자 한 명, RTX 4080 한 장, 27B 모델 하나이므로 법칙이 아니라 사례 연구로 읽어 주세요. 근본 원인 분석도 그들의 것입니다. 한 메인테이너는 이를 "알려진 이슈"라고 부르고 다음 날 이슈를 닫았습니다.

화면에서 보이는 모습#

설정은 평범했습니다. 누군가 16 GB 카드 한 장에서 131072 토큰 컨텍스트를 원해서 KV 캐시를 q5_1로 줄였습니다:

bash
llama-server -m model.gguf -ngl 99 -fa on -c 131072 -np 4 \
  --cache-type-k q5_1 --cache-type-v q5_1

그다음 3.6K 토큰 프롬프트를 보냈습니다. 프롬프트 처리는 38.4 tokens/s, 디코딩은 6.5 tokens/s로 돌았습니다. 나눠 보면 3,600 ÷ 38.4 = 93.75이므로 첫 단어까지 약 94초입니다. q8_0에서는 같은 프롬프트가 1348 tokens/s로 돌았습니다. 3,600 ÷ 1,348 = 2.67로 3초 미만입니다.

단서는 작업 관리자에 있습니다. 느린 실행 중에 GPU는 0% 근처에 머물렀고 CPU는 약 930%(코어 9~10개 사용)를 보였습니다. "GPU에서 실행 중"인데 GPU가 놀고 있다면 그게 찾아야 할 증상입니다.

q5_1 및 q8_0 KV 캐시에서의 프롬프트 처리 속도
3.6K 토큰 프롬프트의 프롬프트 평가, RTX 4080, llama.cpp 이슈 #28455 (2026년 9월 5일)

더 작은 캐시가 이렇게까지 느려지는 이유#

Flash attention은 융합된 GPU 커널입니다. 기본 CUDA 빌드는 일부 KV 캐시 타입 조합에만 이를 제공합니다. q4_0, q4_1, q5_0, q5_1에는 커널이 없으므로 llama.cpp는 오류로 멈추지 않습니다. 해당 연산을 CPU로 옮기고 계속 진행합니다. 모든 것이 여전히 동작하지만 프롬프트 처리에서 약 35배 느립니다(1348 ÷ 38.4 = 35.1).

문제의 핵심은 바로 그 "조용히"입니다. 답변 어디에도 알려 주지 않습니다. 프롬프트 처리가 가장 크게 떨어지고(약 35배), 생성도 느려집니다. 같은 이슈에서 q8_0일 때 30.1 tokens/s인 반면 6.5 tokens/s입니다.

2분 만에 확인하기#

서버에서 쓰는 것과 같은 캐시 타입으로 llama-bench를 변형별로 한 번씩 실행하고, 다른 터미널에서 GPU를 지켜보세요:

bash
llama-bench -m model.gguf -ngl 99 -fa 1 -ctk q8_0 -ctv q8_0 -p 512 -n 0
llama-bench -m model.gguf -ngl 99 -fa 1 -ctk q5_1 -ctv q5_1 -p 512 -n 0
nvidia-smi -l 1

이슈 작성자의 Qwen 27B Q5_K_XL에서의 pp512 결과는 q8_0/q8_0가 1511.92 tokens/s, 기본 빌드에서 q5_1/q5_1가 약 197입니다. 두 줄에 그런 차이가 보이고 nvidia-smi에서 두 번째 실행 중 GPU가 놀고 있다면 원인을 찾은 것입니다.

무언가를 고치기 전에 보이는 증상을 유력한 원인과 맞춰 보세요:

짝 맞추기

각 증상을 원인과 연결하기

  • 긴 프롬프트 중 GPU 약 0%, CPU 약 930%Attention이 CPU에서 실행 중
  • q8_0일 때 30.1인데 디코딩이 약 6.5 tokens/s로 하락프롬프트 처리뿐 아니라 생성도 느려짐
  • 같은 플래그가 q8_0에서는 빠름이 캐시 타입 조합에는 기본 빌드에 GPU flash-attention 커널이 없음

해결 1: q8_0 사용(또는 재빌드)#

가장 빠른 해결책은 플래그 두 개를 바꾸고 나머지는 그대로 두는 것입니다:

bash
llama-server -m model.gguf -ngl 99 -fa on -c 131072 -np 4 \
  --cache-type-k q8_0 --cache-type-v q8_0

이슈에서 이렇게 하자 프롬프트 처리가 1348 tokens/s, 디코딩이 30.1 tokens/s(6.5에서 상승)가 되었습니다. 대가는 메모리입니다. q8_0 캐시는 q5_1보다 크므로 컨텍스트 길이가 여전히 VRAM에 들어가는지 확인하세요.

메모리 때문에 q5_1를 유지해야 한다면 모든 flash-attention 캐시 조합을 컴파일해 넣고 재빌드하세요:

bash
cmake -B build -DGGML_CUDA=ON -DGGML_CUDA_FA_ALL_QUANTS=ON
cmake --build build --config Release -j

그 빌드는 pp512에서 q5_1를 1509.70 tokens/s로, 3.6K 토큰 프롬프트에서 1162 tokens/s로 실행했고 디코딩은 38.5 tokens/s였습니다. 이슈의 한 댓글 작성자는 백엔드 간에 작업을 옮기면 수치 결과가 조금 달라질 수 있다고 지적하며 빌드 간 perplexity 비교를 제안했습니다. 정확한 출력이 중요하다면 한 번 해 보세요.

비교하기

어떤 해결책이 내 카드에 맞을까?

q8_0 캐시

--cache-type-k와 --cache-type-v를 q8_0로 바꾸세요. 재빌드는 필요 없습니다. q5_1보다 VRAM을 더 쓰므로 맞출 수 있는 컨텍스트 크기를 다시 확인하세요.

FA_ALL_QUANTS로 재빌드

q5_1를 유지하고 -DGGML_CUDA_FA_ALL_QUANTS=ON로 빌드하세요. VRAM은 이전과 같지만 직접 빌드를 관리해야 하고 컴파일 시간이 더 걸립니다.

메인테이너의 "알려진 이슈" 답변은 현재 llama.cpp 버전에서 이것이 바뀌었는지 말하지 않습니다. 정직한 확인은 위의 llama-bench 쌍입니다. 두 줄이 비슷하다면 문제없습니다.

튜닝이 더 이상 가치 없어질 때#

해결책이 동작한다고 치고, 이제 다음 라운드를 저울질한다고 해 봅시다. 플래그를 더 넣거나, GPU를 하나 더 달거나, 더 작은 양자화를 쓰는 것입니다. 작업이 몇 천 토큰 프롬프트에 대한 요약, 코드 리뷰, 추출이라면 요청 하나에 와트뿐 아니라 시간과 노력이 얼마나 드는지 물어보세요.

비교해 볼 만한 명시된 워크로드가 있습니다. 요약 작업이 입력 3,600 토큰을 보내고 500 토큰을 받는 일을 1,000번 한다고 해 봅시다. CheapTokens에서는 DeepSeek-V4-Flash(380K 컨텍스트, 답변당 최대 32,768 출력 토큰, 시간대와 무관한 단일 가격)에서 약 $0.104, GLM-5.3-Flash(390K 컨텍스트)에서 약 $0.092입니다. 표는 입력과 출력을 합쳐 세며, 비교용 OpenRouter 열도 있습니다:

모델 요청당 1,000회 요청 OpenRouter, 1,000회 요청
GLM-5.3-Flash $0.000092 $0.092 $0.187
DeepSeek-V4-Flash $0.0001 $0.104 $0.249
MiniMax-M2.7 $0.00026 $0.255 $1.68

요청당 입력 3,600개 및 출력 500개 토큰입니다. 100만 토큰당 가격은 현재 기준이며 당사 요금제에 따라 함께 변경됩니다. OpenRouter 열은 동일한 작업량에 대한 참고 가격을 보여줍니다.

API는 캐시 타입, VRAM, 빌드 플래그에 관심이 없고 CPU로 넘어갈 일도 없습니다. 먼저 실제 프롬프트 몇 개를 보내고 답변을 직접 판단해 보세요. 단계는 작습니다. 엔드포인트가 OpenAI Chat Completions 형식을 쓰므로 대부분의 로컬 클라이언트는 base URL만 바꾸면 됩니다.

bash
curl https://api.cheaptokens.dev/v1/chat/completions \
  -H "Authorization: Bearer $CHEAPTOKENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "deepseek-ai/DeepSeek-V4-Flash-0731", "messages": [{"role": "user", "content": "Summarize the text below in 5 bullets: ..."}], "max_tokens": 500}'

전체 설정은 CheapTokens 문서에 있고, 현재 모델별 가격은 가격 페이지에 있습니다. 하드웨어와 API를 더 길게 비교하려면 Run DeepSeek locally or use an API를 보세요. 입력과 출력이 따로 청구되는 방식을 보려면 input vs output tokens에서 다룹니다.

작업 순서#

순서대로 배열하기

문제 해결 단계 순서 정하기

  1. q8_0과 q5_1에 대해 llama-bench를 실행하고 nvidia-smi를 지켜보기
  2. 두 캐시 타입을 q8_0으로 바꾸기
  3. 컨텍스트 길이가 여전히 VRAM에 들어가는지 확인하기
  4. q5_1이 필요할 때만 GGML_CUDA_FA_ALL_QUANTS=ON으로 재빌드하기
  5. 로컬 설정이 여전히 노력에 비해 가치가 없다면 같은 프롬프트를 API에서 비교하기

출처에 대한 주의를 하나 더 하자면, 전체 진단은 공개 이슈 하나와 GPU 하나에 기대고 있습니다. 1348과 38.4는 그곳에서 일어난 일로 받아들이고, 둘 중 어느 것도 믿기 전에 자기 카드에서 측정하세요.