[{"content":"한눈에 보는 결과 모델 유형 감지율 시간 비용 Claude Sonnet 4 Cloud 100% 416s $0.564 Codestral 22B 로컬 91.7% 903s $0 CodeGemma 7B 로컬 83.3% 621s $0 GPT-4o Cloud 83.3% 99s $0.146 Granite-Code 8B 로컬 36.1% 716s $0 StarCoder2 15B 로컬 0% 132s $0 왜 이걸 해봤나 코드 리뷰에 LLM을 붙이는 시도가 늘고 있습니다. GitHub Copilot Code Review 같은 서비스도 나왔고, 직접 파이프라인을 구축하는 사례도 많아지고 있습니다.\n그런데 막상 도입하려고 보면 고민이 생깁니다.\nGPT-4o나 Claude 같은 Cloud API를 쓰면 코드가 외부로 나갑니다 그렇다고 로컬 LLM을 쓰면 성능이 떨어지지 않을까? 실제로 얼마나 차이 나는지 데이터가 없습니다 Ollama 기반 오픈소스 모델 4종과 Cloud API 2종을 같은 diff로 테스트해서, 어떤 이슈를 잡아내고 어떤 걸 놓치는지 비교한 기록입니다.\n실험 설계 테스트 환경 항목 사양 하드웨어 Apple Silicon, 48GB Unified Memory OS macOS 로컬 실행 Ollama Cloud API OpenAI (GPT-4o), Anthropic (Claude Sonnet 4) 벤치마크 코드 Python 3.11 + httpx 비교 모델 중국산 모델은 제외하고, 코드 리뷰에 쓸만한 모델 위주로 골랐습니다. 10B 이하 2종, 20B 이하 2종으로 나눠서 파라미터 크기별 차이도 같이 봤습니다.\n모델 파라미터 유형 만든 곳 특징 CodeGemma 7B 7B 로컬 Google 코드 특화, FIM 지원 Granite-Code 8B 8B 로컬 IBM 128K 컨텍스트, 116개 언어 StarCoder2 15B 15B 로컬 BigCode 600+ 언어, 4T 토큰 학습 Codestral 22B 22B 로컬 Mistral AI HumanEval 81%, 32K 컨텍스트 GPT-4o - Cloud OpenAI 128K 컨텍스트 Claude Sonnet 4 - Cloud Anthropic 200K 컨텍스트 테스트 데이터 보안 취약점, 설계 이슈, 성능 문제를 의도적으로 넣은 diff 3개를 준비했습니다.\nDiff 줄 수 언어 심어둔 이슈 sample.diff ~130줄 Python 하드코딩 비밀번호, SQL 인젝션, 빈 except springboot-ddd.diff ~650줄 Java DDD 레이어 위반, 트랜잭션 누락, N+1 ecommerce-platform.diff ~1200줄 Java 하드코딩 시크릿, MD5, SQL 인젝션, JWT 각 모델에 동일한 시스템 프롬프트를 주고, JSON 형식으로 이슈를 반환하도록 요청했습니다.\n측정 지표 감지율: 심어둔 이슈 중 몇 개를 찾았는지 (키워드 매칭) 리뷰 시간: wall-clock 기준 JSON 파싱 성공률: 자동화 파이프라인에 넣으려면 구조화된 출력이 필수입니다 토큰 / 비용: Cloud 모델 한정 파이프라인 flowchart LR A[Diff 로드] --\u003e B[프롬프트 생성] B --\u003e C{모델 유형} C --\u003e|로컬| D[Ollama API] C --\u003e|Cloud| E[OpenAI / Anthropic] D --\u003e F[JSON 파싱] E --\u003e F F --\u003e G[정답 대조] G --\u003e H[메트릭 저장] diff 안의 모든 리뷰 대상 파일을 각각 개별 리뷰하는 방식으로 테스트했습니다. ecommerce diff는 28개 파일이 들어있어서 모델당 28번 호출했습니다.\n로컬 모델 결과 감지율 모델 sample (3이슈) springboot (4이슈) ecommerce (4이슈) 평균 총 시간 Codestral 22B 100% 75% 100% 91.7% 903s CodeGemma 7B 100% 50% 100% 83.3% 621s Granite-Code 8B 33% 50% 25% 36.1% 716s StarCoder2 15B 0% 0% 0% 0% 132s xychart-beta title \"로컬 모델 감지율\" x-axis [\"Codestral 22B\", \"CodeGemma 7B\", \"Granite-Code 8B\", \"StarCoder2 15B\"] y-axis \"감지율 (%)\" 0 --\u003e 100 bar [91.7, 83.3, 36.1, 0.0] 모델별 소감 Codestral 22B — 91.7%로 GPT-4o(83.3%)보다 높게 나왔습니다. sample.diff의 하드코딩 비밀번호, SQL 인젝션, 빈 except를 전부 잡았고 ecommerce의 JWT 시크릿, MD5 해시까지 찾아냈습니다. springboot에서 트랜잭션 누락 하나만 놓쳤습니다. 대신 느려서 ecommerce 28파일 처리에 약 500초 걸렸습니다.\nCodeGemma 7B — 7B인데 GPT-4o와 같은 83.3%가 나왔습니다. Codestral보다 30% 빠르고, JSON도 100% 깨지지 않았습니다. 메모리도 6~8GB면 되니까 웬만한 개발 머신에서 돌릴 수 있습니다.\nGranite-Code 8B — 128K 컨텍스트가 스펙상 좋아 보여서 기대했는데, 실제로는 JSON 출력이 불안정했습니다. 많은 파일에서 0개 코멘트를 뱉거나, 갑자기 80초 넘게 걸리는 응답이 나왔습니다.\nStarCoder2 15B — 코드 리뷰에는 못 씁니다. 48개 파일 전부 이슈 0개였습니다. JSON 대신 코드 완성 형태로만 응답했습니다. 코드 생성 모델이지 코드 리뷰 모델은 아닌 셈입니다. 15B라서 기대했는데 파라미터 수가 전부가 아니었습니다.\nCloud 모델 결과 감지율 모델 sample (6파일) springboot (14파일) ecommerce (28파일) 평균 총 시간 Claude Sonnet 4 100% 100% 100% 100% 416s GPT-4o 100% 50% 100% 83.3% 99s Claude만 100%를 기록했습니다. GPT-4o는 springboot에서 트랜잭션 누락과 N+1을 놓쳤습니다.\nGPT-4o vs Claude Sonnet 4 항목 GPT-4o Claude Sonnet 4 감지율 83.3% 100% 리뷰 스타일 간결, 핵심 위주 상세, 배경 설명 포함 파일당 코멘트 ~2개 ~5개 오탐 적음 보통 속도 (전체) 99s 416s 비용 (전체) $0.146 $0.564 GPT-4o는 빠르고 저렴하지만, 아키텍처 수준 이슈는 Claude가 더 잘 잡습니다. Claude는 \u0026ldquo;왜 문제인지\u0026quot;까지 설명해주는 반면 GPT-4o는 핵심만 짚고 넘어갑니다.\n토큰 소모량 모델 Diff Input Output 합계 비용 GPT-4o sample (6파일) 3,413 504 3,917 $0.014 GPT-4o springboot (14파일) 10,804 886 11,690 $0.036 GPT-4o ecommerce (28파일) 21,544 4,290 25,834 $0.097 Claude Sonnet 4 sample (6파일) 4,916 1,508 6,424 $0.037 Claude Sonnet 4 springboot (14파일) 15,360 7,794 23,154 $0.163 Claude Sonnet 4 ecommerce (28파일) 31,027 18,002 49,029 $0.363 합계 87,064 32,984 120,048 $0.710 Claude가 출력 토큰을 3~4배 더 씁니다. 상세한 설명을 달아주는 만큼 비용도 올라갑니다.\n기준 단가 — GPT-4o: input $2.50/1M, output $10.00/1M. Claude Sonnet 4: input $3.00/1M, output $15.00/1M (2026년 4월)\n종합 비교 전체 결과 모델 유형 감지율 시간 JSON 비용 Claude Sonnet 4 Cloud 100% 416s 100% $0.564 Codestral 22B 로컬 91.7% 903s 100% $0 CodeGemma 7B 로컬 83.3% 621s 100% $0 GPT-4o Cloud 83.3% 99s 100% $0.146 Granite-Code 8B 로컬 36.1% 716s 불안정 $0 StarCoder2 15B 로컬 0% 132s 0% $0 Codestral 22B가 GPT-4o보다 높게 나왔습니다.\n속도 비교 ecommerce diff(28파일) 기준으로 속도 차이가 가장 큽니다.\n모델 sample springboot ecommerce GPT-4o 10.6s 21.3s 67s Claude Sonnet 4 34.8s 121.0s 260s CodeGemma 7B 29.2s 393.9s 198s Codestral 22B 79.2s 316.3s 508s 로컬 LLM이 느린 건 모델 문제가 아니라 하드웨어 차이입니다. Cloud는 HBM 2~3TB/s 대역폭의 A100/H100에서 돌리지만, Apple Silicon은 ~400GB/s Unified Memory에서 단일 디바이스로 순차 실행됩니다. 거기에 Cloud LLM은 speculative decoding, continuous batching으로 분산 처리까지 하기 때문에 차이가 날 수밖에 없습니다. 전용 GPU 서버를 쓰면 로컬 모델도 훨씬 빨라집니다.\n비용 추산 PR 규모별로 Cloud 비용을 계산해보면 다음과 같습니다.\n월간 PR diff 크기 GPT-4o Claude Sonnet 4 100건 Small ~$1.4 ~$3.7 100건 Medium ~$3.6 ~$16.3 100건 Large ~$9.7 ~$36.3 500건 Mixed ~$24.5 ~$94 GPT-4o는 월 $25 수준이면 500건도 처리할 수 있습니다. Claude는 상세한 리뷰를 원할 때 쓸 만하지만 비용이 3~4배 듭니다. 로컬 모델은 하드웨어 초기 비용 외에는 추가 비용이 없습니다.\ndiff 전처리로 테스트 파일이나 lock 파일을 제외하면 토큰을 20~40% 줄일 수 있습니다.\n보안 항목 로컬 Cloud 코드 외부 전송 없음 있음 데이터 보존 자체 관리 공급사 정책 규정 준수 용이 별도 계약 필요 코드가 외부로 나가면 안 되는 환경이라면 로컬 모델을 써야 합니다.\n결론 로컬 모델이 생각보다 잘 나왔습니다.\nCodestral 22B(91.7%)가 GPT-4o(83.3%)보다 높았고, CodeGemma 7B도 7B로 GPT-4o와 같은 수치를 기록했습니다. Claude Sonnet 4만 100%였지만, 비용이나 보안 때문에 로컬 모델을 써야 하는 상황이라면 충분히 쓸 수 있는 수준입니다.\n어떤 모델을 쓸까 flowchart TD A[코드 리뷰 자동화] --\u003e B{코드 외부 전송 가능?} B --\u003e|가능| C{예산} B --\u003e|불가| D[로컬 모델] C --\u003e|충분| E[\"Claude Sonnet 4 (100%)\"] C --\u003e|절약| F[\"GPT-4o (83%, $25/월)\"] D --\u003e G{GPU/RAM} G --\u003e|22B 가능| H[\"Codestral 22B (91.7%)\"] G --\u003e|8B 이하| I[\"CodeGemma 7B (83.3%)\"] 상황 추천 감지율 이유 코드 유출 불가 Codestral 22B 91.7% GPT-4o 이상, 로컬 실행 GPU/RAM 부족 CodeGemma 7B 83.3% 6GB면 충분, GPT-4o 동급 최고 품질 Claude Sonnet 4 100% 설계 이슈까지 잡음 빠른 응답 GPT-4o 83.3% 99초, 비용 합리적 하이브리드 CodeGemma + Claude - 1차 필터 + 2차 정밀 리뷰 남은 과제 프롬프트 튜닝으로 감지율을 더 올릴 수 있는지 few-shot 예시를 모델별로 최적화하면 차이가 줄어드는지 실제 PR에 붙여서 운용할 때 오탐률이 어떤지 이 벤치마크는 특정 diff 셋 기준입니다. 언어나 코드 스타일이 다르면 결과도 달라질 수 있으니, 자기 코드베이스로 직접 돌려보는 걸 권장합니다.\n실험 코드는 GitHub에 올려뒀습니다.\n","permalink":"https://geonhos.github.io/posts/llm-benchmark-code-review/","summary":"Ollama로 돌린 오픈소스 LLM 4종과 GPT-4o, Claude Sonnet 4의 코드 리뷰 성능을 직접 비교해봤습니다.","title":"코드 리뷰용 LLM 벤치마크: 로컬 vs Cloud"},{"content":" 원본: anthropics/claude-code CHANGELOG.md\n마지막 업데이트: 2026-04-09 (v2.1.97 기준)\n모델 업그레이드 이력 버전 모델 변경 v2.1.77 Opus 4.6 기본 최대 출력 64k 토큰, Opus 4.6·Sonnet 4.6 상한 128k 토큰으로 확대 v2.1.75 Opus 4.6 1M 컨텍스트 윈도우 기본 적용 (Max/Team/Enterprise) v2.1.73 Bedrock/Vertex/Foundry 기본 Opus를 4.6으로 변경 (기존 4.1) v2.1.94 기본 effort를 high로 상향 (API-key·Bedrock/Vertex/Foundry·Team·Enterprise) v2.1.68 Opus 4.6 기본 effort를 medium으로 변경 (Max/Team), Opus 4·4.1 퇴출 v2.1.45 Claude Sonnet 4.6 지원 v2.1.36 Opus 4.6 Fast mode 지원 v2.1.32 Claude Opus 4.6 출시 v2.0.51 Opus 4.5 출시, Pro 사용자 extra usage로 사용 가능 v2.0.17 Haiku 4.5 모델 셀렉터 추가, Explore subagent에 Haiku 적용 v1.0.69 Opus 4.1로 업그레이드 Major Feature Release 버전 카테고리 핵심 기능 설명 v2.1.97 UX Focus View \u0026amp; NO_FLICKER 안정화 Ctrl+O 포커스 뷰 토글, statusline refreshInterval 주기 갱신, Cedar 구문 하이라이팅, Bridge 세션에 로컬 git 정보 표시, NO_FLICKER 다수 수정 v2.1.94 Platform Bedrock Mantle 지원 \u0026amp; effort high 기본 CLAUDE_CODE_USE_MANTLE=1로 Mantle 지원, 기본 effort medium→high 변경, Console 로그인 수정, --resume worktree 직접 재개 v2.1.92 UX Bedrock 셋업 위저드 \u0026amp; /cost 세부 표시 로그인 화면에서 Bedrock 대화형 설정 위저드, /cost에 모델별·캐시 히트 비용 분류, forceRemoteSettingsRefresh 정책, /release-notes 버전 선택 UI v2.1.91 Extensibility MCP 대용량 결과 \u0026amp; 플러그인 실행파일 MCP 도구 결과 최대 500K까지 크기 제한 재정의, 플러그인 bin/ 실행파일 지원, disableSkillShellExecution 설정 v2.1.90 UX /powerup 기능 튜토리얼 기능별 애니메이션 데모로 학습하는 /powerup 커맨드, auto 모드 사용자 제한 지시 반영 수정, SSE 대용량 프레임 선형 시간 처리 v2.1.89 Automation defer 권한 결정 \u0026amp; Flicker-free 렌더링 PreToolUse hooks에 \u0026quot;defer\u0026quot; 결정 추가, CLAUDE_CODE_NO_FLICKER=1 alt-screen 렌더링, PermissionDenied hook, auto 모드 거부 알림 및 /permissions Recent 탭 재시도 v2.1.86 Stability 세션 안정성 \u0026amp; 토큰 최적화 --resume 호환성 수정, Read 도구 토큰 절감 포맷, @ 멘션 JSON 이스케이프 제거, 메모리 렌더 캐시 누수 수정 v2.1.85 Extensibility Hook 조건부 필터링 \u0026amp; MCP OAuth RFC 9728 hooks에 if 필드로 조건부 실행, MCP OAuth RFC 9728 Protected Resource Metadata, PreToolUse hooks AskUserQuestion 응답 지원 v2.1.84 Platform PowerShell 도구 \u0026amp; IME 입력 수정 Windows PowerShell 도구 opt-in 프리뷰, CJK IME 인라인 렌더링 수정, TaskCreated hook, idle-return 프롬프트(75분+), 토큰 1M+ 표기 개선 v2.1.83 Mgmt managed-settings.d/ \u0026amp; 트랜스크립트 검색 관리 설정 drop-in 디렉토리, CwdChanged/FileChanged hook, 트랜스크립트 / 검색, sandbox.failIfUnavailable, 이미지 [Image #N] 칩, subagent initialPrompt v2.1.81 Automation --bare 모드 \u0026amp; Channels 릴레이 스크립팅용 --bare 플래그 (hooks/LSP/플러그인 생략), --channels로 MCP 서버가 권한 승인 프롬프트를 모바일로 전달 가능 v2.1.80 Extensibility Channels (Research Preview) MCP 서버가 세션에 메시지를 push할 수 있는 --channels, 스킬/슬래시커맨드에 effort frontmatter 지원, statusline에 rate_limits 필드 추가 v2.1.79 IDE VS Code Remote Control VS Code에서 /remote-control로 claude.ai/code와 브릿지 세션, AI 기반 세션 탭 타이틀 자동 생성 v2.1.78 Extensibility Streaming 개선 \u0026amp; 플러그인 데이터 응답 텍스트 라인별 스트리밍, ${CLAUDE_PLUGIN_DATA} 영구 저장소, StopFailure hook, tmux passthrough 알림 지원 v2.1.77 Performance 출력 토큰 대폭 확대 Opus 4.6 기본 64k, 상한 128k 토큰, /copy N 인덱스 지원, /fork → /branch 리네임, 세션 재개 최대 45% 빨라짐 v2.1.76 Integration MCP Elicitation MCP 서버가 태스크 중 사용자에게 구조화된 입력을 요청 가능 (폼/브라우저 URL), /effort 커맨드, worktree.sparsePaths 설정 v2.1.75 Model Opus 4.6 1M 컨텍스트 기본 Max/Team/Enterprise에서 Opus 4.6 1M 컨텍스트 기본 적용, /color 커맨드, 메모리 파일에 last-modified 타임스탬프 v2.1.74 UX /context 커맨드 \u0026amp; 메모리 설정 /context로 컨텍스트 사용량 분석 및 최적화 팁 제공, autoMemoryDirectory 설정, 스트리밍 버퍼 메모리 누수 수정 v2.1.73 Stability modelOverrides \u0026amp; 안정성 패치 모델 피커 항목을 커스텀 provider 모델 ID로 매핑, Bedrock/Vertex/Foundry 기본 Opus 4.6 적용, CPU 100% 프리즈 수정 v2.1.72 UX Effort 단순화 \u0026amp; Plan 개선 effort 레벨을 low/medium/high로 단순화 (○ ◐ ●), /plan 설명 인자 지원, ExitWorktree 도구, /copy 파일 쓰기(w키), bash 파싱 네이티브 모듈 전환, 번들 ~510KB 축소 v2.1.71 Automation /loop 커맨드 \u0026amp; Cron 스케줄링 /loop 5m check the deploy 식의 반복 실행 커맨드, 세션 내 cron 스케줄링, voice:pushToTalk 키 리바인딩, 장시간 세션 stdin 프리즈 수정 v2.1.70 Stability 서드파티 API \u0026amp; VS Code 안정성 ANTHROPIC_BASE_URL 서드파티 게이트웨이 400 에러 수정, Windows/WSL 비ASCII 클립보드 수정, VS Code 세션 관리 아이콘·MCP 관리 다이얼로그 추가 v2.1.69 Skills /claude-api 스킬 \u0026amp; 다국어 음성 Claude API 개발용 스킬, 음성 STT 20개 언어로 추가, effort 표시 UI, sandbox.enableWeakerNetworkIsolation, /reload-plugins, 다수 메모리 누수 수정 v2.1.68 Model Opus 4.6 medium effort 기본 Max/Team 구독자 기본 effort를 medium으로 변경, \u0026ldquo;ultrathink\u0026rdquo; 키워드 부활 (high effort), Opus 4·4.1 퇴출 → 자동 4.6 이관 v2.1.63 Extensibility HTTP Hooks \u0026amp; 메모리 누수 다수 수정 HTTP hooks (POST JSON) 추가, /simplify·/batch 번들 슬래시 커맨드, 프로젝트 설정/auto memory가 git worktree 간 공유, 10+ 메모리 누수 수정 v2.1.59 UX Auto Memory \u0026amp; /copy 유용한 컨텍스트를 자동으로 auto-memory에 저장, /copy 커맨드에 코드 블록 선택 UI 추가 v2.1.53 Stability Windows \u0026amp; 크로스 플랫폼 안정성 Windows ARM64 크래시 수정, WASM 인터프리터 크래시 수정, UI 깜빡임 수정, ctrl+f 일괄 에이전트 종료 개선 v2.1.51 Platform Remote Control \u0026amp; Managed Settings claude remote-control 서브커맨드, macOS plist / Windows Registry로 관리형 설정, 커스텀 npm 레지스트리 지원 v2.1.50 Performance 메모리 최적화 \u0026amp; 1M 컨텍스트 장시간 세션 메모리 누수 수정, Opus 4.6 fast mode 1M 컨텍스트 윈도우, claude agents CLI 명령 추가 v2.1.49 Isolation Git Worktree 세션 --worktree (-w) 플래그로 격리된 git worktree 세션 지원, subagent에도 isolation: \u0026quot;worktree\u0026quot; 모드 추가 v2.1.47 Stability 안정성 일괄 패치 PDF 대화 compaction 수정, Windows MSYS2/Cygwin bash 수정, chat:newline keybinding 등 70+ 버그픽스 v2.1.46 Integration claude.ai MCP 커넥터 claude.ai의 MCP 커넥터를 Claude Code에서 사용 가능 v2.1.32 Multi-Agent Agent Teams (Research Preview) 다중 에이전트 협업 기능, 자동 메모리 기록/회상, \u0026ldquo;Summarize from here\u0026rdquo; 메시지 셀렉터 v2.1.18 UX 커스텀 키보드 단축키 컨텍스트별 설정 가능한 키보드 단축키 및 chord 시퀀스 지원 v2.1.16 Task Mgmt 태스크 관리 시스템 의존성 추적이 가능한 새로운 태스크 관리 시스템 v2.1.3 Skills Slash commands + Skills 통합 슬래시 커맨드와 스킬을 하나로 합쳐 단순화 v2.1.2 Platform Windows winget 설치 Windows Package Manager 설치 지원, OSC 8 하이퍼링크 v2.0.72 Integration Claude in Chrome (Beta) Chrome 확장 프로그램 연동, @ 멘션 3배 속도 향상 v2.0.64 Core 비동기 실행 \u0026amp; /stats Agent/bash 비동기 실행, /stats 통계, /rename 세션 이름, .claude/rules/ 지원 v2.0.60 Agent Background Agent 백그라운드 에이전트 지원, \u0026amp;로 웹에 백그라운드 태스크 전송 v2.0.51 Product Claude Code for Desktop 데스크톱 앱 출시, Plan Mode 개선, 사용량 한도 알림 개선 v2.0.45 Platform Microsoft Foundry 지원 MS Foundry 지원, PermissionRequest hook, 백그라운드 태스크 \u0026amp; 전송 v2.0.28 Agent Plan subagent \u0026amp; Subagent 재개 Plan subagent 도입, subagent 재개 가능, 동적 모델 선택, --max-budget-usd 플래그 v2.0.24 Security Sandbox Mode Linux/Mac BashTool 샌드박스 모드 릴리스, Claude Code Web teleport 지원 v2.0.20 Skills Claude Skills 스킬 시스템 도입 v2.0.12 Extensibility Plugin System 플러그인 시스템 릴리스, /plugin install, marketplace, 구조 검증 v2.0.10 Core 터미널 렌더러 재작성 새 터미널 렌더러, Ctrl+G 외부 에디터, PreToolUse hook 입력 수정 v2.0.0 Major v2.0 릴리스 VS Code 네이티브 확장, 새 UI, /rewind, /usage, Tab 사고모드 토글, Ctrl+R 히스토리 검색, Agent SDK v1.0.60 Agent 커스텀 Subagent 특정 태스크용 커스텀 subagent 생성 기능 v1.0.58 Tool PDF 읽기 지원 Read 도구에서 PDF 파일 직접 읽기 가능 v1.0.51 Platform Windows 네이티브 지원 Git for Windows 기반 네이티브 Windows 지원 v1.0.44 UX /export \u0026amp; MCP 개선 대화 공유 /export, MCP 리소스 링크, Ctrl+Z suspend 변경 v1.0.38 Extensibility Hooks 시스템 커스터마이징을 위한 hooks 시스템 릴리스 v1.0.30 Skills 커스텀 슬래시 커맨드 bash 실행, @-mention, thinking 지원 커스텀 슬래시 커맨드 주요 키보드 단축키 변경 이력 버전 변경 내용 v2.1.97 Ctrl+O: NO_FLICKER 모드 포커스 뷰 토글 (프롬프트·도구 요약·최종 응답만 표시) v2.1.92 /tag 및 /vim 커맨드 제거, /release-notes 버전 선택 UI v2.1.89 auto 모드 거부 명령 /permissions Recent 탭 표시, r키 재시도 v2.1.84 Ctrl+X Ctrl+E: 외부 에디터 열기 별칭 (readline 네이티브), chat:killAgents/chat:fastMode 리바인딩 가능 v2.1.83 트랜스크립트 모드에서 / 검색, n/N 탐색, 백그라운드 에이전트 종료 Ctrl+F → Ctrl+X Ctrl+K로 변경 v2.1.78 Ctrl+U: normal 모드에서 readline kill-line으로 변경 (반페이지 스크롤은 transcript 모드 전용) v2.1.77 /copy N: N번째 최신 어시스턴트 응답 복사, /fork → /branch 리네임 v2.1.71 voice:pushToTalk 키바인딩 리바인딩 가능 (기본값: Space) v2.1.69 Ctrl+U: 빈 bash 프롬프트에서 bash 모드 종료, 숫자 키패드 지원 v2.1.49 Ctrl+F: 백그라운드 에이전트 종료 (2회 눌러 확인) v2.1.18 커스텀 키보드 단축키 시스템 도입 v2.0.72 사고 모드 토글: Tab → Alt+T v2.0.65 Alt+P: 프롬프트 작성 중 모델 전환 v2.0.0 Ctrl+R: 히스토리 검색, Tab: 사고모드 토글 v1.0.71 Ctrl+B: bash 백그라운드 실행 v1.0.48 Vim c, f/F, t/T 모션 추가 v1.0.44 Ctrl+Z: suspend로 변경 (undo는 Ctrl+U) v1.0.30 Ctrl+R: 타임스탬프 및 Ctrl+C 핸들링 플랫폼 \u0026amp; 인프라 지원 확대 버전 플랫폼 내용 v2.1.97 Perf MCP HTTP/SSE 재연결 시 ~50MB/hr 버퍼 누수 수정, 429 재시도 지수 백오프 최소값 적용, 세션 트랜스크립트 크기 최적화 v2.1.94 Platform Amazon Bedrock Mantle 지원 (CLAUDE_CODE_USE_MANTLE=1), CJK UTF-8 스트림 깨짐 수정 v2.1.92 Perf Write 도구 diff 대형 파일 60% 속도 향상, Linux sandbox apply-seccomp 헬퍼 npm/native 빌드 모두 포함 v2.1.91 Perf Edit 도구 shorter old_string 앵커로 출력 토큰 절감 v2.1.90 Perf WASM yoga-layout → 순수 TypeScript 구현 전환, /powerup 기능 튜토리얼 v2.1.89 Perf autocompact 무한 루프 수정, 중첩 CLAUDE.md 중복 주입 방지, Bash 도구 포맷터/린터 파일 변경 감지 경고 v2.1.86 Perf Read 도구 토큰 사용량 절감 포맷, @ 멘션 JSON 이스케이프 제거 v2.1.85 MCP MCP OAuth RFC 9728 Protected Resource Metadata discovery v2.1.84 Platform PowerShell 도구 opt-in 프리뷰, CJK IME 인라인 수정, Scalar/GVFS partial clone 대량 blob 다운로드 수정 v2.1.83 Mgmt managed-settings.d/ drop-in 디렉토리, sandbox.failIfUnavailable, CLAUDE_CODE_SUBPROCESS_ENV_SCRUB 자격 증명 제거 v2.1.81 MCP MCP OAuth Client ID Metadata Document (CIMD / SEP-991) 지원 v2.1.80 Perf 대형 저장소(250k 파일) 시작 시 메모리 ~80MB 절감 v2.1.79 VS Code /remote-control 브릿지, AI 세션 타이틀, 시작 메모리 ~18MB 절감 v2.1.78 Terminal tmux passthrough 알림 지원 (iTerm2/Kitty/Ghostty), 라인별 응답 스트리밍 v2.1.77 Performance macOS 시작 ~60ms 단축, --resume 최대 45% 빨라짐·~100-150MB 메모리 절감 v2.1.76 MCP MCP Elicitation — 서버가 태스크 중 구조화된 입력 요청 가능 v2.1.75 Managed Windows managed settings 경로 변경: C:\\Program Files\\ClaudeCode\\managed-settings.json v2.1.74 Platform RTL 텍스트(히브리어, 아랍어) Windows Terminal/VS Code 렌더링 수정, macOS 마이크 권한 entitlement 추가 v2.1.73 Platform Amazon Linux 2 (glibc 2.26) 네이티브 모듈 지원, Bedrock/Vertex/Foundry 기본 Opus 4.6 v2.1.72 VS Code vscode://anthropic.claude-code/open URI 핸들러 추가, effort 표시 인디케이터 v2.1.70 VS Code 세션 관리 아이콘, Plan 마크다운 뷰, 네이티브 MCP 서버 관리 다이얼로그 v2.1.69 Voice 음성 STT 20개 언어 지원 (러시아어, 폴란드어, 터키어, 네덜란드어 등 10개 추가) v2.1.51 Mgmt macOS plist / Windows Registry 관리형 설정 v2.1.41 ARM64 Windows ARM64 네이티브 바이너리 v2.1.2 Windows winget 패키지 매니저 설치 v2.0.45 Cloud Microsoft Foundry 지원 v2.0.0 IDE VS Code 네이티브 확장 v1.0.73 Linux Alpine, musl 지원 v1.0.51 Windows 네이티브 Windows 지원 (Git for Windows) v1.0.35 MCP OAuth Authorization Server discovery SDK / API 주요 변경 버전 변경 내용 v2.1.97 statusline refreshInterval 주기 갱신, workspace.git_worktree statusline 입력, sandbox.network.allowMachLookup macOS 적용, Bash OTEL TRACEPARENT 환경변수 전파, /claude-api 스킬에 Managed Agents 추가 v2.1.94 CLAUDE_CODE_USE_MANTLE=1 Bedrock Mantle 지원, keep-coding-instructions frontmatter, hookSpecificOutput.sessionTitle (UserPromptSubmit), 플러그인 스킬 frontmatter name 기반 호출명 v2.1.92 forceRemoteSettingsRefresh 정책 (fail-closed), /cost 모델별·캐시 히트 분류, Bedrock 대화형 셋업 위저드 v2.1.91 MCP _meta[\u0026quot;anthropic/maxResultSizeChars\u0026quot;] (최대 500K), disableSkillShellExecution 설정, 플러그인 bin/ 실행파일 v2.1.90 SSE 대용량 프레임 선형 시간 처리, SDK 대화 transcript 이차 시간 → 선형 최적화 v2.1.89 PreToolUse \u0026quot;defer\u0026quot; 결정, PermissionDenied hook, CLAUDE_CODE_NO_FLICKER=1, MCP_CONNECTION_NONBLOCKING, Edit symlink 대상 체크, hook if 복합 명령 매칭 수정 v2.1.86 .jj/.sl VCS 디렉토리 제외, Read 도구 compact 라인 넘버 포맷, @ 멘션 토큰 오버헤드 절감 v2.1.85 hooks if 조건부 필터 (permission rule 문법), PreToolUse updatedInput으로 AskUserQuestion 자동 응답, OTEL_LOG_TOOL_DETAILS=1 v2.1.84 PowerShell 도구 (Windows opt-in), TaskCreated hook, CLAUDE_STREAM_IDLE_TIMEOUT_MS, allowedChannelPlugins 관리 설정, MCP 설명 2KB 제한, idle-return 프롬프트 v2.1.83 managed-settings.d/ drop-in, CwdChanged/FileChanged hook, sandbox.failIfUnavailable, CLAUDE_CODE_SUBPROCESS_ENV_SCRUB, subagent initialPrompt, TaskOutput → Read 전환 v2.1.81 --bare 플래그 (hooks/LSP/플러그인 생략), --channels 권한 릴레이 v2.1.80 --channels (Research Preview), statusline rate_limits 필드, 스킬/커맨드 effort frontmatter v2.1.79 --console 플래그 (Anthropic Console API 인증), CLAUDE_CODE_PLUGIN_SEED_DIR 다중 경로 지원 v2.1.78 StopFailure hook, ${CLAUDE_PLUGIN_DATA} 영구 저장소, ANTHROPIC_CUSTOM_MODEL_OPTION 환경변수, Agent 도구에서 resume 파라미터 제거 → SendMessage 사용 v2.1.77 Opus 4.6 기본 출력 64k / 상한 128k 토큰, allowRead 샌드박스 설정 v2.1.76 Elicitation·ElicitationResult hooks, PostCompact hook, -n/--name 세션명 플래그, worktree.sparsePaths, /effort 커맨드 v2.1.74 autoMemoryDirectory 설정, SessionEnd hook timeout CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS로 설정 가능 v2.1.73 modelOverrides 설정 (모델 피커 → 커스텀 provider ID 매핑) v2.1.72 CLAUDE_CODE_DISABLE_CRON 환경변수, Agent 도구에 model 파라미터 복원, SDK query() 프롬프트 캐시 무효화 수정 (토큰 비용 최대 12배 절감) v2.1.69 InstructionsLoaded hook 이벤트, hook 이벤트에 agent_id·agent_type·worktree 필드 추가, ${CLAUDE_SKILL_DIR} 변수 v2.1.63 ENABLE_CLAUDEAI_MCP_SERVERS 환경변수로 claude.ai MCP 서버 옵트아웃 지원 v2.1.51 CLAUDE_CODE_ACCOUNT_UUID, CLAUDE_CODE_USER_EMAIL, CLAUDE_CODE_ORGANIZATION_UUID 환경변수 추가 v2.1.49 SDK model info에 supportsEffort, supportedEffortLevels, supportsAdaptiveThinking 추가 v2.1.33 Task(agent_type) 구문으로 sub-agent 제한, memory frontmatter v2.0.28 --max-budget-usd 플래그 추가 v2.0.0 Claude Agent SDK로 리브랜딩, --agents 플래그 v1.0.82 요청 취소, additionalDirectories 옵션 v1.0.59 도구 확인(tool confirmation) 지원 보안 관련 주요 패치 버전 내용 v2.1.97 --dangerously-skip-permissions protected path 승인 후 accept-edits 강등 수정, Bash 도구 env-var 접두사·네트워크 리다이렉트 권한 강화, managed-settings allow 규칙 삭제 후 재시작 전까지 활성 유지 버그 수정, permissions.additionalDirectories 세션 중 변경 미적용 수정 v2.1.92 Linux sandbox apply-seccomp 헬퍼 npm/native 빌드 모두 포함 v2.1.90 .husky 보호 디렉토리 추가 (acceptEdits 모드) v2.1.89 Edit/Read symlink 대상 경로 검증, auto 모드 사용자 제한 지시(\u0026ldquo;don\u0026rsquo;t push\u0026rdquo; 등) 반영 수정, autocompact 무한 루프 방지 v2.1.86 공식 마켓플레이스 플러그인 스크립트 macOS/Linux \u0026ldquo;Permission denied\u0026rdquo; 수정 (v2.1.83 회귀) v2.1.85 deniedMcpServers 설정이 claude.ai MCP 서버 차단하지 않는 버그 수정 v2.1.84 --mcp-config CLI 플래그가 allowedMcpServers/deniedMcpServers 관리 정책 우회하는 취약점 수정, Partial clone 대량 blob 다운로드 방지 v2.1.83 CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1로 하위 프로세스에서 Anthropic/클라우드 자격 증명 제거 v2.1.78 샌드박스 의존성 누락 시 자동 비활성화 대신 경고 표시, bypassPermissions 모드에서 .git/.claude 등 보호 디렉토리 쓰기 권한 수정 v2.1.77 PreToolUse hooks의 \u0026quot;allow\u0026quot; 반환이 deny 권한 규칙 우회하는 취약점 수정 (Enterprise managed settings 포함) v2.1.74 managed policy ask 규칙이 user allow 또는 skill allowed-tools에 의해 우회되는 취약점 수정 v2.1.69 gitignore 디렉토리에서 중첩 스킬 탐색 차단, .mcp.json 서버 자동 활성화 trust 다이얼로그 수정, symlink bypass 보안 수정 v2.1.51 statusLine·fileSuggestion hooks가 workspace trust 없이 실행되는 취약점 수정 v2.1.38 heredoc delimiter 파싱 보완 (command smuggling 방지) v2.1.34 bash permission bypass 취약점 수정 v2.1.7 wildcard permission rules 보안 취약점 수정 v2.1.6 shell line continuation을 통한 permission bypass 수정 v2.1.2 bash 처리 시 command injection 취약점 수정 v2.0.24 BashTool 샌드박스 모드 릴리스 v1.0.124 Bash tool permission check 보안 취약점 수정 v1.0.120 Bash permission check 보안 취약점 수정 이 페이지는 Claude Code CHANGELOG를 기반으로 주요 변경사항만 발췌하여 정리한 것입니다. 전체 내용은 원본을 참고하세요.\n","permalink":"https://geonhos.github.io/posts/claude-code-changelog-summary/","summary":"Claude Code의 주요 업데이트를 버전별로 핵심만 추려 정리합니다. 모델 업그레이드, 신규 기능, 플랫폼 지원 등 메이저 변경사항을 한눈에 볼 수 있습니다. (v2.1.97 기준)","title":"Claude Code 업데이트 핵심 정리 (v1.0 ~ v2.1.97)"},{"content":" Ollama 성능 시리즈 — 로컬 LLM을 프로덕션에 올리기 위해 알아야 할 것들을 실측 데이터로 정리합니다.\n메모리 관리 — 모델 크기별 리소스 점유와 최적화 Cold Start — 내부 동작부터 해결까지 (현재) 동시 처리 — 병렬 슬롯, 큐잉, 그리고 처리량 들어가며 1편에서는 모델 크기별 메모리 점유를, 2편에서는 Cold Start의 원인과 해결을 분석했습니다. 2편에서 Cold Start를 해결하여 모델을 상시 Warm 상태로 유지할 수 있게 됐습니다. 그렇다면 이 Warm 상태의 모델에 여러 사용자가 동시에 요청을 보내면 어떻게 될까요?\n이번 마지막 글에서는 실서비스에서 가장 중요한 질문에 답합니다:\n\u0026ldquo;Ollama에 동시에 여러 요청이 들어오면 어떻게 되는가?\u0026rdquo;\n챗봇, API 서비스, 에이전트 시스템 등 다중 사용자 환경에서 Ollama를 운영할 때 동시 요청 처리 능력이 서비스 품질을 결정합니다. OLLAMA_NUM_PARALLEL 설정에 따라 처리량(throughput)과 응답 지연(latency)이 어떻게 달라지는지 실측 데이터로 확인합니다.\n이 글에서 다루는 내용:\nOllama의 동시 요청 처리 아키텍처 (병렬 슬롯 + Continuous Batching) OLLAMA_NUM_PARALLEL 값별 성능 벤치마크 (1, 2, 4) 동시 요청 수(1~8)별 throughput 및 TTFT 실측 큐잉 메커니즘과 공정성(Fairness) 분석 프로덕션 동시 처리 최적화 가이드 1. Ollama의 동시 요청 처리 아키텍처 1.1 llama.cpp 백엔드와 병렬 슬롯 Ollama는 내부적으로 llama.cpp의 서버 기능을 활용하여 동시 요청을 처리합니다:\n클라이언트 요청 1 ─┐ 클라이언트 요청 2 ──┤ ┌─ Slot 0: KV Cache ──→ Token Gen 클라이언트 요청 3 ──┼→ Ollama 스케줄러 →─┤ 클라이언트 요청 4 ──┤ └─ Slot 1: KV Cache ──→ Token Gen 클라이언트 요청 5 ─┘ ↑ (모델 파라미터 공유) 슬롯 부족 시 → 내부 큐 대기 핵심 개념:\n병렬 슬롯(Parallel Slot): 하나의 모델 인스턴스에서 여러 요청을 동시 처리하는 단위 Continuous Batching: 모델 파라미터는 공유하되, 여러 슬롯의 토큰을 하나의 forward pass 배치로 묶어 GPU 연산을 병렬 수행 KV Cache: 각 슬롯이 독립적인 KV Cache를 할당받음 중요: Continuous Batching은 단일 forward pass 내에서 여러 슬롯의 토큰을 동시 처리하지만, GPU 연산 자원과 메모리 대역폭을 공유하므로 \u0026ldquo;완벽한 병렬화\u0026quot;는 아닙니다. 슬롯이 늘어나면 전체 throughput은 증가하지만, 개별 요청의 tokens/s는 감소하며, 슬롯이 일정 수를 초과하면 TBT가 증가하여 사용자 체감 응답 속도가 느려질 수 있습니다.\n1.2 OLLAMA_NUM_PARALLEL 환경변수 # 병렬 슬롯 수 설정 (서버 시작 시) OLLAMA_NUM_PARALLEL=4 ollama serve 설정 의미 KV Cache 메모리 1 (기본값, v0.17.4 기준) 한 번에 1개 요청 처리 모델 메모리 + KV Cache × 1 2 2개 요청 동시 처리 모델 메모리 + KV Cache × 2 4 4개 요청 동시 처리 모델 메모리 + KV Cache × 4 KV Cache 메모리 계산 (1편 실측 데이터 기반):\nllama3.2:3b 모델 파라미터: ~5.8 GB RSS KV Cache 증가분 (num_ctx 2048 추가 시): 슬롯당 약 224 MB 추가 할당 (1편 실측: num_ctx 2048→4096 증가분 기준) 전체 메모리 ≈ 모델 파라미터 + (KV Cache per slot × NUM_PARALLEL) 참고: Ollama 최신 버전에서는 하드웨어 가속기(VRAM) 크기에 따라 NUM_PARALLEL 기본값이 자동 조정될 수 있습니다. 명시적으로 설정하지 않은 경우 ollama serve 로그에서 실제 적용된 값을 확인하십시오.\nKV Cache 224 MB의 산출 근거: KV Cache 크기는 이론적으로 2 × num_layers × num_kv_heads × head_dim × num_ctx × sizeof(dtype)로 계산됩니다. llama3.2:3b의 경우 num_layers=28, num_kv_heads=8, head_dim=128, Q4_K_M 양자화 시 실효 dtype ≈ fp16 (KV Cache는 양자화와 무관하게 fp16 유지)이므로, num_ctx=2048일 때 이론값은 2 × 28 × 8 × 128 × 2048 × 2 bytes ≈ 234 MB입니다. 1편에서 실측한 224 MB와 거의 일치하며, 소폭 차이는 llama.cpp의 메모리 정렬 및 할당 최적화에 기인합니다.\n1.3 큐잉 메커니즘 동시 요청이 병렬 슬롯 수를 초과하면 내부 큐가 작동합니다:\n[요청 도착] ↓ [슬롯 확인] ─→ 여유 슬롯 있음 → 즉시 처리 │ └───→ 슬롯 부족 → 내부 큐 대기 ↓ [슬롯 해제 시] ↓ 큐에서 꺼내 처리 (FCFS) NUM_PARALLEL=1, 동시 요청 4개: 1개 처리 + 3개 큐 대기 NUM_PARALLEL=4, 동시 요청 4개: 4개 모두 즉시 처리 큐 대기 시간이 TTFT에 직접적으로 추가됩니다.\n프로덕션 참고: Ollama 내부 큐의 최대 깊이는 공식 문서에 명시되지 않으며, 과부하 시 요청이 타임아웃될 수 있습니다. 프로덕션에서는 애플리케이션 레이어에서 별도의 요청 큐와 타임아웃을 구현하는 것을 권장합니다.\n1.4 OLLAMA_NUM_PARALLEL vs OLLAMA_MAX_LOADED_MODELS 설정 범위 목적 OLLAMA_NUM_PARALLEL 단일 모델 내 \u0026ldquo;한 모델이 동시에 몇 개 요청을 처리하느냐\u0026rdquo; OLLAMA_MAX_LOADED_MODELS 모델 간 \u0026ldquo;몇 개 모델을 메모리에 유지하느냐\u0026rdquo; (1편 참조) 2. 동시 요청 성능 벤치마크 (실측) 2.1 테스트 환경 및 방법론 항목 내용 OS macOS (Apple Silicon, arm64) 메모리 48 GB 가속기 Metal (통합 메모리) Ollama v0.17.4 모델 llama3.2:3b (Q4_K_M, 디스크 2.0GB / 런타임 RSS ~5.8GB) num_ctx 2048 (고정) 프롬프트 \u0026ldquo;Explain the concept of parallel processing in computing in 3 sentences.\u0026rdquo; 동시 요청 수 1, 2, 4, 8 NUM_PARALLEL 1, 2, 4 (서버 재시작하여 각각 측정) 반복 시나리오당 3회 방법 Python asyncio + aiohttp 동시 스트리밍 요청 통계적 한계 고지: 시나리오당 3회 반복은 탐색적 분석 수준입니다. 트렌드 파악은 가능하나, 정밀 퍼센타일 분석에는 부적합합니다.\n측정 방법: Warm Start 상태를 보장한 뒤 (2편에서 검증된 방법), asyncio.gather()로 N개 요청을 동시 발송합니다.\n2.2 동시 요청 수별 성능 비교 (NUM_PARALLEL=1) NUM_PARALLEL=1 (v0.17.4 기본값)에서의 동시 요청 결과:\n동시 요청 Throughput (tokens/s) Avg TTFT (ms) Max TTFT (ms) 1 85 115 115 2 83 663 1,207 4 80 1,678 3,480 8 80 4,066 8,175 핵심 발견:\nThroughput은 거의 일정 (~80-85 tokens/s): 슬롯이 1개뿐이므로 동시 요청이 늘어도 한 번에 1개씩만 처리. 전체 처리량은 변하지 않음 TTFT는 동시 요청 수에 선형 비례: 큐에서 대기하므로 TTFT = 자기 앞 요청들의 처리 시간 합 8개 동시 요청 시 마지막 요청의 TTFT는 8.2초: 사용자 체감상 매우 느림 2편에서 측정한 Warm Start TTFT(81ms)와 비교하면, concurrent=1 시 TTFT(115ms)와 유사하여 baseline이 일치합니다.\n2.3 OLLAMA_NUM_PARALLEL 값별 성능 비교 서버를 NUM_PARALLEL=1, 2, 4로 재시작하며 측정한 결과:\nNUM_PARALLEL Concurrent Throughput (tokens/s) Avg TTFT (ms) Max TTFT (ms) 1 1 85 115 115 1 2 83 663 1,207 1 4 80 1,678 3,480 1 8 80 4,066 8,175 2 1 85 103 103 2 2 107 114 128 2 4 108 1,130 2,216 2 8 109 2,402 4,844 4 1 88 82 82 4 2 102 138 151 4 4 99 145 172 4 8 97 1,843 3,935 핵심 발견:\nNUM_PARALLEL 증가 시 throughput 향상: concurrent=4 기준으로, 1→2 슬롯에서 80→108 tokens/s (35% 향상)이 가장 효과적. 2→4에서는 108→99 tokens/s로 오히려 소폭 감소 수확 체감(Diminishing Returns): 슬롯 수를 2 이상으로 늘려도 throughput 증가폭이 급격히 줄어듦. Apple Silicon 통합 메모리 환경에서 메모리 대역폭 병목이 주 원인 왜 2 슬롯에서 throughput이 증가하고, 4 슬롯에서는 오히려 감소하는가?\nContinuous Batching은 여러 슬롯의 토큰 생성을 하나의 GPU 배치로 묶어 처리합니다. 슬롯이 1→2로 늘어나면 GPU 연산 유닛의 유휴 시간이 줄어들어 전체 throughput이 증가합니다 (85→107 tokens/s). 그러나 슬롯이 4개로 늘어나면 두 가지 병목이 발생합니다. 첫째, 각 슬롯이 독립적인 KV Cache를 유지하므로 전체 KV Cache 크기가 슬롯 수에 비례하여 증가합니다. 둘째, Apple Silicon의 통합 메모리 아키텍처에서는 CPU와 GPU가 메모리 대역폭을 공유하기 때문에, 4개 슬롯의 KV Cache를 동시에 읽고 쓰는 과정에서 메모리 대역폭 경합이 심화됩니다. 결과적으로 개별 슬롯의 처리 효율이 저하되어 전체 throughput이 107→99 tokens/s로 소폭 감소하는 현상이 나타납니다. 3. TTFT 극적 개선: NUM_PARALLEL=4, concurrent=4일 때 TTFT 1,678ms→145ms (11.6배 개선) 4. 슬롯 \u0026lt; 동시 요청 시에만 큐잉 발생: NUM_PARALLEL=4에서 concurrent=4까지는 큐잉 없음, concurrent=8에서 큐잉 시작\n2.4 요청 간 공정성(Fairness) 분석 동시에 보낸 요청들이 얼마나 공평하게 처리되는지 분석합니다.\nNUM_PARALLEL=1, concurrent=4 시 요청별 TTFT 분포:\n요청 순서 처리 상태 TTFT (ms) 대기 원인 요청 1 즉시 슬롯 배정 ~115 (실측) 없음 (즉시 처리) 요청 2 큐 대기 1번째 ~1,207 (실측) 요청 1 처리 완료 대기 요청 3 큐 대기 2번째 ~2,340 (순차 큐잉 추정) 요청 1, 2 처리 완료 대기 요청 4 큐 대기 3번째 ~3,480 (실측 Max TTFT) 요청 1, 2, 3 처리 완료 대기 TTFT가 요청 순서에 따라 선형으로 증가하며, 마지막 요청은 첫 번째 대비 약 30배의 지연을 겪습니다. 요청 3의 값은 요청 1(115ms)과 요청 2(1,207ms)의 패턴으로부터 선형 보간한 추정치이며, 요청 1·2·4는 실측값입니다.\nNUM_PARALLEL=1, concurrent=4 시:\n첫 번째 슬롯에 배정된 요청: TTFT ~115ms 마지막 큐 대기 요청: TTFT ~3,480ms (30배 차이) 스케줄링 방식: FCFS — 먼저 도착한 요청이 우선 처리 NUM_PARALLEL=4, concurrent=4 시:\n모든 요청이 즉시 슬롯 배정 TTFT 편차: 82~172ms (2배 이내) 공정성이 크게 개선됨 2.5 Latency vs Throughput 트레이드오프 NUM_PARALLEL=1: throughput이 일정하지만 latency가 급증 — 단일 사용자에 최적 NUM_PARALLEL=2: 2개 동시 요청까지 최적 지점 — 소규모 서비스에 적합 NUM_PARALLEL=4: 4개 동시 요청까지 최적 — 중규모 서비스에 적합 최적 지점: 동시 요청 수 ≤ NUM_PARALLEL인 구간 위 boxplot은 NUM_PARALLEL 및 동시 요청 수 조합별 응답 시간 분포를 보여줍니다. NUM_PARALLEL=1에서 동시 요청이 늘어날수록 응답 시간의 분산이 급격히 커지는 반면, NUM_PARALLEL=4에서는 4개 동시 요청까지 분산이 최소화됩니다.\n2.6 실측 데이터 교차 검증 2편 Warm Start baseline 대조:\n2편 Warm Start TTFT: 81.3ms 3편 concurrent=1 TTFT: 82~115ms 일치도: 양호 (aiohttp 오버헤드 ~30ms) tokens/s 대조:\n2편 Warm Start: 98.6 tokens/s (직접 HTTP 클라이언트, 짧은 프롬프트) 3편 concurrent=1: 85~88 tokens/s (aiohttp 스트리밍, 긴 프롬프트) 차이 원인: 측정 도구(직접 HTTP vs aiohttp)와 프롬프트 길이(2문장 vs 3문장) 모두 다르므로 직접 비교는 제한적입니다. 두 값이 85~99 tokens/s 범위 내에 있다는 점에서 동일 모델·동일 환경임을 확인하는 참고 수준으로 해석하십시오. 아래 요약 테이블은 NUM_PARALLEL 값별 주요 성능 지표를 한눈에 비교합니다.\n3. 프로덕션 동시 처리 최적화 가이드 3.1 OLLAMA_NUM_PARALLEL 설정 가이드 설정 적합 상황 메모리 추가 비용 1 (기본) 단일 사용자, 최저 latency 우선 없음 2 소규모 서비스 (2-3명 동시 사용) KV Cache × 1 추가 4 중규모 서비스, throughput 중시 KV Cache × 3 추가 8+ 대규모 서비스 (충분한 메모리 + GPU 필요) KV Cache × 7+ 추가 ⚠️ 주의: NUM_PARALLEL을 높이면 슬롯당 KV Cache가 추가 할당됩니다. llama3.2:3b (num_ctx=2048) 기준으로 NUM_PARALLEL=1일 때 메모리 ~5.8 GB, NUM_PARALLEL=2일 때 ~6.0 GB (+224 MB), NUM_PARALLEL=4일 때 ~6.5 GB (+672 MB)입니다. 대형 모델이나 긴 컨텍스트(num_ctx 8192+)에서는 슬롯당 수 GB가 추가될 수 있으므로, 반드시 ollama ps로 실제 메모리 점유를 확인한 뒤 설정하십시오.\n권장 공식:\n최대 NUM_PARALLEL = (가용 메모리 - 모델 메모리) / KV Cache per slot llama3.2:3b 예시 (1편 실측 기반):\n모델 메모리: ~5.8 GB 가용 메모리: 48 GB 이론상 최대: (48 - 5.8) / KV Cache ≈ 다수 슬롯 가능 실측 기반 권장: 2-4 슬롯 (throughput 수확 체감으로 4 이상 효과 미미) num_ctx와 메모리 비용: 본 벤치마크는 num_ctx=2048 기준입니다. 컨텍스트 길이가 4배(8192)가 되면 슬롯당 KV Cache 비용도 약 4배(~896 MB)로 증가합니다. llama3.1(128k) 등 대형 컨텍스트 모델에서 NUM_PARALLEL을 높이면 슬롯당 수 GB가 추가되어 OOM 위험이 있으므로, KV Cache per slot은 1.2절의 공식으로 추정하거나 ollama ps로 메모리 증가분을 실측하는 것이 가장 정확합니다.\n3.2 부하 분산 전략 전략 장점 단점 스케일업 (NUM_PARALLEL 증가) 구성 간단, 모델 메모리 공유 수확 체감, 단일 장애점 스케일아웃 (멀티 인스턴스 + LB) 선형 확장, 장애 격리 모델별 메모리 중복, 인프라 복잡 하이브리드 최적 균형 가장 복잡 권장: NUM_PARALLEL=2-4로 스케일업 후, 부하 초과 시 인스턴스 추가 (스케일아웃)\n3.3 모니터링 지표 지표 임계값 의미 Avg TTFT \u0026gt; 2초 사용자 체감 지연 시작 Max TTFT \u0026gt; 5초 일부 사용자에게 심각한 지연 Queue Wait \u0026gt; 0ms 슬롯 부족, NUM_PARALLEL 증가 검토 Throughput 감소 이전 대비 20%+ 감소 시스템 과부하 # 현재 모델 상태 확인 curl -s http://localhost:11434/api/ps | python3 -c \u0026#34; import sys, json data = json.load(sys.stdin) for m in data.get(\u0026#39;models\u0026#39;, []): print(f\\\u0026#34;{m[\u0026#39;name\u0026#39;]}: VRAM {m.get(\u0026#39;size_vram\u0026#39;,0)/1024/1024:.0f}MB\\\u0026#34;) \u0026#34; 4. 마치며 핵심 정리 NUM_PARALLEL은 throughput과 latency의 핵심 제어 변수. 기본값 1 (v0.17.4 기준)은 단일 사용자에만 적합 1→2 슬롯에서 throughput 35% 향상 (concurrent=4 기준, 80→108 tokens/s)이 가장 효과적. 4 이상에서는 메모리 대역폭 경합으로 수확 체감 슬롯 \u0026lt; 동시 요청 시 큐잉 발생, TTFT가 수 초~수십 초로 증가 최적 설정: 동시 사용자 수 ≤ NUM_PARALLEL을 유지하면 모든 요청이 큐잉 없이 즉시 처리 시리즈 전체 요약 주제 핵심 발견 핵심 해결책 1편 — 메모리 관리 llama3.2:3b → 5.8GB RSS, num_ctx 2배 시 +224MB 모델 크기/양자화 선택, MAX_LOADED_MODELS 2편 — Cold Start TTFT 881ms(Cold) vs 81ms(Warm), 10.8배 차이 keep_alive 연장, 프리로드 + 헬스체크 3편 — 동시 처리 NUM_PARALLEL 1→2: throughput 35%↑ (concurrent=4 기준), TTFT 663→114ms NUM_PARALLEL=2 (소규모) ~ 4 (중규모), 초과 시 스케일아웃 이 세 가지 요소 — 메모리, Cold Start, 동시 처리 — 를 함께 최적화하면 Ollama를 프로덕션에서 안정적으로 운영할 수 있습니다.\n참고 자료 Ollama API Documentation Ollama FAQ llama.cpp Server — Parallel Decoding ","permalink":"https://geonhos.github.io/posts/ollama-concurrent-processing/","summary":"Ollama에 동시에 여러 요청이 들어오면 어떻게 되는가? OLLAMA_NUM_PARALLEL 설정별 throughput과 TTFT를 실측 데이터로 비교하고, 프로덕션 동시 처리 최적화 전략을 정리합니다.","title":"Ollama 동시 요청 처리의 이해와 최적화 (3)"},{"content":" Ollama 성능 시리즈 — 로컬 LLM을 프로덕션에 올리기 위해 알아야 할 것들을 실측 데이터로 정리합니다.\n메모리 관리 — 모델 크기별 리소스 점유와 최적화 (현재) Cold Start — 내부 동작부터 해결까지 동시 처리 — 병렬 슬롯, 큐잉, 그리고 처리량 들어가며 1편에서는 Ollama의 모델 크기별 메모리 점유를 직접 측정했습니다. 이번 글에서는 Ollama를 실서비스에 도입할 때 첫 번째로 부딪히는 문제 — Cold Start를 파헤칩니다.\n첫 요청에 수백 밀리초~수 초(대형 모델/HDD 환경에서는 수십 초 이상)가 걸리는 이유가 무엇인지, 내부 파이프라인 수준에서 분석하고, 실측 데이터로 그 차이를 확인한 뒤, 해결 방법까지 정리합니다.\n이 글에서 다루는 내용:\nOllama의 요청 처리 파이프라인 (Cold vs Warm 경로) Cold Start vs Warm Start 실측 벤치마크 (5회 반복) keep_alive, 프리로드, 헬스체크를 통한 Cold Start 해소 프로덕션 환경 적용 시 주의사항 1. Ollama는 프롬프트를 어떻게 처리하는가 요청 수신부터 응답까지의 전체 흐름 Ollama의 /api/generate 엔드포인트에 요청이 도달하면 내부적으로 다음 파이프라인을 거칩니다:\nflowchart TD A[\"API 요청 수신\"] --\u003e B{\"스케줄러:모델 로드 상태 확인\"} B --\u003e|\"미로드\"| C[\"GGUF 파일 메모리 로드(= Cold Start)\"] C --\u003e C1[\"디스크 → RAM → GPU/Metal 복사\"] C1 --\u003e C2[\"load_duration 기록\"] C2 --\u003e D B --\u003e|\"로드됨\"| D[\"Prompt Evaluation(prompt_eval_duration)\"] D --\u003e D1[\"입력 토큰 임베딩 + KV Cache 초기화\"] D1 --\u003e E[\"Token Generation(eval_duration)\"] E --\u003e E1[\"Auto-regressive 토큰 생성(이전 토큰을 참고해 다음 토큰을 하나씩 생성)스트리밍 응답 전송\"] E1 --\u003e F[\"완료 (done: true + 메트릭 포함)\"] Cold Start vs Warm Start: 차이가 발생하는 지점 구간 Cold Start Warm Start load_duration GGUF 파일 메모리 매핑 + 초기 페이지 로드 이미 메모리에 있으므로 대폭 감소 (실측 ~71ms) prompt_eval_duration KV Cache 초기 할당 포함 할당 완료 상태에서 실행 eval_duration 차이 없음 (순수 추론) 차이 없음 핵심은 load_duration입니다. 1편에서 측정한 것처럼 llama3.2:3b 모델은 약 5.8GB 메모리를 점유합니다. Cold Start 시에는 이 전체를 디스크에서 읽어야 합니다.\nOllama 스케줄러와 모델 생명주기 Ollama는 keep_alive 타이머로 모델 생명주기를 관리합니다:\nflowchart LR A[\"요청\"] --\u003e B[\"모델 로드\"] --\u003e C[\"추론\"] --\u003e D[\"keep_alive타이머 시작\"] D --\u003e E[\"5분 경과(기본값)\"] E --\u003e F[\"모델 언로드\"] F --\u003e G[\"다음 요청= Cold Start\"] 기본값: 5분 (OLLAMA_KEEP_ALIVE=5m) 다중 모델 환경에서는 OLLAMA_MAX_LOADED_MODELS와 함께 메모리 기반 스케줄링이 적용됩니다 (1편 참조) API 응답 메트릭 해석 Ollama API의 최종 응답(done: true)에는 상세 메트릭이 포함됩니다:\n메트릭 단위 의미 load_duration ns 모델 로드 시간 (Cold Start의 핵심) prompt_eval_duration ns 프롬프트 평가 시간 prompt_eval_count 개 입력 토큰 수 eval_duration ns 토큰 생성 시간 eval_count 개 생성된 토큰 수 total_duration ns 전체 소요 시간 핵심 지표 계산:\nTTFT(첫 토큰 응답 시간) = load_duration + prompt_eval_duration tokens/s(토큰 생성 속도) = eval_count / (eval_duration / 1e9) 주의: 모든 시간 값은 나노초(ns) 단위입니다. ms로 변환하려면 ÷ 1,000,000을 적용합니다.\n2. Cold Start가 왜 문제인가 실서비스에서 Cold Start가 문제가 되는 대표적인 시나리오:\nUX 저하: 사용자가 첫 요청에 수백ms~수십초를 기다려야 함 SLA 위반: API 응답 시간 보장 실패 헬스체크 실패: 컨테이너 오케스트레이터가 Cold Start 지연을 장애로 인식 → 재시작 루프 반복적 발생: keep_alive 기본값(5분) 이후 매번 Cold Start 재발생 스케일링 지연: 새 인스턴스 추가 시 초기 응답 지연 3. 실제로 얼마나 차이가 나는가 (벤치마크) 테스트 환경 항목 내용 OS macOS (Apple Silicon, arm64) 메모리 48 GB 가속기 Metal (통합 메모리) Ollama v0.17.4 모델 llama3.2:3b (Q4_K_M, 2.0GB) 프롬프트 \u0026ldquo;Explain what a cold start is in 2 sentences.\u0026rdquo; 반복 5회 (각 반복 전 모델 완전 언로드 / 로드 유지) 통계적 한계 고지: 5회 반복은 탐색적 분석 수준입니다. 정밀한 통계적 유의성을 보장하기 위해서는 30회 이상의 반복이 권장됩니다.\nCold Start vs Warm Start 성능 비교 메트릭 Cold Start (평균) Warm Start (평균) 배율 Load Duration 792.1 ms 70.7 ms 11.2x TTFT 881.1 ms 81.3 ms 10.8x Total Response 1,812.0 ms 826.3 ms 2.2x tokens/s 92.4 98.6 0.9x 핵심 발견:\nLoad Duration이 11.2배 차이납니다. Cold Start 시 ~792ms, Warm에서는 ~71ms로 모델이 이미 메모리에 있는지 여부가 결정적 TTFT는 10.8배 차이. 사용자 체감으로는 Cold Start 시 약 0.9초, Warm에서는 0.08초 순수 추론 속도(tokens/s)는 거의 동일 (92.4 vs 98.6). 모델이 메모리에 올라간 후의 성능은 동일 전체 응답 시간은 2.2배 차이. load_duration이 전체의 ~44%를 차지 반복별 안정성 분석 Cold Start와 Warm Start 모두 반복 간 안정적인 결과를 보였습니다:\n메트릭 Cold Stddev Warm Stddev Load Duration 29.3 ms 10.8 ms TTFT 27.4 ms 10.7 ms Total Response 152.4 ms 70.3 ms tokens/s 2.0 2.0 Cold Start의 편차가 더 크지만(stddev 29ms vs 11ms), Apple Silicon SSD의 빠른 읽기 속도 덕분에 편차는 비교적 작은 편입니다. HDD 환경에서는 이 편차가 크게 증가할 수 있습니다.\n요약 테이블 4. Cold Start 해결 방법 OLLAMA_KEEP_ALIVE 설정 가장 간단한 해결책은 모델 유지 시간을 늘리는 것입니다.\n환경변수 방식 (서버 전체 적용):\n# 24시간 유지 — 업무 시간 동안 cold start 방지 export OLLAMA_KEEP_ALIVE=24h # 영구 유지 — 메모리가 충분할 때 export OLLAMA_KEEP_ALIVE=-1 # 기본값: 5m API 파라미터 방식 (요청별 설정):\ncurl http://localhost:11434/api/generate -d \u0026#39;{ \u0026#34;model\u0026#34;: \u0026#34;llama3.2:3b\u0026#34;, \u0026#34;prompt\u0026#34;: \u0026#34;Hello\u0026#34;, \u0026#34;keep_alive\u0026#34;: \u0026#34;24h\u0026#34; }\u0026#39; 설정 장점 단점 5m (기본값) 메모리 자동 회수 5분 간격으로 Cold Start 반복 24h 업무 시간 내 Warm 유지 미사용 시에도 메모리 점유 -1 (영구) Cold Start 완전 제거 서버 재시작 전까지 메모리 영구 점유 0 (즉시 해제) 최소 메모리 사용 매 요청마다 Cold Start 주의: API 파라미터가 환경변수보다 우선 적용됩니다. GitHub Issue #5272에서 일부 버전의 keep_alive 버그가 보고되었습니다.\n모델 프리로드 (Warm-up) 서버 시작 직후 빈 요청으로 모델을 메모리에 미리 로드(프리로드)합니다:\n# 프리로드 (빈 프롬프트 + 영구 유지) curl http://localhost:11434/api/generate -d \u0026#39;{ \u0026#34;model\u0026#34;: \u0026#34;llama3.2:3b\u0026#34;, \u0026#34;prompt\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;keep_alive\u0026#34;: \u0026#34;-1\u0026#34; }\u0026#39; # 로드 확인 curl http://localhost:11434/api/ps 자동화 예시:\n# Docker Compose — 서비스 시작 후 자동 프리로드 services: ollama: image: ollama/ollama healthcheck: test: curl -f http://localhost:11434/ || exit 1 warmup: depends_on: ollama: condition: service_healthy command: \u0026gt; curl http://ollama:11434/api/generate -d \u0026#39;{\u0026#34;model\u0026#34;:\u0026#34;llama3.2:3b\u0026#34;,\u0026#34;prompt\u0026#34;:\u0026#34;\u0026#34;,\u0026#34;keep_alive\u0026#34;:\u0026#34;-1\u0026#34;}\u0026#39; # systemd — ExecStartPost로 프리로드 [Service] ExecStart=/usr/local/bin/ollama serve ExecStartPost=/usr/local/bin/ollama-preload.sh 헬스체크 및 자동 복구 프로덕션에서는 모델이 실제로 메모리에 로드되어 있는지 확인해야 합니다:\n#!/bin/bash # 1. 서버 생존 확인 curl -s http://localhost:11434/ | grep -q \u0026#34;Ollama is running\u0026#34; # 2. 모델 다운로드 확인 (GET /api/tags) # → 디스크에 파일이 있는지 확인 curl -s http://localhost:11434/api/tags # 3. 모델 메모리 로드 확인 (GET /api/ps) ← 핵심! # → 실제 GPU/RAM에 올라와 있는지 확인 curl -s http://localhost:11434/api/ps # 4. 미로드 시 자동 프리로드 주의: /api/tags는 다운로드 여부만 확인합니다. 메모리 로드 상태는 반드시 /api/ps로 확인해야 합니다.\n해결 방법 비교표 방법 효과 복잡도 메모리 비용 적합한 환경 keep_alive 연장 Cold Start 주기 감소 낮음 중간 개발/소규모 프리로드 + keep_alive=-1 Cold Start 완전 제거 중간 높음 단일 모델 서비스 헬스체크 + 자동 복구 장애 자동 대응 중간 중간 프로덕션 전체 조합 최고 안정성 높음 높음 엔터프라이즈 5. 적용 시 주의사항 메모리 트레이드오프 keep_alive=-1은 Cold Start를 완전히 제거하지만, 모델이 메모리를 영구 점유합니다:\n모델 메모리 점유 keep_alive=-1 시 llama3.2:1b ~2.5 GB RSS 상시 2.5 GB 점유 llama3.2:3b ~5.8 GB RSS 상시 5.8 GB 점유 llama3.1:8b ~9.0 GB RSS 상시 9.0 GB 점유 (1편 실측 데이터 참조)\n다중 모델을 동시에 keep_alive=-1로 유지하면 메모리 합산이 급격히 증가합니다.\nkeep_alive 우선순위 API 파라미터 keep_alive \u0026gt; 환경변수 OLLAMA_KEEP_ALIVE \u0026gt; 기본값 5m 환경변수로 24h를 설정하더라도, API 요청에서 \u0026quot;keep_alive\u0026quot;: \u0026quot;5m\u0026quot;을 보내면 해당 모델은 5분 후 언로드됩니다.\nApple Silicon에서의 특이사항 Apple Silicon(Metal)은 통합 메모리 아키텍처를 사용합니다:\nGPU 전용 VRAM이 없어 시스템 메모리를 공유 UMA 덕분에 CPU→GPU VRAM 별도 복사가 불필요하여 로드 과정이 단순 SSD 속도가 빨라 Cold Start가 상대적으로 짧음 (우리 측정: ~792ms) x86 + 외장 GPU 환경에서는 PCIe 병목(CPU↔GPU 간 데이터 전송 경로가 좁음)으로 Cold Start가 더 길어질 수 있음 6. 마치며 핵심 정리 항목 결과 Cold Start 원인 load_duration (전체 응답의 44%) TTFT 차이 Cold 881ms vs Warm 81ms (10.8x) 추론 속도 차이 거의 없음 (~7%) 가장 쉬운 해결책 keep_alive 연장 (24h / -1) 프로덕션 표준 프리로드 + 헬스체크 조합 다음 편 예고 3편: Ollama 동시 요청 처리의 이해와 최적화 — OLLAMA_NUM_PARALLEL과 Continuous Batching, 동시 요청 수별 성능 실측, 대기열 관리와 스케줄링 최적화 참고 자료 Ollama API Documentation Ollama FAQ — keep_alive GitHub Issue #5272 — keep_alive not effective ","permalink":"https://geonhos.github.io/posts/ollama-cold-start/","summary":"Ollama의 첫 요청에 수백 밀리초~수 초(환경에 따라 수십 초 이상)가 걸리는 이유를 내부 파이프라인 수준에서 분석하고, 실측 데이터로 Cold Start와 Warm Start의 차이를 확인한 뒤 해결 방법까지 정리합니다.","title":"Ollama Cold Start 완전 정복: 내부 동작부터 해결까지 (2)"},{"content":" Ollama 성능 시리즈 — 로컬 LLM을 프로덕션에 올리기 위해 알아야 할 것들을 실측 데이터로 정리합니다.\n(현재) 메모리 관리 — 모델 크기별 리소스 점유와 최적화 Cold Start — 내부 동작부터 해결까지 동시 처리 — 병렬 슬롯, 큐잉, 그리고 처리량 들어가며 업무 특성상 외부 API를 사용할 수 없는 폐쇄망 환경에서 LLM을 운영해야 하는 경우, Ollama는 가장 현실적인 선택지 중 하나입니다. 로컬에서 모델을 직접 서빙하므로 데이터가 외부로 나가지 않고, 네트워크 연결 없이도 동작합니다.\n이때 가장 먼저 마주치는 질문이 있습니다 — \u0026ldquo;이 모델을 돌리려면 메모리가 얼마나 필요할까?\u0026rdquo;\n모델 파일 크기와 실제 메모리 점유량은 같지 않습니다. KV Cache와 런타임 오버헤드 때문에 실제 VRAM은 파일의 2~4배입니다. 이 구조를 이해하면, 왜 4.58GB짜리 모델이 10GB 넘는 메모리를 차지하는지 자연스럽게 따라옵니다.\n이 글의 배경이 되는 GPU/메모리 대역폭 개념은 LLM은 왜 GPU가 필요한가에서 다룹니다.\n이 글에서 다루는 내용:\n모델 크기별(1B/3B/8B) 실제 메모리 점유량 실측 양자화(Q4 vs Q8)에 따른 메모리 차이 컨텍스트 크기(num_ctx)와 KV Cache 메모리 관계 GPU/가속 코어 활용 패턴 다중 모델 동시 운영 시 메모리 관리 프로덕션 메모리 산정 가이드 1. 모델 크기별 메모리 점유량 실측 측정 환경과 방법 항목 내용 OS macOS (Apple Silicon, arm64) 메모리 48 GB 가속기 Metal (통합 메모리) Ollama v0.17.4 측정 도구 psutil (RSS), Ollama /api/ps (size_vram) 측정 방법:\n전체 모델 언로드 → 깨끗한 baseline 모델 로드 (빈 프롬프트) → RSS / VRAM 측정 추론 1회 실행 → 추론 중 RSS 측정 모델 언로드 참고: 메모리 측정은 모델별 단일 측정(N=1)으로, 반복 측정에 따른 통계적 유의성은 포함하지 않습니다. 메모리 점유는 시스템 상태에 따라 수십 MB 범위 내에서 변동될 수 있습니다. Cold Start / Warm Start별 성능 지표의 반복 측정 결과는 2편에서 확인할 수 있습니다.\n파라미터 수별 메모리 비교 모델 파라미터 양자화 파일 크기 RSS(로드) RSS(추론) VRAM(API) tokens/s llama3.2:1b 1.2B Q8_0 1.23 GB 2,554 MB 2,560 MB 4,404 MB 161 llama3.2:3b 3.2B Q4_K_M 1.88 GB 5,800 MB 5,810 MB 7,126 MB 96 llama3.1:8b 8.0B Q4_K_M 4.58 GB 9,048 MB 9,057 MB 10,644 MB 47 이사에 비유하면, 모델 파일 크기는 짐의 부피이고 실제 메모리 점유량은 짐을 풀어놓을 방의 크기입니다. 짐을 풀면 작업 공간(KV Cache), 도구(런타임 버퍼), 작업대(컴퓨트 그래프 — 연산 순서를 미리 설계해 둔 실행 계획)가 추가로 필요하니, 방은 짐보다 훨씬 커야 합니다.\n핵심 발견:\n파일 크기 ≠ 메모리 점유량: llama3.2:1b는 파일 1.23GB이지만 VRAM 4,404MB를 점유합니다. KV Cache, 런타임 버퍼, 컴퓨트 그래프 등 추가 메모리 할당 때문입니다. VRAM(API) vs RSS: Apple Silicon 통합 메모리 환경에서 /api/ps의 size_vram은 RSS보다 크게 측정됩니다. GPU 가속에 할당된 전체 메모리 영역을 포함하기 때문입니다. 로드 vs 추론 차이 미미: 추론 중 RSS 증가는 10MB 미만으로, 토큰 생성 자체의 추가 메모리 오버헤드는 적습니다. 파라미터 대비 성능: 1B→3B(2.6배)로 크기 증가 시 tokens/s는 161→96(40% 감소), 3B→8B(2.5배) 시 96→47(51% 감소). 양자화 참고 사항 위 표에서 llama3.2:1b는 Q8_0, llama3.2:3b와 llama3.1:8b는 Q4_K_M이 기본 양자화입니다. 파라미터 수가 다르므로 양자화 효과를 직접 비교할 수는 없지만, 일반적인 이론값은 다음과 같습니다.\n양자화 비트 수 FP16 대비 크기 특징 FP16 16-bit 1x (기준) 최고 품질, 크기 최대 Q8_0 8-bit ~0.5x 품질 거의 동일, 크기 절반 Q4_K_M 4-bit ~0.25x 품질 소폭 하락, 크기 1/4 같은 모델이라면 Q4는 Q8 대비 파일 크기와 메모리 점유가 약 절반으로 줄어듭니다. 프로덕션에서는 품질과 리소스 사이의 트레이드오프를 고려해야 합니다.\n참고: FP16 모델은 Ollama Hub에서 직접 제공하지 않습니다. Q4_K_M과 Q8_0이 가장 일반적인 선택지입니다.\n컨텍스트 크기와 KV Cache 그렇다면 파일 크기 외에 메모리를 가장 크게 좌우하는 요소는 무엇일까요? 바로 num_ctx(컨텍스트 윈도우 크기)입니다.\nKV Cache는 이전 대화 내용을 기억하는 메모장과 같습니다. 메모장 페이지가 많을수록(num_ctx가 클수록) 더 긴 대화를 기억할 수 있지만, 그만큼 책상 위 공간을 더 차지합니다. llama3.2:3b로 num_ctx만 변경하며 측정한 결과:\n참고: 아래 표는 KV Cache 증가분만 비교하기 위해 num_ctx를 명시적으로 지정하고 측정한 값입니다. 위 모델별 메모리 표의 llama3.2:3b RSS(5,800MB)는 Ollama 기본 num_ctx(모델 기본값)로 로드한 수치이므로, 이 표의 num_ctx=2048 baseline(2,431MB)과 차이가 있습니다.\nnum_ctx RSS (MB) VRAM (MB) VRAM 증가분 2,048 2,431 2,399 (baseline) 4,096 2,671 2,623 +224 MB 8,192 3,119 3,238 +840 MB 핵심 발견:\nnum_ctx를 2배로 늘리면 KV Cache 메모리가 약 224MB 증가 (2048→4096) num_ctx를 4배로 늘리면 약 840MB 증가 (2048→8192) 증가폭이 선형이 아닌 이유: KV Cache는 num_ctx × num_layers × head_dim × 2(K+V)로 계산되며, 메모리 정렬/패딩 오버헤드가 추가됩니다 3편 연결: OLLAMA_NUM_PARALLEL로 병렬 슬롯을 늘리면 슬롯마다 독립적으로 KV Cache가 할당됩니다. 자세한 내용은 3편 — 동시 처리에서 다룹니다.\n2. GPU/가속 코어 활용 패턴 모델이 메모리에 올라갔다면, 다음 궁금증은 \u0026ldquo;GPU를 실제로 얼마나 쓰고 있는가?\u0026ldquo;입니다.\nmacOS Metal 환경 Apple Silicon의 Metal GPU는 통합 메모리 아키텍처를 사용합니다. GPU utilization %는 직접 측정이 불가능하며, powermetrics(sudo 필요)로 전력/주파수만 간접 확인 가능합니다.\nflowchart LR A[\"IdleVRAM 0MB\"] --\u003e|\"모델 로드\"| B[\"LoadingVRAM 7,126MB\"] B --\u003e|\"프롬프트 입력\"| C[\"InferenceVRAM 7,126MB\"] C --\u003e|\"응답 완료\"| D[\"Idle (모델 상주)VRAM 7,126MB\"] 단계 VRAM (MB) CPU (%) 시스템 메모리 (MB) Idle 0 16% 23,134 Loading 7,126 18% 27,119 Inference 7,126 15% 27,238 Loading 시 시스템 메모리가 약 4GB 증가 (모델 로드) Inference 시 VRAM 변화 없음 (이미 할당된 KV Cache 내에서 연산) CPU 사용률은 크게 변하지 않음 (Metal GPU가 추론 수행) Metal GPU가 추론을 수행하는 구조와 메모리 대역폭이 tok/s 성능에 미치는 영향은 LLM은 왜 GPU가 필요한가 편에서 상세히 다룹니다.\nLinux NVIDIA 환경 (참고) Linux + NVIDIA GPU 환경에서는 nvidia-smi로 정확한 GPU utilization, memory used, temperature, power draw를 측정할 수 있습니다.\n3. 다중 모델 동시 운영 모델 하나의 메모리 특성을 파악했다면, 실제 운영에서는 한 걸음 더 나아가야 합니다 — \u0026ldquo;모델 여러 개를 동시에 올릴 수 있을까?\u0026rdquo;\nOllama의 모델 풀 관리 Ollama는 메모리가 충분하면 여러 모델을 동시에 메모리에 유지합니다. 카페에 비유하면, 자주 오는 단골(자주 사용하는 모델)의 자리를 미리 잡아두는 것과 같습니다. 새 손님이 오면 빈자리에 앉히고, 자리가 부족할 때만 가장 오래 안 온 손님(LRU)부터 자리를 비웁니다.\nflowchart TD REQ[\"새 모델 요청\"] --\u003e CHK{\"메모리에이미 로드?\"} CHK --\u003e|\"예\"| HIT[\"즉시 추론 (Warm)\"] CHK --\u003e|\"아니오\"| MEM{\"여유 메모리충분?\"} MEM --\u003e|\"충분\"| LOAD[\"모델 추가 로드 → 추론\"] MEM --\u003e|\"부족\"| LRU[\"LRU 모델 언로드\"] LRU --\u003e LOAD OLLAMA_MAX_LOADED_MODELS 설정 동작 미설정 (기본) 가용 메모리에 따라 자동 관리 OLLAMA_MAX_LOADED_MODELS=1 한 번에 1개만 로드 (즉시 swap) OLLAMA_MAX_LOADED_MODELS=2 최대 2개 동시 유지 실측: 2개 모델 동시 로드 48GB 메모리 환경에서 동시 로드 결과:\n조합 A 로드 후 RSS A+B 동시 RSS A 유지? 1b + 3b 2,564 MB 8,246 MB 예 3b + 8b 5,839 MB 14,711 MB 예 핵심 발견:\n48GB 환경에서 3b+8b 동시 로드 시 약 14.7GB 사용 — 충분히 여유 있음 모델 B를 로드해도 A가 자동 언로드되지 않음 (기본 설정) 메모리 부족 시 Ollama가 LRU 정책으로 자동 언로드 운영 전략 메모리 여유 충분 시: OLLAMA_MAX_LOADED_MODELS 미설정 — Ollama 자동 관리 메모리 제약 시: OLLAMA_MAX_LOADED_MODELS=1 — 사용 중인 모델만 메모리 유지 메모리 산정: 동시 운영할 모델들의 VRAM 합산 + 시스템 여유(2-4GB) 4. 프로덕션 메모리 최적화 모델별 메모리 특성과 다중 모델 운영 방식을 이해했으니, 이제 실제 서버 환경에서 메모리를 어떻게 산정하고 관리할지 정리합니다.\n메모리 산정 가이드 실측 데이터 기반 메모리 산정 공식:\n필요 메모리 = 모델 VRAM + KV Cache 오버헤드 + 시스템 여유 구체적 예시 (llama3.2:3b, num_ctx=2048 기준):\n모델 VRAM: ~2,400 MB (num_ctx=2048 기준) KV Cache 추가 (num_ctx 증가 시): +224MB/2048 tokens 시스템 여유: 2-4 GB 권장 최소: 6 GB 다중 모델 운영 시:\n1b + 3b 동시: 약 10 GB (여유 포함) 3b + 8b 동시: 약 18 GB (여유 포함) Docker 환경 메모리 제한 프로덕션에서는 Docker로 Ollama를 운영하는 경우가 많습니다. 컨테이너 메모리 제한을 설정할 때, 위의 실측 데이터를 기준으로 산정합니다.\n# docker-compose.yml (Apple Silicon / CPU 전용 예시) # NVIDIA GPU 환경에서는 deploy.resources.reservations.devices에 GPU 마운트 필요 # 참고: https://docs.docker.com/compose/how-tos/gpu-support/ services: ollama: image: ollama/ollama deploy: resources: limits: memory: 16G # 모델 VRAM + 여유 기반 설정 메모리 제한 시 OOM 발생 가능 → 모델 크기에 맞는 적절한 제한 설정 필요.\n모니터링 설정 # jq 사용 (간결) curl -s http://localhost:11434/api/ps | jq \u0026#39;.models[] | {name, vram_gb: (.size_vram / 1073741824 | round)}\u0026#39; # python3 사용 (jq 미설치 환경) curl -s http://localhost:11434/api/ps | python3 -c \u0026#34; import sys, json data = json.load(sys.stdin) for m in data.get(\u0026#39;models\u0026#39;, []): vram_gb = m.get(\u0026#39;size_vram\u0026#39;, 0) / 1024**3 print(f\\\u0026#34;{m[\u0026#39;name\u0026#39;]}: VRAM {vram_gb:.1f} GB\\\u0026#34;) \u0026#34; 5. 마치며 핵심 정리 항목 실측 결과 파일 크기 vs 메모리 실제 VRAM은 파일의 2-4배 num_ctx 영향 2048→8192: +840 MB (llama3.2:3b) 다중 모델 48GB에서 3b+8b 동시 로드 가능 (~15GB) 추론 추가 메모리 로드 대비 \u0026lt;10MB 증가 결국 핵심은 하나입니다 — \u0026ldquo;모델 파일 크기가 아니라, KV Cache와 런타임 오버헤드를 포함한 실제 VRAM 점유량으로 메모리를 산정해야 한다.\u0026rdquo; 파일 크기만 보고 서버를 구성하면 OOM을 피할 수 없고, 실측 데이터로 계산해야 안정적인 운영이 가능합니다.\n다음 편 예고 2편: Ollama Cold Start 완전 정복 — 모델 로딩 지연 원인과 해결 방법 3편: Ollama 동시 요청 처리의 이해와 최적화 — OLLAMA_NUM_PARALLEL, 큐잉, throughput 최적화 참고 자료 Ollama API Documentation Ollama FAQ Ollama GPU Documentation GGUF Format Specification ","permalink":"https://geonhos.github.io/posts/ollama-memory-management/","summary":"Ollama로 로컬 LLM을 운영할 때 실제 메모리가 얼마나 필요한지, 모델 크기별·양자화별·컨텍스트별 실측 데이터로 분석하고 프로덕션 메모리 산정 가이드를 정리합니다.","title":"Ollama 메모리 관리 — 모델 크기별 리소스 점유와 최적화 (1)"},{"content":"들어가며 ChatGPT나 Claude를 쓰다 보면 자연스럽게 드는 궁금증이 있습니다 — \u0026ldquo;이거 왜 이렇게 비싼 GPU가 필요하지?\u0026rdquo;\nLLM은 글자를 한 번에 만들어내는 게 아니라 한 토큰씩 순차적으로 예측합니다. 그리고 토큰 하나를 만들 때마다 모델 전체를 읽어야 합니다. 이 구조를 이해하면 왜 GPU가 필요하고, 왜 메모리 대역폭이 성능을 좌우하는지 자연스럽게 따라옵니다.\n대상 독자: \u0026ldquo;LLM이 왜 비싼 GPU가 필요한지\u0026rdquo; 궁금한 입문~중급 개발자. 토큰 생성 원리부터 시작해 하드웨어 선택까지 다루고, 클라우드 서빙은 개요 수준으로 소개합니다.\n이 글에서 다루는 내용:\n토큰 생성 원리와 모델 내부 구조 GPU 메모리에 모델을 올려야 하는 이유 메모리 대역폭이 성능을 결정하는 구조 하드웨어 아키텍처와 성능 비교 클라우드 LLM 서빙 개요 1. 토큰 생성이란 토큰이 뭔가 토큰은 LLM이 텍스트를 처리하는 최소 단위입니다. 단어 하나가 토큰 하나일 수도 있고, 긴 단어는 여러 토큰으로 쪼개지기도 합니다.\n텍스트 토큰 분리 예시 토큰 수 Hello [Hello] 1 Transformer [Trans] [former] 2 서울 [서울] 1 맛집 추천 [맛] [집] [추천] 3 한국어는 영어보다 토큰이 더 잘게 쪼개지는 경향이 있습니다. LLM의 학습 데이터에 영어 비중이 높아서 영어 단어를 더 효율적으로 인코딩하기 때문입니다. 같은 문장이라도 한국어가 더 많은 토큰을 소비하고, 이는 곧 더 많은 연산과 비용으로 이어집니다.\n한 토큰씩 순차 생성 LLM에게 질문하면, 모델은 다음에 올 토큰을 하나씩 예측합니다.\nflowchart LR Q[\"서울 맛집 추천해줘\"] --\u003e T1[\"광\"] T1 --\u003e T2[\"화\"] T2 --\u003e T3[\"문\"] T3 --\u003e T4[\"근처\"] T4 --\u003e T5[\"에\"] T5 --\u003e T6[\"...\"] ChatGPT나 Claude에서 글자가 한 글자씩 타이핑되듯 나오는 게 이 때문입니다. 한 번에 답을 만드는 게 아니라 한 토큰씩 순차적으로 생성합니다.\n토큰 하나를 어떻게 예측하나 \u0026ldquo;서울 맛집 추천해줘\u0026rdquo; 다음에 올 토큰을 예측하려면, 모델은 모든 가능한 단어 중에서 확률이 가장 높은 것을 골라야 합니다.\n다음 토큰 후보: \u0026#34;광\u0026#34; → 12.3% \u0026#34;강\u0026#34; → 8.7% \u0026#34;종\u0026#34; → 6.1% \u0026#34;을\u0026#34; → 3.2% \u0026#34;the\u0026#34; → 0.001% ... (수만 개 후보 중 확률 계산) 이 확률을 계산하는 과정이 추론(inference)이고, 여기에 행렬 곱셈이 쓰입니다.\n2. 토큰 하나 = 모델 전체 읽기 모델 내부 구조 모델 내부는 여러 레이어(layer)가 쌓인 구조입니다. 레이어란 입력 데이터를 변환하는 연산 블록으로, 각 레이어에는 어텐션과 피드포워드 연산을 수행하는 가중치(weights)가 들어 있습니다. 레이어 수는 모델 설계 시 결정하는 하이퍼파라미터로, 모델마다 다릅니다.\n모델 파라미터 레이어 수 Llama 3.1 8B 8B 32 Llama 3.1 70B 70B 80 Llama 3.1 405B 405B 126 레이어가 많을수록 더 복잡한 패턴을 학습할 수 있지만, 그만큼 가중치가 늘어나 메모리와 연산량이 커집니다.\nflowchart TD A[\"입력: 서울 맛집 추천해줘\"] --\u003e L1 L1[\"Layer 1 — ~140MB\"] --\u003e L2[\"Layer 2 — ~140MB\"] L2 --\u003e L3[\"⋮\"] L3 --\u003e L32[\"Layer 32 — ~140MB\"] L32 --\u003e B[\"출력: 광 (12.3%)\"] llama3.1:8b 기준: 32개 레이어, 합계 ~4.58GB\n토큰 하나를 예측하려면 32개 레이어를 전부 통과해야 합니다. 1층만 거쳐선 답이 안 나옵니다. 전체를 통과해야 비로소 \u0026ldquo;다음 토큰은 이거다\u0026quot;라는 확률 분포가 나옵니다.\n매 토큰마다 전체 읽기 \u0026#34;광\u0026#34; 생성 → 4.58GB 전체 읽기 (32개 레이어 통과) \u0026#34;화\u0026#34; 생성 → 4.58GB 전체 읽기 (또 32개 레이어 통과) \u0026#34;문\u0026#34; 생성 → 4.58GB 전체 읽기 (또 32개 레이어 통과) ... 100 토큰짜리 답변이면 4.58GB를 100번 읽습니다. \u0026ldquo;한 번 로드하고 끝\u0026quot;이 아닙니다.\n연산의 실체 토큰 하나를 생성할 때마다 일어나는 일:\n입력 벡터 [1×4096] × 가중치 행렬 [4096×4096] = 출력 [1×4096] ↑ 이게 \u0026#34;모델\u0026#34;의 실체 이 행렬 곱셈이 레이어 수(32개)만큼 반복됩니다. 한 번의 토큰 생성에 8B 모델 기준 수십억 번의 곱셈+덧셈이 필요하고, 이 연산들은 서로 독립적이라 병렬 처리가 가능합니다.\n3. 왜 GPU 메모리에 올리는가 CPU vs GPU 구조 CPU GPU 연산 유닛 8~24개 수천~수만 개 특성 범용, 순차 처리에 강함 단순 병렬 연산에 특화 비유 박사 4명이 논문 쓰기 공장 노동자 수천 명이 나사 조이기 Apple이 말하는 \u0026ldquo;GPU 20코어\u0026quot;와 NVIDIA의 \u0026ldquo;16,384 CUDA 코어\u0026quot;는 단위가 다릅니다. Apple의 GPU 1코어 안에는 연산 유닛(ALU)이 ~128개 들어있어서, 20코어 GPU의 실제 연산 유닛은 약 2,560개입니다. NVIDIA의 CUDA 코어는 연산 유닛 하나하나를 세는 방식이라 숫자가 훨씬 큽니다.\nLLM 추론의 핵심인 행렬 곱셈은 수천 개의 독립 연산으로 분해됩니다:\nCPU: 4096×4096 = 1,677만 번 연산, 8코어로 나눠도 코어당 ~200만 번 → 느림 GPU: 4096×4096 = 1,677만 번 연산, 수천 유닛이 동시에 처리 → 빠름 메모리 위치가 성능을 결정한다 모델 가중치가 GPU 메모리에 있어야 GPU 코어들이 바로 접근해서 연산할 수 있습니다. 가중치가 시스템 RAM에 있으면:\nflowchart LR A[\"RAM에 모델\"] --\u003e|\"PCIe 전송 ⚠️ 병목\"| B[\"GPU 연산\"] B --\u003e C[\"결과 반환\"] 매 레이어마다 RAM↔GPU 사이를 왕복하면, 전송 시간이 연산 시간보다 커집니다. 모델이 어디에 있느냐만으로 30배 차이가 나는 겁니다.\n4. 메모리 대역폭 대역폭 = 한 번에 얼마나 많이 옮길 수 있는가 용량(capacity) = 물탱크 크기 → \u0026#34;몇 리터 담을 수 있나\u0026#34; 대역폭(bandwidth) = 수도관 굵기 → \u0026#34;초당 몇 리터 흐르나\u0026#34; VRAM 24GB라는 건 물탱크가 24리터라는 뜻이고, 대역폭 1,008GB/s라는 건 수도관으로 초당 1,008GB가 흐를 수 있다는 뜻입니다.\n모델은 이미 VRAM에 올라가 있습니다. 용량은 충분합니다. 문제는 올라가 있는 데이터를 GPU 코어가 얼마나 빨리 읽어올 수 있느냐입니다.\nflowchart LR V[\"VRAM (물탱크)Model 4.58GB\"] G[\"GPU 코어 (공장)Matrix MultiplyToken Generation\"] V --\u003e|\"대역폭 (수도관)\"| G 수도관이 굵으면(대역폭 높으면) → 데이터가 빨리 도착 → 토큰 빨리 생성 수도관이 가늘면(대역폭 낮으면) → 데이터가 느리게 도착 → GPU가 놀면서 대기 대역폭을 결정하는 두 가지 대역폭 = 버스 폭 × 클럭 속도 버스 폭 = 차선 수 (한 번에 몇 대가 나란히 갈 수 있나) 클럭 속도 = 제한 속도 (차가 얼마나 빨리 달리나) 참고로 대역폭(bandwidth)과 지연시간(latency)은 다른 개념입니다. 대역폭은 초당 총 운반량(→ tok/s에 직접 영향), 지연시간은 한 건이 도착하는 시간(→ 첫 토큰까지 시간에 영향)입니다.\n5. 하드웨어 아키텍처와 성능 비교 UMA vs 분리 메모리 NVIDIA (분리 메모리 구조)\nflowchart LR RAM[\"System RAM (DDR5)RSS 측정\"] VRAM[\"GPU VRAM (GDDR6X)VRAM 측정\"] RAM --\u003e|\"PCIe\"| VRAM 물리적으로 분리된 두 메모리\nCPU용 RAM과 GPU용 VRAM이 물리적으로 분리 모델 로드 시 디스크 → RAM → PCIe → VRAM으로 복사 Apple Silicon (UMA)\nflowchart TB subgraph UM[\"Unified Memory (LPDDR)\"] CPU[\"CPU\"] \u003c--\u003e|\"복사 없이 공유\"| GPU[\"GPU (Metal)\"] end CPU와 GPU가 같은 물리 메모리 사용 모델 데이터를 GPU로 \u0026ldquo;복사\u0026quot;하는 게 아니라 같은 메모리를 양쪽에서 접근 PCIe 병목이 없어서 모델 로딩이 빠름 메모리 타입과 대역폭 — 왜 NVIDIA가 더 빠른가 NVIDIA가 빠른 이유는 \u0026ldquo;분리되어서\u0026quot;가 아니라 대역폭 최적화된 전용 메모리를 쓰기 때문입니다. 아래 스펙은 각 제조사 공식 페이지 기준입니다.\n하드웨어 VRAM 메모리 타입 버스 폭 대역폭 출처 Apple M1 Pro 통합 최대 32GB LPDDR5 256-bit 200 GB/s Apple Newsroom Apple M4 Max (40코어 GPU) 통합 최대 128GB LPDDR5X 512-bit 546 GB/s Apple 공식 NVIDIA RTX 4090 24GB GDDR6X 384-bit 1,008 GB/s NVIDIA 공식 NVIDIA RTX 5090 32GB GDDR7 512-bit 1,792 GB/s NVIDIA 공식 NVIDIA H100 SXM5 80GB HBM3 5,120-bit 3,350 GB/s NVIDIA 공식 NVIDIA H200 141GB HBM3e 5,120-bit 4,800 GB/s NVIDIA 공식 고속도로에 비유하면:\nLPDDR5 = 256차선 일반도로 (200 GB/s) GDDR6X = 384차선 고속도로 (1,008 GB/s) GDDR7 = 512차선 차세대 (1,792 GB/s) HBM3 = 5,120차선 전용도로 (3,350 GB/s) 인터커넥트 대역폭:\n인터커넥트 대역폭 출처 PCIe 4.0 x16 ~31.5 GB/s (단방향) PCI-SIG 스펙 NVLink 4.0 (H100) 900 GB/s (양방향) NVIDIA 기술 블로그 성능 비교 — llama3.1:8b (Q4_K_M, 4.58GB) 기준 주의: tok/s 수치는 대역폭(GB/s) ÷ 모델 크기(GB)로 계산한 이론 최대값입니다. 실제 성능은 오버헤드로 이론값의 70~90% 수준입니다.\n환경 대역폭 예상 tok/s 200토큰 응답 체감 NVIDIA H100 SXM5 3,350 GB/s 512~658 0.3초 즉시 NVIDIA RTX 5090 1,792 GB/s 274~352 0.7초 거의 즉시 NVIDIA RTX 4090 1,008 GB/s 150~198 1.2초 카톡 답장 수준 Apple M4 Max 546 GB/s 80~107 2.2초 잠깐 기다리면 옴 Apple M1 Pro 200 GB/s 30~40 5.7초 로딩 느낌 CPU만 (DDR5) 50 GB/s 8~10 22초 \u0026ldquo;멈춘 거 아냐?\u0026rdquo; PCIe 경유 (RAM→GPU) 31.5 GB/s 5~6 40초 ~30배 느림 같은 RTX 4090이라도 모델이 VRAM에 있으면 1.2초, RAM에 있으면 40초 — 모델이 어디에 있느냐만으로 30배 차이가 납니다.\n실측 참고: Apple Silicon(48GB 통합 메모리) 환경에서 Ollama로 llama3.1:8b를 실행한 결과, 실제 47 tok/s를 기록했습니다. 이론값(30~40 tok/s)보다 높은 이유는 통합 메모리의 Metal 가속 최적화 덕분입니다. 모델별 상세 실측 데이터는 Ollama 메모리 관리 편에서 확인할 수 있습니다.\n각자 잘하는 게 다르다 NVIDIA (RTX 4090) Apple (M4 Max) 속도 ~120 tok/s ~60 tok/s 최대 모델 크기 24GB까지만 128GB도 가능 전력 300W+ (콘센트 필수) 20~40W (배터리 OK) 가격 GPU만 250만원+ 노트북 통째로 소음 팬 소음 있음 무소음 가능 8B (4.58GB) → NVIDIA 압승 (속도 2~3배) 70B (40GB) → RTX 4090 VRAM 부족, Apple M4 Max 128GB는 가능 빠른 소형 모델 → NVIDIA / 느린 대형 모델 → Apple / 둘 다 → H100 VRAM은 추가할 수 없다 VRAM은 GPU 기판에 납땜(soldered)되어 있어서 교체/추가가 불가능합니다. RAM처럼 슬롯에 꽂는 구조가 아닙니다. 모델이 VRAM에 들어가느냐 안 들어가느냐가 하드웨어 선택 자체를 결정합니다.\n모델 크기 선택지 24GB 이내 RTX 4090 가성비 최고 24~48GB Apple M4 Max 또는 클라우드 48GB+ 클라우드(H100/H200) 또는 Apple Ultra 6. 클라우드 LLM 서빙 모델 크기의 현실 로컬: llama3.1:8b → 4.58GB (노트북에서 가능) 클라우드: Claude/GPT-4 → 수백 GB~TB, Llama 405B → 810GB (FP16) H100 한 장의 VRAM이 80GB인데, 810GB 모델을 어떻게 올릴까요? 모델 병렬화(Model Parallelism)로 GPU 여러 장에 쪼개서 올립니다.\nflowchart LR G1[\"GPU 1L1~8\"] --- G2[\"GPU 2L9~16\"] --- G3[\"GPU 3L17~24\"] --- G4[\"...\"] --- G16[\"GPU 16L121~128\"] Llama 405B (810GB) → H100 16장 x 50GB = 800GB, NVLink 4.0 (900GB/s)으로 연결\n쪼개는 방식은 크게 두 가지입니다. 텐서 병렬화는 한 레이어를 여러 GPU가 나눠서 동시에 계산하고, 파이프라인 병렬화는 레이어 그룹을 GPU별로 할당해 컨베이어 벨트처럼 흘립니다. 실제로는 둘을 조합해서 씁니다.\n대규모 서빙의 핵심 기술 수백만 명이 동시에 쓰는 서비스에서는 모델 병렬화 외에도 여러 최적화 기술이 적용됩니다.\n주요 최적화 기술 (클릭하여 펼치기) 기술 역할 Continuous Batching 수백 요청을 묶어서 GPU 활용률 극대화 PagedAttention KV Cache를 페이지 단위로 관리, 메모리 낭비 줄임 FP8 양자화 모델 크기 절반으로 줄이면서 품질 유지 Speculative Decoding 작은 모델이 초안 → 큰 모델이 검증 (속도↑) 로드 밸런서 수천 GPU 노드에 요청 분배 로컬: 1장 GPU, 8B 모델, 혼자 사용 클라우드: 수천 장 GPU, 수백B+ 모델, 수백만 명 동시 사용 → 같은 원리(행렬곱 + 메모리 대역폭)를 극한까지 스케일업 7. 마치며 핵심 정리 개념 핵심 토큰 생성 다음 단어를 하나씩 순차 예측 토큰 = 모델 전체 읽기 매 토큰마다 모든 레이어 통과, 전체 가중치 읽기 GPU에 올리는 이유 행렬 곱셈의 병렬 처리 + 메모리 접근 속도 메모리 대역폭 tok/s를 직접 결정하는 가장 중요한 지표 Apple vs NVIDIA UMA(큰 모델) vs 전용 VRAM(빠른 속도) 클라우드 서빙 모델 병렬화 + NVLink로 극한 스케일업 원리는 동일합니다 — \u0026ldquo;모델을 GPU 메모리에 올리고, 대역폭이 빠를수록 토큰이 빨리 나온다.\u0026rdquo; 로컬에서 8B 모델을 돌리든, 클라우드에서 수백B 모델을 서빙하든, 이 한 줄이 전부입니다.\n실전으로 이어가기 이 글에서 다룬 원리를 로컬 환경에서 직접 확인하고 싶다면, Ollama 성능 시리즈를 참고하세요.\nOllama 메모리 관리 — 모델 크기별 실측 VRAM 점유량, KV Cache 메모리 관계, 다중 모델 동시 운영 참고 자료 하드웨어 스펙:\nNVIDIA RTX 4090 공식 스펙 NVIDIA RTX 5090 공식 스펙 NVIDIA H100 공식 스펙 NVIDIA H200 공식 스펙 Apple M1 Pro/Max 소개 — Apple Newsroom Apple M4 Max 스펙 — Apple Support 인터커넥트:\nPCI Express 스펙 — Wikipedia NVIDIA Hopper Architecture In-Depth — NVIDIA Developer Blog ","permalink":"https://geonhos.github.io/posts/llm-serving-infrastructure/","summary":"LLM이 토큰을 하나씩 생성하는 원리, GPU 메모리에 모델을 올리는 이유, 메모리 대역폭이 성능을 결정하는 구조, 그리고 클라우드 LLM 서빙 인프라까지 — 로컬부터 GPT/Claude급 서비스까지 관통하는 핵심 개념을 정리합니다.","title":"LLM은 왜 GPU가 필요한가 — 토큰 생성부터 메모리 대역폭까지"}]