13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[태그:] GPU 가속

  • [Proxmox] Proxmox LLM Ollama 성능 측정 방법론: 재현 가능한 벤치마크

    [Proxmox] Proxmox LLM Ollama 성능 측정 방법론: 재현 가능한 벤치마크

    Proxmox LLM Ollama 환경에서 LLM 추론 성능 측정 방법론

    Proxmox LLM Ollama 조합으로 홈랩 AI 서버를 굴리다 보면, 제일 먼저 막히는 지점이 성능 자체보다 성능 해석이더라고요. 응답이 느릴 때 GPU 가속 경로가 문제인지, 모델 파일을 읽어오는 스토리지가 문제인지, 아니면 VM의 CPU 배치와 메모리 상태가 흔들리는지 한 번에 안 보입니다. 저도 처음엔 모델만 바꿔 가며 체감으로 판단했는데, 그 방식은 기록이 안 남고 원인 분리도 어렵더라고요. 이 글은 Proxmox + Ollama 조합에서 재현 가능한 LLM 성능 측정 방법을 만드는 데 집중합니다.

    핵심은 단순합니다. 총 응답 시간만 보면 거의 항상 잘못된 결론으로 갑니다. Ollama가 내려주는 load_duration, prompt_eval_duration, eval_duration을 분리하고, 같은 시점의 CPU, 메모리, 디스크, GPU 상태를 같이 봐야 합니다. 홈랩에서는 특히 이 구분이 중요합니다. 같은 하드웨어라도 다른 VM의 백업 작업, 메모리 ballooning, 스토리지 캐시 상태 때문에 결과가 꽤 쉽게 흔들리거든요.

    Proxmox 호스트와 게스트 VM, GPU 패스스루, Ollama API, 모니터링 명령 흐름을 한눈에 보는 구성도입니다.

    왜 Proxmox LLM Ollama 환경은 따로 측정해야 할까요?

    베어메탈에서 잘 나오던 수치가 Proxmox VM에서는 다르게 나오는 이유는 단순히 가상화 오버헤드 한 줄로 끝나지 않습니다. 실제로는 가상화 계층이 병목을 숨기거나 증폭하는 방식이 더 문제예요. 예를 들어 GPU 패스스루가 붙어 있어도 VM의 CPU affinity나 NUMA 배치가 어긋나면 생성 구간이 늘어질 수 있고, 모델 파일이 느린 스토리지에 있으면 첫 호출만 유난히 길어집니다. 이걸 분리하지 않으면 괜히 GPU만 탓하게 됩니다.

    • GPU Passthrough 상태: 장치가 VM에 보이는 것과 실제로 추론 가속에 쓰이는 건 다릅니다.
    • CPU affinity와 NUMA 배치: vCPU가 불리한 코어 배치에 놓이면 토큰 생성 지연이 출렁일 수 있습니다.
    • 스토리지 지연시간: 모델 교체가 잦을수록 첫 로딩 시간이 과장되기 쉽습니다.
    • ballooning, swap, 메모리 압박: 평소엔 티가 안 나다가 긴 프롬프트에서 갑자기 드러납니다.
    • Ollama 메트릭 해석 실수: 로딩 시간과 생성 시간을 한 덩어리로 보면 튜닝 방향이 엇나갑니다.

    제가 기준으로 삼는 문장은 하나입니다. 벤치마크는 순위를 매기는 작업이 아니라, 손해가 나는 구간을 특정하는 작업입니다. 이 관점으로 가면 Proxmox 위 LLM 성능 측정이 훨씬 덜 꼬입니다.

    LLM 성능 측정에서 반드시 분리해야 할 항목

    Ollama는 API 응답에 꽤 유용한 타이밍 필드를 포함합니다. 문제는 이 숫자를 읽는 방식입니다. 저는 최소한 아래 네 축을 분리하지 않으면 비교값으로 잘 안 봅니다.

    항목 의미 이 수치가 커질 때 먼저 볼 것 실무 해석
    load_duration 모델을 메모리로 올리는 시간 스토리지, 모델 상주 여부, 첫 호출 여부 첫 호출만 크면 정상 특성일 수 있지만, 반복 호출에서도 크면 적재가 반복되거나 메모리 압박이 있다는 뜻입니다.
    prompt_eval_duration 입력 프롬프트 토큰 처리 시간 프롬프트 길이, 컨텍스트 길이, CPU 상태 RAG나 긴 시스템 프롬프트를 쓰는 환경에서는 이 값이 체감 대기시간의 큰 비중을 차지할 수 있습니다.
    eval_duration 출력 토큰 생성 시간 GPU 가속, CPU 병목, 모델 크기 토큰/초 계산의 핵심 구간입니다. 장비 비교는 대부분 이 값 중심으로 보는 편이 맞습니다.
    시스템 지표 CPU, 메모리, 디스크, GPU 사용량 vmstat, iostat, nvidia-smi API 숫자만으로는 원인을 못 찍습니다. 같은 시점의 시스템 지표가 있어야 근본 원인을 좁힐 수 있습니다.

    여기서 많이 놓치는 포인트가 하나 있습니다. 짧은 프롬프트에서 빠른 모델이 실제 운영 워크로드에서도 빠르다는 보장은 없습니다. 특히 홈랩에서 RAG를 붙이거나 시스템 프롬프트가 길어지면 prompt_eval_duration이 병목으로 튀어나오는 경우가 적지 않았습니다. 그래서 저는 측정 목적을 먼저 나눕니다. 장비 비교인지, 실제 응답 대기시간 확인인지, GPU 패스스루 검증인지에 따라 봐야 할 값이 달라집니다.

    측정 전에 Proxmox 쪽에서 먼저 고정할 것들

    게스트 VM 안에서만 계측을 정교하게 해도, Proxmox 설정이 흔들리면 결과는 들쭉날쭉해집니다. 측정 전에 아래 조건부터 고정해두는 편이 낫습니다.

    1. 같은 모델 태그, 같은 프롬프트, 같은 출력 길이 조건으로 반복합니다.
    2. 테스트 시간대에는 다른 VM의 백업, 미디어 인덱싱, 대용량 복사 같은 잡음을 줄입니다.
    3. GPU 패스스루를 쓴다면 항상 같은 VM, 같은 게스트 커널 상태에서만 비교합니다.
    4. ballooning target, swap 개입 여부를 먼저 확인하고, 가능하면 벤치 구간에서는 메모리 조건을 고정합니다.
    5. 모델 파일 위치가 로컬 SSD인지, 네트워크 스토리지인지, ZFS ARC나 페이지 캐시 영향이 있는지 기록합니다.

    여기서 실전적으로 중요한 구분이 cold run과 warm run입니다. 첫 요청은 적재 비용이 섞이고, 두 번째부터는 메모리 상주 효과가 붙습니다. 둘을 한 표에 섞어두면 얼핏 데이터가 많아 보여도 해석에는 별 도움이 안 됩니다.

    Proxmox 쪽에서 제가 특히 먼저 확인하는 건 세 가지입니다. CPU affinity, NUMA 사용 여부, ballooning 조건 고정 여부입니다. CPU 배치가 계속 흔들리면 짧은 벤치에서는 티가 덜 나도 반복 측정 편차가 커집니다. ballooning도 운영 효율에는 도움이 되지만, 추론 성능 비교에는 변수로 끼어들 수 있습니다. 홈랩에서는 운영 효율과 벤치 재현성이 종종 충돌하는데, 벤치 시간에는 재현성을 우선하는 편이 낫더라고요.

    실전 구현 1: Ollama API로 응답 시간과 토큰 생성 속도 수집

    CLI 체감은 참고용으로는 괜찮지만, 기록용 벤치마크에는 조금 아쉽습니다. 반복 측정과 후처리를 생각하면 Ollama API의 원본 메트릭을 그대로 저장하는 편이 훨씬 낫습니다. 아래처럼 한 번 호출해서 핵심 필드를 바로 계산해보면 감이 빨리 옵니다.

    curl -s http://127.0.0.1:11434/api/generate \
      -H 'Content-Type: application/json' \
      -d '{
        "model": "llama3:8b",
        "prompt": "Explain the difference between CPU bottleneck and GPU bottleneck in one short paragraph.",
        "stream": false,
        "options": {
          "temperature": 0,
          "num_predict": 128
        }
      }' | jq '{
        model,
        created_at,
        total_duration_ns: .total_duration,
        load_duration_ns: .load_duration,
        prompt_eval_count,
        prompt_eval_duration_ns: .prompt_eval_duration,
        eval_count,
        eval_duration_ns: .eval_duration,
        tokens_per_second: (if .eval_duration > 0 then (.eval_count / (.eval_duration / 1000000000)) else 0 end)
      }'

    여기서 temperature를 0으로 두는 이유는 성능 측정에서 출력 다양성을 줄이기 위해서입니다. num_predict를 고정하는 이유도 같고요. 벤치마크에서 가장 흔한 실수가 출력 길이를 매번 다르게 두는 건데, 그렇게 되면 eval_count가 흔들리고 결국 토큰/초 비교도 흐려집니다.

    반복 측정은 최소 5회 정도는 권합니다. 평균만 보지 말고 최솟값, 최댓값, 편차까지 같이 남겨야 합니다. 홈랩에서는 한 번 빠르게 나온 결과보다 얼마나 덜 흔들리는지가 더 중요할 때가 많거든요.

    for i in 1 2 3 4 5; do
      curl -s http://127.0.0.1:11434/api/generate \
        -H 'Content-Type: application/json' \
        -d '{
          "model": "llama3:8b",
          "prompt": "Write exactly five bullet points about Linux memory pressure.",
          "stream": false,
          "options": {
            "temperature": 0,
            "num_predict": 96
          }
        }' | jq -r --arg run "$i" '[
          $run,
          .created_at,
          .total_duration,
          .load_duration,
          .prompt_eval_count,
          .prompt_eval_duration,
          .eval_count,
          .eval_duration,
          (if .eval_duration > 0 then (.eval_count / (.eval_duration / 1000000000)) else 0 end)
        ] | @csv'
    done >> ollama-bench.csv

    이 CSV 로그는 보기보다 강력합니다. 나중에 VM 설정을 바꿨을 때, 모델 파일 위치를 옮겼을 때, GPU를 교체했을 때 비교 기준이 그대로 남습니다. 저는 이 단계에서 대시보드부터 만들지 않는 편을 권합니다. 원본 로그가 안정적으로 쌓이는 체계가 먼저고, 시각화는 그 다음이더라고요.

    하나 더요. 벤치마크 목적이라면 stream: false를 유지하는 편이 낫습니다. 스트리밍 출력은 체감 속도 확인에는 좋지만, 수집 포맷이 지저분해지고 메트릭 해석이 섞이기 쉽습니다. 체감용 테스트와 기록용 테스트를 분리해두면 나중에 덜 꼬입니다.

    Ollama 벤치마크에서 LLM 성능 측정 지표를 설명하는 이미지

    Ollama API 응답에서 어떤 필드를 뽑아 성능 지표로 쓰는지 설명하는 시각 자료입니다.

    실전 구현 2: 시스템 자원 사용량을 같이 보세요

    Ollama API 숫자만 보면 원인 추정이 자꾸 빗나갑니다. 같은 요청이라도 CPU 압박인지, 디스크 대기인지, GPU가 실제로 일하는지 구분이 안 되기 때문입니다. 저는 최소한 아래 세 축은 같이 봅니다.

    vmstat 1
    
    iostat -xz 1
    
    nvidia-smi dmon -s pucm
    

    읽는 기준은 단순하지만, 해석은 꽤 실전적입니다.

    • vmstat 1: r가 계속 높고 si/so가 움직이면 CPU 경쟁이나 swap 개입을 먼저 의심합니다.
    • iostat -xz 1: 모델 로딩 시점에 await와 %util이 튀면 load_duration 상승과 연결해서 봅니다.
    • nvidia-smi dmon -s pucm: 생성 중에도 GPU 사용률과 메모리 사용량이 거의 반응하지 않으면 GPU 가속 경로를 다시 확인합니다.

    제가 자주 보는 실패 패턴은 이겁니다. GPU는 VM에서 보이는데, 실제 생성 구간에서는 GPU가 거의 일하지 않는 경우예요. 이때는 패스스루 성공과 추론 가속 성공을 분리해서 봐야 합니다. 장치 인식 자체는 성공했지만 드라이버 상태, 메모리 부족, 런타임 제약 때문에 기대한 만큼 가속이 안 붙는 경우가 있습니다. 반대로 GPU 사용률이 높다고 바로 좋은 것도 아닙니다. 메모리 이동 비용이나 CPU 병목이 남아 있으면 eval_duration 개선이 생각보다 작을 수 있습니다.

    재현 가능한 시나리오: cold run과 warm run 분리 측정

    제가 홈랩에서 가장 자주 쓰는 측정 패턴은 cold run과 warm run을 아예 별도 시나리오로 나누는 방식입니다. 같은 모델, 같은 프롬프트여도 첫 요청과 반복 요청은 성격이 다릅니다. 이 둘을 분리하면 적어도 로딩 비용과 생성 비용은 꽤 깔끔하게 갈라집니다.

    1. Ollama 서비스를 재시작하거나 충분히 유휴 상태를 만든 뒤 첫 요청을 보냅니다.
    2. 첫 요청의 load_duration, total_duration을 기록합니다.
    3. 같은 프롬프트를 3~5회 반복하고, 이 구간에서는 eval_duration과 토큰/초를 중심으로 봅니다.
    4. 동시에 vmstat, iostat, nvidia-smi dmon의 변화를 같은 시각 기준으로 맞춰둡니다.

    실제로는 아래처럼 분리 저장해두면 후처리가 편합니다.

    PROMPT='Explain why swap activity can distort LLM benchmark results in a VM.'
    MODEL='llama3:8b'
    
    # cold run
    curl -s http://127.0.0.1:11434/api/generate \
      -H 'Content-Type: application/json' \
      -d "{\"model\":\"${MODEL}\",\"prompt\":\"${PROMPT}\",\"stream\":false,\"options\":{\"temperature\":0,\"num_predict\":120}}" \
      | jq -r '["cold", .total_duration, .load_duration, .prompt_eval_duration, .eval_duration, .eval_count] | @csv' \
      >> bench-split.csv
    
    # warm runs
    for i in 1 2 3; do
      curl -s http://127.0.0.1:11434/api/generate \
        -H 'Content-Type: application/json' \
        -d "{\"model\":\"${MODEL}\",\"prompt\":\"${PROMPT}\",\"stream\":false,\"options\":{\"temperature\":0,\"num_predict\":120}}" \
        | jq -r --arg run "warm-${i}" '[$run, .total_duration, .load_duration, .prompt_eval_duration, .eval_duration, .eval_count] | @csv' \
        >> bench-split.csv
    done

    이 방식의 장점은 단순한데 실용적입니다. 첫 요청만 느리면 적재 비용 성격, 반복 요청도 계속 느리면 생성 경로 병목으로 빠르게 가설을 세울 수 있습니다. 홈랩에서는 이 구분만 제대로 해도 괜히 GPU부터 바꾸는 일을 꽤 줄일 수 있더라고요.

    홈랩 AI 서버에서 Proxmox와 Ollama 벤치마크를 수행하는 터미널 이미지

    cold run과 warm run을 비교하면서 시스템 자원 모니터링을 병행하는 실전 측정 화면 예시입니다.

    VM, LXC, 베어메탈 중 무엇을 기준점으로 삼아야 할까요?

    이 질문은 자주 나오는데, 저는 목적에 따라 답을 다르게 합니다. 무조건 하나가 낫다고 하기보다 무엇을 검증하려는지 먼저 정하는 편이 맞습니다.

    환경 이럴 때 추천 장점 주의할 점
    베어메탈 절대 성능 기준점이 필요할 때 가상화 변수가 적어 기준선 만들기 좋습니다. 실제 운영 환경이 Proxmox라면 결과 이식성이 떨어질 수 있습니다.
    Proxmox VM GPU 패스스루와 재현성 검증이 목적일 때 운영 환경과 같은 조건으로 측정하기 좋습니다. CPU affinity, NUMA, ballooning 같은 변수를 같이 관리해야 합니다.
    LXC 경량화와 운영 편의가 더 중요할 때 리소스 오버헤드가 적고 관리가 가볍습니다. GPU 활용 방식과 권한 구성이 환경에 따라 더 예민할 수 있어, 벤치 기준점으로는 다소 까다롭습니다.

    제 판단 기준은 이렇습니다. 운영도 Proxmox VM에서 할 예정이면 VM에서 잰 수치를 기준으로 삼는 편이 맞습니다. 베어메탈 수치가 더 좋아도 실제 배포 환경과 다르면 의사결정에는 덜 도움이 됩니다. 반대로 이 하드웨어가 어디까지 나오는지 보고 싶다면 베어메탈 기준선을 한 번 찍어두는 게 좋습니다.

    자주 겪는 문제와 해결 방법

    이 부분은 공식 문서만 봐서는 감이 잘 안 오는 영역입니다. 실제로 벤치를 돌리면 아래처럼 삐끗하는 경우가 많습니다.

    1. 프롬프트 길이가 매번 달라서 비교가 무너집니다

    prompt_eval_duration은 입력 길이에 직접 반응합니다. 질문을 바꾸거나 출력 길이를 열어두면, 모델 자체보다 워크로드 차이가 더 크게 반영됩니다. 벤치 프롬프트는 고정, 출력 길이는 num_predict 등으로 제한하는 편이 낫습니다.

    2. 스트리밍 체감 속도를 성능으로 착각합니다

    터미널에 토큰이 빨리 흘러나오면 체감상 빨라 보입니다. 그런데 그건 계측 기준으로는 불안정합니다. 수집용 벤치는 stream: false로 고정하고, 체감 테스트는 별도로 분리하세요. 둘을 섞어두면 나중에 기록이 비교 불가능해집니다.

    3. 디스크 병목이 숨어 있는데 GPU만 의심합니다

    모델을 자주 바꾸며 테스트할 때 특히 그렇습니다. load_duration이 갑자기 커지고 같은 시점 iostat의 await가 치솟는다면, 그건 GPU보다 모델 적재 경로를 먼저 봐야 합니다. 홈랩에서는 NVMe, SATA SSD, 네트워크 스토리지 차이가 생각보다 크게 드러납니다.

    4. Proxmox에서 다른 VM 부하가 끼어듭니다

    백업 작업, 미디어 트랜스코딩, ZFS scrub, 테스트용 쿠버네티스 VM이 동시에 돌면 반복 측정 편차가 커집니다. 이런 경우 평균값보다 분산이 커지는 패턴이 먼저 나타납니다. 숫자가 안 예쁜 게 아니라, 측정 조건이 불안정한 겁니다.

    5. GPU 사용률만 높고 성능 향상은 미미합니다

    이건 대개 GPU 연산 자체보다 CPU 준비 단계, 메모리 이동, 컨텍스트 처리가 먼저 막히는 경우입니다. 그래서 저는 GPU utilization을 목표값으로 보지 않습니다. eval_duration이 실제로 줄었는지를 먼저 확인합니다.

    6. 첫 요청이 계속 느린데 warm run처럼 해석합니다

    메모리 압박이나 unload 정책 때문에 모델이 자주 내려가는 환경에서는 매번 사실상 cold run이 됩니다. 이때는 첫 요청만 느린 건 정상이라고 넘기면 안 됩니다. 반복 호출인데도 load_duration이 매번 크게 잡히는지를 꼭 확인해보세요. 그건 상주 실패일 가능성이 큽니다.

    검증과 결과 해석: 숫자를 어떻게 읽어야 할까요?

    숫자를 예쁘게 정리하는 것보다 중요한 건, 어떤 패턴에서 어떤 결론을 내릴지 미리 정해두는 겁니다. 저는 보통 아래처럼 읽습니다.

    • load_duration만 크다: 모델 적재, 스토리지, 최초 호출 비용을 먼저 봅니다.
    • prompt_eval_duration이 길다: 긴 프롬프트, 큰 컨텍스트, CPU 토큰 처리 경로를 의심합니다.
    • eval_duration이 길다: 실제 생성 성능 문제입니다. GPU 가속 상태, CPU 할당, 모델 크기 적합성을 봐야 합니다.
    • GPU 사용률이 낮고 CPU만 바쁘다: 패스스루 성공 여부보다 실제 추론 가속 경로를 재확인합니다.
    • 반복 측정 편차가 크다: 다른 VM 간섭, 메모리 압박, 백그라운드 I/O를 먼저 정리합니다.

    여기서 중요한 결정 포인트가 있습니다. 무엇을 최적화할지 먼저 정해야 수치 해석이 쉬워집니다. 사용자 체감 첫 응답을 줄이는 게 목표라면 total_duration과 load_duration이 더 중요합니다. 장비 비교가 목적이면 eval_duration과 토큰/초가 핵심이고요. 긴 컨텍스트 운영이 목표라면 prompt_eval_duration이 병목인지부터 봐야 합니다.

    로그 포맷은 거창할 필요 없습니다. 다만 나중에 조건을 복원할 수 있을 만큼은 남겨두는 편이 좋습니다.

    timestamp,host,vm,model,prompt_name,run_type,total_duration,load_duration,prompt_eval_count,prompt_eval_duration,eval_count,eval_duration
    

    이 한 줄이 중요한 이유는 숫자와 환경을 같이 묶어두기 때문입니다. 벤치 결과만 남기고 VM 설정 이력을 빼먹으면 몇 주 뒤에는 왜 빨라졌는지, 왜 느려졌는지 복기가 잘 안 됩니다. 관련해서 Proxmox GPU 패스스루 설정 글이나 Ollama 설치 가이드를 같이 정리해두면 나중에 내부 링크 연결에도 꽤 유용합니다.

    Proxmox Ollama 환경의 LLM 추론 성능 결과를 시각화한 대시보드 이미지

    모델 로딩 시간과 실제 생성 시간을 분리해 해석하는 결과 대시보드 예시입니다.

    운영 팁: 측정 환경을 문서화해야 나중에 안 꼬입니다

    벤치마크는 숫자보다 맥락이 먼저입니다. 특히 홈랩 AI 서버는 장비를 자주 만지게 되니까, 어떤 조건에서 잰 수치인지 생각보다 빨리 잊어버립니다. 최소한 아래 항목은 같이 기록해두는 편이 좋습니다.

    • 모델 이름과 태그
    • 게스트 OS 종류와 커널 상태
    • vCPU 개수, 메모리 크기
    • GPU 패스스루 유무와 장치 종류
    • 모델 파일 저장 위치
    • 테스트 프롬프트 이름
    • cold run인지 warm run인지

    여기서 제 권장 방식은 단순합니다. 설정 변경이 있는 날에는 벤치마크를 다시 찍고, 변경 사유를 한 줄 메모로 남겨두세요. CPU affinity를 바꿨는지, 메모리를 늘렸는지, 모델 저장 위치를 옮겼는지 정도만 적어도 나중에 비교가 됩니다. 숫자만 예쁘게 모아두는 것보다 이게 훨씬 실용적이더라고요.

    FAQ: 현장에서 많이 헷갈리는 부분

    Q. Proxmox LLM 테스트는 VM이 낫나요, LXC가 낫나요?

    GPU 패스스루 검증과 재현성을 우선하면 저는 VM 쪽을 더 자주 씁니다. 운영 편의만 보면 LXC가 가벼울 수 있지만, 벤치 기준점을 만들 때는 변수를 줄이기 쉬운 쪽이 VM이었습니다.

    Q. Ollama 벤치마크는 CLI만으로 충분한가요?

    단발성 확인은 가능합니다. 다만 반복 측정, CSV 저장, 후처리, cold/warm 구분까지 생각하면 API 기반 수집이 훨씬 낫습니다.

    Q. 숫자는 좋은데 체감은 별로입니다

    짧은 프롬프트 위주로만 재면 실제 운영 패턴과 어긋날 수 있습니다. 특히 RAG, 긴 시스템 프롬프트, 멀티턴 대화가 붙으면 prompt_eval_duration 비중이 커집니다. 운영 워크로드와 비슷한 입력으로 다시 재보는 게 맞습니다.

    Q. 토큰/초가 높으면 무조건 좋은 건가요?

    장비 비교에는 유용하지만, 첫 응답 대기시간이 중요한 환경에서는 부족합니다. 사용자가 느끼는 체감은 종종 load_duration과 prompt_eval_duration에 더 크게 좌우됩니다.

    마지막으로: 이런 상황이면 이렇게 고르시면 됩니다

    제가 실제로 추천하는 선택 기준은 아래처럼 명확합니다.

    • 장비 비교가 목적: 같은 프롬프트와 같은 출력 길이로 고정하고 eval_duration과 토큰/초 중심으로 보세요.
    • 첫 응답 체감이 중요: load_duration을 포함한 total_duration을 같이 봐야 합니다. 모델 상주 전략과 스토리지 위치가 더 중요해질 수 있습니다.
    • GPU 튜닝이 목적: Ollama API 결과만 보지 말고 nvidia-smi dmon과 vmstat를 반드시 붙이세요.
    • 운영 전 검증이 목적: CSV 형태로 누적 저장하고, cold run과 warm run을 분리 기록하세요.
    • 결과가 자꾸 흔들린다: 모델을 바꾸기 전에 다른 VM 간섭, ballooning, swap, 스토리지 대기를 먼저 정리하세요.

    조금 더 직설적으로 말하면 이렇습니다. 첫 응답만 느리면 스토리지와 적재 전략부터, 반복 응답도 느리면 CPU/GPU 경로부터, 숫자가 출렁이면 Proxmox 자원 간섭부터 보시면 됩니다. 이 순서가 실제로 가장 덜 돌아가는 편이었습니다.

    제 경험상, Ollama 숫자만 보고 결론 내리면 절반은 빗나가고, Proxmox 호스트와 게스트의 시스템 지표까지 같이 보면 비로소 조정 포인트가 보입니다. Proxmox LLM Ollama 벤치마크는 장비 스펙보다 측정 설계가 더 중요할 때가 많습니다. 한 번만 잘 만들어두면, 그다음부터는 모델 교체든 VM 튜닝이든 훨씬 덜 감으로 움직이게 됩니다.

    Proxmox와 Ollama 기반 GPU 가속 성능 측정 요약 인포그래픽

    Proxmox 기반 Ollama 벤치마크 절차와 해석 기준을 요약한 인포그래픽입니다.

  • [AI] LLM 추론 성능 벤치마크: TensorRT vs ONNX Runtime

    [AI] LLM 추론 성능 벤치마크: TensorRT vs ONNX Runtime

    [AI 인프라] LLM 추론 성능 벤치마크: NVIDIA TensorRT vs ONNX Runtime

    LLM 추론 성능 벤치마크를 해보면, 병목은 생각보다 GPU 스펙 한 줄로 설명되지 않더라고요. 같은 GPU, 같은 모델이라도 어떤 런타임이 어떤 shape를 얼마나 안정적으로 처리하느냐, 그리고 초기화 비용과 토큰 생성 구간을 어떻게 분리해 봤느냐에 따라 체감 성능이 꽤 달라집니다. 저도 초반에는 TensorRT 쪽 숫자가 더 높게 나오면 무조건 이긴 줄 알았는데, 실제 운영에선 첫 요청 지연, 엔진 캐시, 입력 길이 분산, Python 생성 루프 오버헤드가 더 크게 문제 되는 경우가 많았습니다.

    이번 글은 단순 비교가 아니라, 같은 조건에서 TensorRT와 ONNX Runtime를 어떻게 공정하게 재는지, 그리고 측정값이 실제 서비스 판단으로 어떻게 이어지는지에 집중했습니다. 홈랩, 사내 검증 서버, PoC 환경에서 바로 써먹을 수 있게 명령어와 체크 포인트를 실무 기준으로 묶었습니다.

    로컬 LLM 추론 성능 벤치마크를 위한 TensorRT와 ONNX Runtime 아키텍처 이미지

    로컬 GPU 환경에서 모델 로딩, 토크나이저, 런타임, 추론 결과까지 이어지는 전체 흐름을 한눈에 보여주는 아키텍처 이미지입니다.

    1. 왜 TensorRT와 ONNX Runtime를 같이 보게 되나

    실무에서 둘을 같이 보는 이유는 단순합니다. TensorRT는 NVIDIA GPU 고정 환경에서 최대 성능을 노릴 때 강력한 선택지고, ONNX Runtime은 모델 비교, 환경 이동, 운영 단순성을 챙기기 좋은 기준선이기 때문입니다. 둘은 경쟁 관계이면서도, 실제론 비교용 baseline과 최적화용 target이라는 식으로 같이 쓰는 경우가 많습니다.

    • TensorRT: 엔진 빌드 비용과 shape 관리 부담을 감수하는 대신, 고정 워크로드에서는 높은 처리량과 낮은 지연을 기대할 수 있습니다.
    • ONNX Runtime: CUDA Execution Provider만 붙여도 baseline이 빨리 잡히고, 디버깅 난도가 비교적 낮습니다.
    • ONNX Runtime + TensorRT Execution Provider: 코드 경로는 유지하면서 일부 최적화를 노릴 수 있지만, 문제 원인 분리가 오히려 더 어려워지는 경우도 있습니다.

    여기서 제가 중요하게 보는 건 하나입니다. 런타임 성능만 빠른지, 전체 생성 경로가 빠른지를 분리해야 한다는 점입니다. TensorRT 엔진 레벨 숫자가 좋아도 실제 서비스 응답이 별 차이 없으면, 대개 병목은 런타임 바깥에 있습니다. 반대로 ONNX Runtime가 조금 느려 보여도 재현성과 운영 안정성이 높으면, 초기 서비스에는 그쪽이 더 값어치 있는 선택일 수 있습니다.

    2. LLM 추론 성능 벤치마크에서 꼭 봐야 할 지표

    LLM 벤치마크는 이미지 분류처럼 한 번 넣고 한 번 끝나는 추론과 다릅니다. 프롬프트 처리(prefill)와 토큰 생성(decode)이 분리되고, KV cache가 붙고, 입력 길이 편차가 커서 평균값 하나로 끝내면 판단을 그르치기 쉽습니다. 그래서 LLM 추론 성능 벤치마크에선 평균 속도보다 구간별 특성을 같이 봐야 합니다.

    2-1. 꼭 맞춰야 하는 비교 조건

    1. 같은 모델 가중치, 같은 토크나이저, 같은 전처리 조건을 씁니다.
    2. 가능하면 같은 ONNX 그래프를 기준으로 비교하고, TensorRT 11.x처럼 강타입 빌드가 필요한 경우에는 사전 precision 변환 단계까지 함께 기록합니다.
    3. 정밀도는 FP16, BF16, FP32, INT8 중 하나로 고정합니다. 섞어 재면 비교가 무너집니다.
    4. 프롬프트 길이와 생성 토큰 수를 고정하고, 가능하면 짧은 입력/긴 입력 두 구간으로 나눠 봅니다.
    5. 워밍업 횟수와 측정 구간을 분리합니다. 첫 요청을 평균에 섞으면 해석이 흐려집니다.
    6. 배치 크기와 동시성은 별도 축입니다. 둘을 한 번에 바꾸면 원인을 찾기 어렵습니다.
    7. 전원 상태, 클럭 상태, 백그라운드 프로세스를 고정합니다. 특히 데스크톱 환경에서는 이 영향이 꽤 큽니다.

    2-2. 해석할 지표

    • TTFT(Time To First Token): 사용자 체감에 가장 직접적입니다. 첫 토큰이 느리면 답답하게 느껴지거든요.
    • Tokens/sec: 긴 출력에서 중요합니다. decode 구간 효율이 여기서 드러납니다.
    • p95 latency: 운영 안정성을 보려면 평균보다 이 수치가 더 중요합니다.
    • GPU memory usage: 속도가 비슷하면 메모리 여유가 있는 쪽이 운영성이 좋습니다.
    • Engine build / session init overhead: 재시작이 잦은 환경, 다중 모델 환경에서는 이 비용이 실제 총비용으로 이어집니다.
    • CPU time: 토크나이저, 후처리, Python 루프가 병목이면 GPU 최적화 이득이 상쇄됩니다.

    제가 실제로 더 신뢰하는 건 최고 기록이 아니라 세 번 돌렸을 때 비슷하게 나오는지입니다. 단일 최고치만 잘 나온 벤치마크는 운영 판단 기준으로는 거의 쓸모가 없습니다.

    3. TensorRT 최적화와 ONNX Runtime 비교를 위한 기준선 만들기

    로컬 LLM 추론 성능 벤치마크에서 제일 먼저 해야 할 일은, 화려한 최적화가 아니라 분리 측정입니다. 저는 보통 아래 네 구간으로 나눕니다.

    1. 모델 준비 구간: ONNX export, shape 정의, 동적 축 확인
    2. 초기화 구간: TensorRT 엔진 빌드 또는 ONNX Runtime 세션 생성
    3. prefill 구간: 긴 프롬프트를 한 번에 넣는 첫 추론
    4. decode 구간: 토큰을 한 개씩 이어서 생성하는 반복 추론

    이 네 구간을 섞어서 평균 latency 하나로 덮으면 실무에서는 해석이 거의 틀어집니다. TensorRT는 엔진 빌드가 무겁고, ONNX Runtime는 세션 초기화와 provider 구성 영향이 큽니다. LLM은 여기에 prefill과 decode의 성격까지 달라서, 어느 구간에서 이겼는지가 곧 선택 기준이 됩니다.

    항목 TensorRT ONNX Runtime 실무 판단 포인트
    최대 성능 잠재력 높음 중간~높음 GPU와 워크로드가 고정이면 TensorRT 투자 가치가 커집니다.
    초기 실험 속도 느린 편 빠른 편 모델 export 직후 baseline을 잡을 때는 ONNX Runtime가 훨씬 편합니다.
    입력 길이 변동 대응 shape 전략에 민감 상대적으로 유연 프롬프트 길이 편차가 크면 TensorRT 최적화 이점이 줄어들 수 있습니다.
    장애 분석 난이도 높음 낮음 팀에 추론 엔진 디버깅 경험이 없으면 운영 비용이 성능 이득을 먹어버릴 수 있습니다.
    재기동 비용 민감도 엔진 캐시 전략 필요 세션 재생성 중심 짧은 수명 컨테이너 환경에서는 캐시 설계가 성능만큼 중요합니다.
    권장 시작점 최적화 단계 기준선 단계 바로 TensorRT로 들어가기보다 ONNX Runtime로 병목 위치를 먼저 확인하는 편이 낫습니다.

    4. 실전 구현 1: TensorRT 엔진 생성과 기본 측정

    TensorRT는 <code>trtexec로 첫 감을 잡는 게 가장 빠릅니다. 다만 LLM에서는 단순히 FP16 옵션 하나만 넣고 끝내면 부족합니다. 입력 shape 범위를 명시하지 않으면 엔진이 실제 프롬프트 분포와 어긋날 수 있고, 그 결과 기대 이하의 최적화가 나올 수 있습니다.

    아래 예시는 TensorRT 10.x 계열에서 많이 쓰는 방식입니다. TensorRT 11.x부터는 일부 precision 관련 플래그가 바뀌었거나 제거됐기 때문에, 실제 사용 전에는 trtexec --help로 로컬 버전을 꼭 확인해 두는 게 좋습니다. 이거 안 보고 그대로 복붙했다가 한 번씩 막히더라고요.

    trtexec \
      --onnx=model.onnx \
      --saveEngine=model_fp16.plan \
      --fp16 \
      --minShapes=input_ids:1x16,attention_mask:1x16 \
      --optShapes=input_ids:1x512,attention_mask:1x512 \
      --maxShapes=input_ids:1x2048,attention_mask:1x2048 \
      --warmUp=200 \
      --duration=60 \
      --verbose

    이 명령에서 실무적으로 중요한 건 아래입니다.

    • --minShapes, --optShapes, --maxShapes: 동적 입력 모델이라면 사실상 핵심입니다. opt shape는 가장 자주 들어오는 프롬프트 길이대로 잡는 편이 낫습니다.
    • --warmUp: 보통 워밍업 시간 기준으로 해석합니다. 반복 횟수라고 착각하면 결과 비교가 꼬일 수 있습니다.
    • --verbose: 성능보다 먼저 봐야 할 건 변환 실패 레이어, precision 강등, 지원되지 않는 연산 흔적입니다.

    LLM에서 자주 놓치는 건 shape 전략이 사실상 성능 전략이라는 점입니다. 짧은 프롬프트 위주 서비스인데 max shape를 과하게 크게 잡으면 메모리와 최적화 효율이 함께 손해를 봅니다. 반대로 입력 길이 분산이 큰데 지나치게 좁게 잡으면 실제 요청에서 비효율이 생길 수 있습니다.

    엔진 생성과 추론을 분리해서 보고 싶다면 저장된 엔진으로 다시 측정하는 편이 낫습니다.

    trtexec \
      --loadEngine=model_fp16.plan \
      --shapes=input_ids:1x512,attention_mask:1x512 \
      --warmUp=200 \
      --duration=60

    이렇게 해야 엔진 빌드 시간이 빠진 순수 추론 구간을 보기 좋습니다. 엔진 빌드 시간을 평균 latency에 섞는 실수는 생각보다 자주 나오고, 그 순간부터 비교표는 의미가 많이 줄어듭니다.

    TensorRT 최적화와 GPU 가속 흐름을 설명하는 LLM 추론 성능 벤치마크 이미지

    ONNX 모델이 TensorRT 엔진으로 변환되고, 워밍업과 프로파일링을 거쳐 성능 지표가 수집되는 흐름을 설명하는 이미지입니다.

    5. 실전 구현 2: ONNX Runtime baseline 측정

    ONNX Runtime의 강점은 baseline을 빠르게 만들 수 있다는 점입니다. 저는 여기서 단순 평균 latency보다 provider가 제대로 붙었는지, 세션 옵션이 적절한지, 실제 생성 루프와 분리해 엔진 수준 시간을 볼 수 있는지를 먼저 확인합니다.

    import time
    import numpy as np
    import onnxruntime as ort
    
    session_options = ort.SessionOptions()
    session_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
    session_options.log_severity_level = 0
    session_options.log_verbosity_level = 1
    
    providers = [
        (
            "CUDAExecutionProvider",
            {
                "device_id": 0,
                "arena_extend_strategy": "kNextPowerOfTwo",
                "cudnn_conv_algo_search": "EXHAUSTIVE",
                "do_copy_in_default_stream": True,
            },
        ),
        "CPUExecutionProvider",
    ]
    
    session = ort.InferenceSession(
        "model.onnx",
        sess_options=session_options,
        providers=providers,
    )
    
    print("active providers:", session.get_providers())
    input_names = [x.name for x in session.get_inputs()]
    print("inputs:", input_names)
    
    feeds = {
        "input_ids": np.ones((1, 16), dtype=np.int64),
        "attention_mask": np.ones((1, 16), dtype=np.int64),
    }
    
    for _ in range(20):
        session.run(None, feeds)
    
    latencies_ms = []
    for _ in range(100):
        start = time.perf_counter()
        session.run(None, feeds)
        latencies_ms.append((time.perf_counter() - start) * 1000)
    
    latencies_ms.sort()
    p95 = latencies_ms[int(len(latencies_ms) * 0.95) - 1]
    print(f"avg latency: {sum(latencies_ms)/len(latencies_ms):.3f} ms")
    print(f"p95 latency: {p95:.3f} ms")

    이 코드는 baseline용입니다. 실제 LLM 생성 벤치마크로 가려면 past_key_values, position_ids, attention_mask shape, 그리고 prefill/decode 분리를 모델 구조에 맞게 넣어야 합니다. 중요한 건 엔진 호출 성능과 애플리케이션 생성 루프 성능을 따로 수집하는 습관입니다.

    ONNX Runtime 안에서 TensorRT Execution Provider를 시험해 보고 싶다면 아래처럼 provider를 분리해 보는 방법도 있습니다. 이 경우는 성능 수치보다 캐시 동작과 fallback 여부를 먼저 보는 편이 낫습니다.

    providers = [
        (
            "TensorrtExecutionProvider",
            {
                "trt_engine_cache_enable": True,
                "trt_engine_cache_path": "./trt_cache",
                "trt_fp16_enable": True,
            },
        ),
        "CUDAExecutionProvider",
        "CPUExecutionProvider",
    ]
    
    session = ort.InferenceSession("model.onnx", providers=providers)

    GPU 상태는 꼭 같이 봐야 합니다. 벤치마크 로그만 보면 빨라 보이는데 GPU util이 낮고 CPU 한 코어만 치솟는 경우가 꽤 많습니다.

    nvidia-smi dmon -s pucm -d 1

    여기서 저는 네 가지를 같이 봅니다. power는 클럭 유지 여부, util은 GPU 바쁨 정도, clock은 스로틀링 여부, memory는 컨텍스트 길이와 배치 변화의 영향을 읽는 데 도움이 됩니다.

    6. 주의사항과 트러블슈팅: 제가 자주 겪었던 문제들

    벤치마크는 숫자를 뽑는 작업이 아니라 왜 그 숫자가 나왔는지 설명하는 작업에 가깝습니다. 아래 문제들은 LLM 추론 비교에서 반복해서 나오는 실패 모드입니다. 실제로는 런타임보다 입력 경로에서 막히는 경우도 꽤 많더라고요.

    6-1. 같은 모델인데 결과가 안 맞는 경우

    • 토크나이저 옵션 차이로 입력 길이가 달라집니다. 성능 비교 전에 실제 token count부터 맞춰야 합니다.
    • padding 방향과 최대 길이 설정이 다르면 연산량이 달라집니다.
    • 배치 크기 자동 조정이나 동적 batching이 켜져 있으면 공정 비교가 깨집니다.
    • FP16과 FP32, 또는 BF16을 섞어 재면 런타임 차이보다 precision 차이가 더 크게 나옵니다.

    근본 원인은 대부분 같은 모델을 보고 있다고 착각하는 것입니다. 파일명이 같아도 export 옵션, 동적 축, 입력 schema가 달라지면 사실상 다른 워크로드입니다.

    6-2. TensorRT 엔진은 빠른데 실제 생성 속도는 애매한 경우

    이건 정말 자주 봅니다. 원인은 대체로 런타임 바깥에 있습니다.

    • 토크나이저가 CPU 병목입니다. 짧은 응답에서는 이 영향이 생각보다 큽니다.
    • Python 루프가 토큰마다 GPU 호출을 감싸면서 왕복 오버헤드를 만듭니다.
    • KV cache 레이아웃이나 메모리 복사가 비효율적입니다.
    • 입력 shape가 계속 바뀌어 TensorRT 최적화가 기대만큼 먹지 않습니다.
    • 엔진 캐시가 제대로 재사용되지 않아 재시작 후마다 준비 비용이 다시 듭니다.

    GPU util은 낮은데 TTFT와 p95가 큰 경우는 계산 문제가 아니라 상위 경로 병목일 가능성이 큽니다. 이때 GPU 커널을 더 최적화하는 건 우선순위가 아닙니다.

    6-3. ONNX Runtime가 예상보다 느린 경우

    • CUDAExecutionProvider가 실제로 활성화됐는지 먼저 확인합니다.
    • 지원되지 않는 연산이 CPU로 fallback 되는지 로그를 봅니다.
    • 세션 옵션의 그래프 최적화가 빠져 있지 않은지 확인합니다.
    • ONNX export에서 dynamic axis를 과하게 열어 둬 shape 추론과 최적화가 약해진 경우를 의심합니다.
    • 입력 텐서를 매 요청마다 새로 할당하면서 호스트 메모리 오버헤드를 키우는 경우도 꽤 있습니다.

    실무에서 제가 제일 경계하는 건 런타임이 느린 게 아니라 export 품질이 낮은 경우입니다. ONNX Runtime 성능 문제처럼 보여도, 실제로는 모델 export가 비효율적으로 된 사례가 적지 않습니다.

    ONNX Runtime 비교를 위한 CUDA Execution Provider 구성 이미지

    ONNX Runtime 세션 초기화, 그래프 최적화, CUDA Execution Provider 적용 구조를 이해하기 쉽게 풀어낸 이미지입니다.

    7. LLM 추론 성능 벤치마크 결과, 숫자보다 읽는 법이 먼저입니다

    측정이 끝났다면 이제 숫자를 읽어야 합니다. 저는 아래처럼 봅니다. 이 구간이 사실 제일 중요합니다. 숫자만 높다고 바로 정답은 아니거든요.

    1. TTFT가 길다: 엔진/세션 초기화, 긴 프롬프트 prefill, 토크나이저, 첫 decode 경로를 의심합니다.
    2. 평균 latency는 좋은데 p95가 나쁘다: 동시성 구간의 큐잉, 메모리 압박, shape 분산이 원인일 가능성이 큽니다.
    3. tokens/sec가 잘 안 오른다: decode 루프의 CPU 개입, KV cache 처리, 작은 배치가 문제일 수 있습니다.
    4. GPU util이 낮다: GPU가 한가한데 응답은 느리다면 애플리케이션 경로를 먼저 봐야 합니다.
    5. GPU memory가 높고 출렁인다: 최대 시퀀스 길이, 동시성, 정밀도 조합이 과한 신호일 수 있습니다.

    홈랩이나 PoC에서는 특히 재현성이 중요합니다. 저는 최소 3회 반복, 워밍업 제외, 첫 요청 별도 기록, prefill/decode 분리, GPU 모니터링 병행 이 다섯 가지를 기본으로 둡니다. 이렇게 해 두면 나중에 팀 내에서 숫자 해석으로 싸울 일이 확 줄어듭니다.

    관측 결과 자주 나오는 근본 원인 먼저 할 조치 TensorRT 쪽 힌트 ONNX Runtime 쪽 힌트
    첫 요청만 유독 느림 엔진 빌드, 세션 초기화, 캐시 미적용 초기화 구간을 분리 측정 엔진 캐시와 사전 빌드 여부 확인 세션 재사용과 provider 초기화 로그 확인
    긴 프롬프트에서 급격히 느려짐 prefill 비용 증가, shape 최적화 불일치 입력 길이 구간별로 분리 측정 opt shape를 실제 분포에 맞게 재설계 export dynamic axis와 입력 텐서 구성 점검
    GPU util은 낮은데 응답은 느림 토크나이저, Python 루프, CPU fallback CPU 사용률과 provider 로그 확인 엔진 바깥 호출 오버헤드 축소 fallback 연산과 메모리 할당 패턴 확인
    평균은 괜찮은데 p95가 나쁨 동시성, 큐잉, 메모리 압박 동시성 단계별로 별도 실행 shape 편차와 메모리 상한 재검토 세션 옵션, provider 설정, CPU 개입 확인

    결과 정리 시에는 숫자 순위표보다 어떤 조건에서 우세했는지를 남기는 편이 훨씬 유용합니다. TensorRT가 이긴 게 아니라, 고정 shape의 긴 decode 구간에서 TensorRT가 우세했다처럼 써야 다음 의사결정에 바로 써먹을 수 있습니다.

    LLM 추론 성능 벤치마크 결과와 GPU 사용률을 시각화한 이미지

    TTFT, tokens/sec, GPU utilization, memory usage를 한 화면에서 비교하는 대시보드 형태의 결과 이미지입니다.

    8. 실무 추천과 다음 단계

    제가 현업에서 내리는 기준은 꽤 단순합니다. 먼저 ONNX Runtime로 병목 위치를 밝히고, 그다음 TensorRT로 돈 되는 구간만 최적화하는 흐름이 가장 덜 아픕니다. 처음부터 TensorRT로 들어가면 성능은 빨리 보여도, 왜 빨라졌는지와 왜 안 빨라졌는지를 분리하기 어려운 경우가 많습니다.

    • 이럴 땐 ONNX Runtime: 모델을 자주 바꾼다, 비교 실험이 많다, 디버깅 시간을 아껴야 한다, NVIDIA 고정 배포가 아니다.
    • 이럴 땐 TensorRT: GPU가 고정돼 있다, 긴 기간 같은 워크로드를 운영한다, p95와 처리량을 끝까지 밀어야 한다, 엔진 관리까지 감당할 팀이 있다.
    • 이럴 땐 둘 다 본다: ONNX Runtime baseline은 안정적인데 비용이 아쉽고, 병목이 GPU 쪽이라는 근거가 이미 있다.

    조금 더 직설적으로 말씀드리면, PoC 단계에서 바로 TensorRT를 주력 경로로 잡는 건 대개 이릅니다. 반대로 이미 NVIDIA GPU 기반으로 워크로드가 굳어 있고, 하루 종일 같은 패턴의 요청을 받는다면 TensorRT 최적화는 충분히 투자할 만합니다. 이 경우에는 성능 향상 자체보다도 예측 가능한 지연과 재현성이 장점으로 돌아옵니다.

    1. 개발 속도, 실험 반복, 모델 교체 빈도가 중요하면 ONNX Runtime로 시작하는 편이 맞습니다.
    2. 배포 환경이 NVIDIA GPU로 고정이고, TTFT보다 지속 처리량과 p95가 더 중요하면 TensorRT 우선 검토가 맞습니다.
    3. 둘 중 하나로 바로 못 정하겠다면, 같은 ONNX 기준으로 prefill과 decode를 분리 측정한 뒤 TTFT, tokens/sec, p95, GPU memory 네 항목으로 결정하는 게 가장 안전합니다.

    다음 단계도 명확합니다. baseline이 흔들리면 런타임 최적화 전에 export와 입력 경로부터 정리하시고, baseline이 안정적인데 GPU 병목이 분명하면 그때 TensorRT로 들어가시면 됩니다. 이 순서를 지키면 삽질 시간이 확실히 줄어듭니다.

    관련해서 모델 경량화나 GPU 가속 운영 팁이 더 궁금하시면, 블로그의 다른 AI 인프라 글도 같이 읽어보시면 흐름이 훨씬 잘 잡힐 겁니다.

    TensorRT와 ONNX Runtime 선택 기준을 요약한 LLM 추론 성능 벤치마크 이미지

    어떤 상황에서 TensorRT를 고르고, 어떤 상황에서 ONNX Runtime를 고르면 되는지 한 장으로 정리한 요약 이미지입니다.

    FAQ

    Q. ONNX Runtime 안에서 TensorRT도 쓸 수 있나요?

    네, TensorRT Execution Provider로 구성할 수 있습니다. 다만 성능만 보지 말고, 엔진 캐시 재사용, fallback 경로, 디버깅 난이도를 같이 보셔야 합니다. 코드 경로는 단순해 보여도 장애 분석은 더 어려워질 수 있습니다.

    Q. LLM 추론 성능 벤치마크에서 숫자가 자꾸 달라집니다.

    워밍업, 프롬프트 길이, 출력 길이, 배치 크기, 전원/클럭 상태를 먼저 고정해 보세요. 거기에 백그라운드 프로세스와 첫 요청 분리 기록까지 넣으면 흔들림 원인을 상당히 빨리 좁힐 수 있습니다.

    Q. AI 모델 경량화가 꼭 필요할까요?

    메모리가 빠듯하거나 동시성을 올려야 한다면 우선순위가 높습니다. 다만 INT8이나 더 공격적인 최적화는 정확도 검증과 함께 가야 해서, baseline 없이 바로 들어가면 성능은 빨라져도 판단은 더 어려워질 수 있습니다.

  • [AI] Ollama 로컬 LLM 성능 벤치마크: NPU/GPU 가속 효과 분석

    [AI] Ollama 로컬 LLM 성능 벤치마크: NPU/GPU 가속 효과 분석

    안녕하세요, 13년차 서버실 지킴이입니다. 🤓

    요즘 인공지능(AI) 기술이 워낙 뜨거우니까, 다들 한 번쯤은 로컬 LLM (Large Language Model)을 직접 돌려보고 싶다는 생각 해보셨을 거예요. 저도 홈랩에서 이것저것 테스트해보는 걸 좋아해서, Ollama 같은 도구들이 나왔을 때 정말 반갑더라고요. 쉽고 빠르게 로컬 환경에서 다양한 LLM을 실행할 수 있게 해주거든요.

    그런데 막상 돌려보면 “생각보다 느리네?” 하는 경우가 많아요. 특히 텍스트를 쭉쭉 뽑아내는 속도(토큰 생성 속도)가 답답하게 느껴질 때가 있죠. 저도 처음엔 “이게 뭔가 싶었는데” 결국 해답은 하드웨어 가속에 있더군요. 💡

    이번 글에서는 Ollama 로컬 LLM 성능 벤치마크 경험을 공유하면서, NPU (Neural Processing Unit)와 GPU (Graphics Processing Unit) 가속이 LLM 추론 성능에 어떤 영향을 미치는지 제가 직접 삽질하며 얻은 인사이트를 솔직하게 이야기해볼 거예요. 로컬 LLM 성능 때문에 고민이셨다면, 이 글이 좋은 가이드가 될 거라고 생각합니다!

    Ollama를 활용한 로컬 LLM 아키텍처 다이어그램

    Ollama를 이용한 로컬 LLM 아키텍처의 개념도입니다. 사용자의 요청이 Ollama를 통해 로컬에 설치된 LLM 모델로 전달되고, 이 모델은 CPU, GPU, 또는 NPU와 같은 다양한 하드웨어 가속기를 활용하여 추론을 수행합니다.

    Ollama와 로컬 LLM 가속, 왜 중요할까요?

    먼저 핵심 개념들을 잠깐 짚고 넘어갈게요. 쉽게 풀어 설명해 드릴게요.

    • Ollama (올라마): 로컬 환경에서 LLM을 정말 쉽게 설치하고 실행할 수 있도록 도와주는 오픈소스 플랫폼이에요. Docker처럼 모델을 ‘pull’해서 바로 ‘run’할 수 있어서, 저 같은 인프라 엔지니어에겐 정말 친숙하고 편하더라고요.
    • 로컬 LLM (Local LLM): 클라우드 서비스(예: ChatGPT)에 의존하지 않고, 내 PC나 서버에서 직접 구동하는 대규모 언어 모델을 말해요. 프라이버시 보호, 비용 절감, 인터넷 연결 없이 사용 가능 등 여러 장점이 있죠.
    • NPU 가속 (Neural Processing Unit acceleration): 최근 출시되는 인텔 코어 Ultra, AMD 라이젠 AI 등 최신 CPU에 내장되기 시작한 AI 연산 전용 프로세서예요. 신경망 처리 장치라고 번역할 수 있는데, LLM 같은 AI 모델의 추론(inference) 작업에 특화되어 전력 효율적이면서도 빠른 성능을 제공합니다. 아직 GPU만큼 범용적이진 않지만, 노트북 같은 저전력 환경에서 강점을 보여요.
    • GPU 가속 (Graphics Processing Unit acceleration): 다들 아시는 그래픽 카드죠. 수많은 코어를 이용한 병렬 연산에 정말 강해서, LLM 추론의 핵심인 행렬 곱셈 연산에 탁월한 성능을 보여줍니다. 특히 엔비디아(NVIDIA)의 CUDA나 AMD의 ROCm 같은 플랫폼을 통해 LLM 가속에 널리 사용되고 있어요.
    • Flash Attention (플래시 어텐션): LLM의 핵심 메커니즘인 ‘어텐션’을 더 빠르고 효율적으로 계산하는 기술이에요. 메모리 대역폭 사용을 최적화해서 특히 긴 컨텍스트(context)를 처리할 때 속도와 메모리 사용량을 크게 개선해 줍니다. Ollama도 내부적으로 Flash Attention을 지원해서 성능을 끌어올리고 있어요.

    결론적으로, 로컬 LLM을 쾌적하게 쓰려면 CPU만으로는 한계가 있고, NPU나 GPU의 도움을 받아야 한다는 거예요. 특히 LLM 벤치마크를 해보면 이 차이가 정말 확연히 드러나거든요.

    Ollama 로컬 LLM 성능 벤치마크, 제가 직접 해봤습니다!

    자, 이제 실전으로 들어가 볼까요? 제가 홈랩에서 여러 환경으로 직접 테스트해 본 경험을 바탕으로 설명해 드릴게요. 처음엔 “이게 뭔가 싶었는데” 몇 번 해보니 감이 잡히더라고요.

    1. Ollama 설치 및 모델 준비

    Ollama 설치는 정말 간단해요. 공식 웹사이트에서 다운로드하거나, 리눅스라면 다음 명령어로 설치하면 돼요.

    curl -fsSL https://ollama.com/install.sh | sh

    설치 후에는 원하는 LLM 모델을 다운로드하면 돼요. 저는 테스트를 위해 가볍고 성능 좋은 Mistral 모델을 주로 사용했어요. (물론 Llama2나 다른 모델들도 테스트해봤죠.)

    ollama run mistral

    이 명령어를 실행하면 Mistral 모델이 없으면 자동으로 다운로드하고 바로 채팅 프롬프트가 떠요. 정말 편하죠? 🎉

    2. 벤치마킹 환경 설정 및 테스트

    Ollama 성능 벤치마크의 핵심은 다양한 하드웨어 환경에서 동일한 모델과 프롬프트로 테스트하는 거예요. 제가 사용한 방법은 이렇습니다.

    1. 테스트 프롬프트 선정: 항상 동일한 길이와 복잡도를 가진 프롬프트를 사용해야 해요. 예를 들어, “한국의 수도는 어디이며, 그곳의 대표적인 관광지 3곳을 설명해 주세요.” 처럼 일관된 질문을 던졌어요.
    2. 측정 지표: 주로 토큰 생성 속도 (tokens/second)를 측정했어요. Ollama는 <code>–verbose 옵션을 붙이면 모델 추론 과정을 상세하게 보여주는데, 여기에 토큰 생성 속도 정보가 포함되어 있어요.
    3. 하드웨어 환경별 테스트:
      • CPU Only: GPU나 NPU 가속 없이 순수 CPU로만 돌리는 경우예요. (제 홈랩 PC의 AMD Ryzen 7 5800X CPU로 테스트했어요.)
      • Integrated GPU (내장 GPU): 인텔 내장 그래픽이나 AMD APU의 내장 그래픽을 활용하는 경우죠. (노트북의 인텔 Iris Xe로 테스트했어요.)
      • Dedicated GPU (외장 GPU): 엔비디아 RTX 3060 같은 전용 그래픽 카드를 사용하는 경우예요. (제 데스크톱의 RTX 3060으로 테스트했어요.)
      • NPU 가속 환경: 최신 노트북의 NPU를 활용하는 경우예요. (지인의 인텔 코어 Ultra 7 노트북으로 잠시 테스트해봤어요. Ollama가 특정 백엔드를 통해 NPU를 활용하는데, 이는 드라이버 및 시스템 설정에 따라 달라요.)

    Ollama는 기본적으로 사용 가능한 가장 빠른 장치(GPU > NPU > CPU)를 자동으로 사용하려고 시도해요. 특정 장치를 사용하도록 강제하려면, 환경 변수나 구성 파일로 조정할 수 있는데, 일반적으로는 Ollama가 최적의 설정을 찾아줍니다.

    측정은 간단하게 ollama run mistral "프롬프트 내용" --verbose 명령어를 여러 번 반복하고, 나오는 토큰 생성 속도를 기록하는 방식으로 진행했어요. 최소 3회 이상 반복해서 평균값을 취하는 게 좋더라고요.

    CPU, GPU, NPU 각각의 LLM 추론 가속 역할 개념도

    LLM 추론 과정에서 CPU, GPU, NPU가 각각 어떻게 연산 작업을 분담하고 가속하는지에 대한 개념적인 표현이에요. GPU는 병렬 연산으로, NPU는 AI 전용 연산으로 효율성을 높입니다.

    ⚠️ 삽질 경험 & 트러블슈팅

    벤치마크를 진행하면서 저도 삽질 좀 했어요 ㅎㅎ. 몇 가지 겪었던 문제와 해결 방법을 공유해 드릴게요.

    • GPU 드라이버 문제: “아니 분명 GPU가 있는데 왜 CPU로만 돌지?” 했던 때가 있어요. 대부분 엔비디아 CUDA 드라이버나 AMD ROCm 드라이버가 최신이 아니거나, 제대로 설치되지 않은 경우였어요. 항상 최신 드라이버를 유지하고, 공식 가이드를 따라 설치하는 게 중요해요. 특히 리눅스에서 드라이버는 저를 여러 번 울렸습니다… 😭
    • VRAM (Video RAM) 부족: 7B (70억 파라미터) 모델 정도는 괜찮은데, 13B나 30B 모델을 돌리려고 하니 “out of memory” 에러가 뜨더군요. 이건 GPU 메모리(VRAM)가 부족하다는 뜻이에요. 이럴 땐 더 작은 모델을 쓰거나, 양자화(quantization)된 모델(예: Q4_K_M)을 사용해야 해요. 양자화는 모델의 정밀도를 낮춰 메모리 사용량을 줄이는 기술이거든요.
    • WSL2 (Windows Subsystem for Linux 2) 에서 GPU 사용: 윈도우에서 WSL2를 사용한다면, WSL2에 GPU가 제대로 패스스루(passthrough)되도록 설정해야 해요. 마이크로소프트의 WSL 공식 문서를 참고해서 GPU 드라이버를 설치하고, wsl --update로 최신 버전을 유지하는 게 중요합니다.
    • Ollama 버전 문제: 가끔 특정 버전에서 성능 저하나 버그가 있을 수 있어요. ollama update 명령어로 항상 최신 버전을 유지하는 게 좋아요. 새로운 하드웨어 가속 기술 지원은 주로 최신 버전에서 이뤄지거든요.

    벤치마크 결과: NPU/GPU 가속 효과는 확실하네요!

    제가 직접 여러 환경에서 Ollama 성능 벤치마크를 해보니, 예상했던 대로 NPU/GPU 가속의 효과는 정말 컸어요. 구체적인 수치를 지어낼 순 없지만, 제 경험을 바탕으로 일반적인 경향을 말씀드릴게요.

    확실히 CPU만으로는 한계가 명확했어요. 텍스트를 생성하는 속도가 답답할 정도로 느리더군요. 하지만 내장 GPU만으로도 CPU 단독 대비 2~3배 정도의 성능 향상을 체감할 수 있었어요. 특히 외장 GPU, 즉 엔비디아 RTX 계열 같은 전용 그래픽 카드를 사용했을 때는 그야말로 “드디어 됐다!” 싶을 정도로 쾌적한 속도를 보여줬어요. CPU 단독 대비 5~10배 이상의 성능 향상을 보이는 경우도 많았어요.

    NPU 가속의 경우, 아직은 GPU만큼 범용적인 성능을 보여주진 않지만, 전력 효율 측면에서 정말 인상적이었어요. 노트북에서 배터리 소모를 최소화하면서도, CPU만 쓰는 것보다는 훨씬 나은 성능을 제공하더라고요. 앞으로 NPU 성능이 더 발전하면 노트북에서 로컬 LLM을 돌리는 표준이 되지 않을까 싶어요.

    Flash Attention의 효과도 분명했어요. Ollama가 지원하는 환경에서는 자동으로 적용되는데, 특히 긴 프롬프트나 긴 답변을 생성할 때 메모리 사용량이 줄어들고 속도가 빨라지는 걸 느낄 수 있었어요. 이건 마치 “숨겨진 터보 부스터” 같은 느낌이었습니다. 🚀

    CPU, GPU, NPU 하드웨어별 LLM 추론 속도 비교 차트

    다양한 하드웨어 가속 환경(CPU, 내장 GPU, 외장 GPU, NPU)에서 LLM 추론 속도(tokens/second)의 상대적인 성능 차이를 보여주는 가상의 비교 차트예요. 외장 GPU가 가장 높은 성능을, CPU가 가장 낮은 성능을 보이는 경향을 나타냅니다.

    마무리하며: 로컬 LLM, 하드웨어가 곧 성능입니다

    이번 Ollama 로컬 LLM 성능 벤치마크를 통해 다시 한번 하드웨어의 중요성을 깨달았어요. 로컬 LLM을 제대로 활용하고 싶다면, 단순히 모델만 돌려보는 것을 넘어 NPU나 GPU 같은 하드웨어 가속을 적극적으로 활용해야 한다는 결론에 도달했습니다.

    물론 좋은 하드웨어는 비용이 들지만, 클라우드 LLM API 비용을 장기적으로 봤을 때 충분히 투자 가치가 있다고 생각해요. 특히 프라이버시를 중시하거나, 외부 네트워크 없이 AI를 활용해야 하는 환경에서는 로컬 LLM이 유일한 대안이 될 수 있거든요.

    여러분도 직접 Ollama를 설치하고, 가지고 계신 하드웨어로 다양한 모델을 돌려보면서 LLM 벤치마크를 해보시길 추천해요. 제가 겪었던 삽질 경험들을 참고해서, 여러분은 좀 더 수월하게 쾌적한 로컬 LLM 환경을 구축하시길 바랍니다! 궁금한 점이 있다면 언제든 댓글로 남겨주세요. 다음에는 Ollama로 커스텀 모델을 만들거나 파인튜닝하는 방법에 대해서도 다뤄볼 생각이에요. 기대해주세요! 👋

    Ollama 로컬 LLM 가속화를 위한 핵심 요약 인포그래픽

    Ollama를 이용한 로컬 LLM 가속화를 위한 주요 요소들을 요약한 인포그래픽이에요. 하드웨어 가속(GPU, NPU), 최신 드라이버, 모델 양자화, Flash Attention 등의 중요성을 시각적으로 보여줍니다.

  • [AI] Ollama 1년 실사용 회고: 로컬 LLM 개발, 기대와 현실 사이

    [AI] Ollama 1년 실사용 회고: 로컬 LLM 개발, 기대와 현실 사이

    Ollama 1년 실사용 회고: 로컬 LLM 개발, 기대와 현실 사이

    안녕하세요, 13년차 서버실 지킴이, 13년차의 서버실입니다. 오늘은 제가 지난 1년간 홈랩에서 직접 경험하고 삽질했던 Ollama(올라마)에 대한 솔직한 회고를 들려드리려고 합니다. 최근 몇 년간 AI, 특히 LLM(Large Language Model, 거대 언어 모델)이 IT 업계를 뜨겁게 달구고 있잖아요? 인프라 엔지니어로서 저도 이 거대한 흐름을 놓칠 수 없었습니다.

    처음에는 GPT 같은 클라우드 기반 LLM을 사용하면서 그 성능에 감탄했지만, 문득 이런 생각이 들더라고요. ‘내가 만든 서비스에 LLM을 붙이려면 비용은 어떻게 감당하지? 그리고 중요한 건, 민감한 우리 회사 데이터를 외부 서비스에 보내도 괜찮을까?’ ⚠️ 이런 고민을 하다 보면 결국 로컬 LLM(Local LLM)으로 눈을 돌리게 됩니다. 하지만 로컬에서 LLM을 돌린다는 게 말처럼 쉽지 않았어요. 복잡한 환경 설정, 모델 다운로드, 의존성 관리… 솔직히 엄두가 안 났거든요.

    그러다 1년 전쯤, Ollama라는 녀석을 처음 만났습니다. “어? 이거 뭔가 다르다!” 싶더라고요. 설치도 간편하고, 모델 관리도 쉽다는 얘기에 ‘이거다!’ 싶어서 바로 홈랩에 세팅하고 이것저것 굴려봤습니다. 지난 1년간의 경험을 바탕으로, Ollama를 통한 로컬 LLM 개발이 과연 어떤 기대를 충족시켜주고 또 어떤 현실적인 벽에 부딪혔는지, 제 삽질 경험과 함께 솔직하게 풀어보겠습니다. 혹시 로컬 LLM 개발에 관심 있으신 분들이라면 제 이야기가 조금이나마 도움이 되었으면 좋겠네요. 😊

    Ollama 아키텍처 및 로컬 LLM 실행 흐름 다이어그램 (Ollama, 로컬 LLM 키워드 포함)

    Ollama는 복잡한 LLM 모델을 마치 Docker 이미지처럼 손쉽게 다운로드하고 실행할 수 있도록 돕는 도구입니다. 이 그림은 Ollama를 통해 로컬 환경에서 LLM이 어떻게 구동되는지 간략하게 보여줍니다.

    💡 Ollama, 로컬 LLM의 진입 장벽을 낮추다

    Ollama가 정확히 무엇인지부터 짚고 넘어갈까요? Ollama(올라마)는 쉽게 말해, 여러분의 컴퓨터에서 오픈소스 LLM(Open-source Large Language Model)을 아주 쉽게 설치하고 실행할 수 있도록 도와주는 도구입니다. 기존에는 로컬 LLM을 돌리려면 Python 환경 설정부터 시작해서 PyTorch, CUDA 같은 라이브러리 설치, 모델 가중치(weights) 다운로드, 그리고 복잡한 코드 작성까지, 정말 해야 할 일이 많았거든요. 저도 처음엔 이 과정에서만 몇 번이나 포기할 뻔했습니다.

    근데 Ollama는 이런 복잡한 과정을 컨테이너 방식으로 단순화시켜 줍니다. 마치 Docker(도커)로 이미지를 다운받아 실행하듯이, <code>ollama pull [모델명] 명령 하나로 원하는 로컬 LLM 모델을 내려받고, ollama run [모델명] 명령으로 바로 실행할 수 있게 해주는 거죠. 이게 진짜 혁신적이라고 느꼈어요. 🚀 덕분에 저 같은 인프라 엔지니어는 물론, AI 개발에 막 입문하는 분들도 훨씬 쉽게 LLM을 만져볼 수 있게 된 겁니다.

    Ollama는 다양한 오픈소스 LLM들을 지원합니다. 대표적으로 Meta의 Llama 2(라마 2), Mistral AI의 Mistral(미스트랄), Google의 Gemma(젬마) 등 유명 모델들을 공식적으로 제공하고 있어요. 게다가 커뮤니티에서 만들어진 수많은 모델들도 쉽게 사용할 수 있어서 선택의 폭이 엄청 넓습니다. 로컬 LLM 개발 환경을 구축하고 싶다면 Ollama는 정말 탁월한 선택지라고 자신 있게 말씀드릴 수 있습니다.

    🛠️ 홈랩에서 Ollama 설치하고 첫 LLM 실행하기

    자, 그럼 실제로 제 홈랩에서 Ollama를 어떻게 설치하고 첫 LLM을 실행했는지 보여드릴게요. 저는 주로 Linux 환경을 사용하는데, macOS나 Windows(WSL2)에서도 비슷하게 적용할 수 있습니다. 설치 과정은 정말 간단해서 놀라실 거예요.

    1. Ollama 설치

    터미널을 열고 다음 명령어를 입력하면 끝입니다. 스크립트가 알아서 필요한 것들을 설치해줍니다. 💡

    curl -fsSL https://ollama.com/install.sh | sh
    

    설치가 완료되면 ollama 명령어를 입력해서 정상적으로 설치되었는지 확인할 수 있습니다.

    ollama
    

    아마 사용 가능한 명령어 목록이 주르륵 뜰 거예요. 🎉

    2. LLM 모델 다운로드

    다음으로 원하는 로컬 LLM 모델을 다운로드합니다. 저는 가장 먼저 Llama 2(라마 2)를 다운로드해봤어요. 7B(70억 파라미터) 모델이 로컬에서 돌려보기 적당하거든요.

    ollama pull llama2
    

    모델 크기에 따라 시간이 좀 걸릴 수 있습니다. 제 홈랩 인터넷이 광랜이라 금방 내려받았지만, 모델이 몇 GB씩 하는 경우도 많으니 인내심을 가지세요. ㅎㅎ

    3. LLM 모델 실행 및 대화

    모델 다운로드가 완료되면 바로 실행해볼 수 있습니다.

    ollama run llama2
    

    명령어를 입력하면 터미널에서 Llama 2와 대화를 시작할 수 있습니다. 처음으로 로컬에서 제가 질문한 내용에 LLM이 답변하는 걸 봤을 때의 그 감동이란… 직접 경험해보지 않으면 모릅니다! 🤩

    >>> Tell me a joke.
    Why don't scientists trust atoms?
    Because they make up everything!
    

    4. 프로그래밍 연동 (Python 예시)

    Ollama는 REST API를 제공해서 다양한 프로그래밍 언어에서 쉽게 연동할 수 있습니다. 저는 Python으로 간단한 챗봇을 만들어보면서 로컬 LLM 연동 테스트를 해봤어요.

    import ollama
    
    # 로컬 Ollama 서버에 요청
    response = ollama.chat(model='llama2', messages=[
      {
        'role': 'user',
        'content': 'Why is the sky blue?',
      },
    ])
    print(response['message']['content'])
    

    이렇게 코드로 쉽게 로컬 LLM의 기능을 활용할 수 있다니, 개발자 입장에서 정말 편하더라고요. AI 개발 환경 구축이 이렇게 쉬워지다니, 격세지감을 느꼈습니다.

    Ollama 모델 다운로드 및 실행 터미널 스크린샷 (Ollama 설치, 로컬 LLM 개발 키워드 포함)

    Ollama를 설치하고 Llama 2 모델을 다운로드하여 실행하는 과정을 터미널 스크린샷으로 담아봤습니다. 몇 줄의 간단한 명령어로 로컬 LLM 개발 환경이 구축되는 것을 직접 확인할 수 있습니다.

    ⚠️ 1년간의 삽질과 트러블슈팅: 기대와 현실 사이

    Ollama가 로컬 LLM의 진입 장벽을 낮춰준 것은 분명하지만, 그렇다고 마냥 순탄하기만 했던 건 아닙니다. 1년 동안 삽질도 많이 했고, 현실적인 제약에 부딪히면서 ‘아, 이게 아직은 갈 길이 멀구나’ 하는 생각도 많이 했어요.

    1. 하드웨어 제약: GPU와 VRAM의 중요성

    가장 크게 느낀 점은 역시 하드웨어 제약입니다. 특히 GPU(Graphics Processing Unit, 그래픽 처리 장치)의 중요성은 아무리 강조해도 지나치지 않아요. 제 홈랩에는 NVIDIA GeForce RTX 3060 12GB 모델이 장착되어 있습니다. 처음에는 이 정도면 꽤 좋은 GPU라고 생각했는데, 로컬 LLM을 돌려보니 바로 한계를 느꼈습니다.

    작은 모델(예: Llama 2 7B)은 그럭저럭 돌아가지만, 조금만 더 큰 모델(예: 13B 이상)이나 여러 모델을 동시에 띄우려고 하면 VRAM(Video RAM, 비디오 램)이 순식간에 동나 버립니다. VRAM이 부족하면 로컬 LLM은 CPU(Central Processing Unit, 중앙 처리 장치)로 동작하게 되는데, 그러면 응답 속도가 현저히 느려져서 사실상 실사용이 어렵습니다. 답변 하나 받는 데 몇 분씩 걸리니 답답해서 못 쓰겠더라고요. 😥

    그래서 저는 주로 Quantization(양자화)된 모델을 사용했습니다. 양자화는 모델의 정밀도를 낮춰 크기와 VRAM 사용량을 줄이는 기법인데, Q4_0, Q8_0 같은 버전들이 대표적입니다. 성능 저하가 있긴 하지만, 제 RTX 3060으로는 양자화된 13B 모델도 버겁게 돌려야 했어요. 7B Q4_0 모델 정도가 그나마 쾌적하게 느껴지는 마지노선이었습니다.

    2. 발열과 소음

    홈랩에서 밤늦게 로컬 LLM을 돌리다 보면 GPU 팬 소리가 비행기 이륙하는 소리처럼 커지곤 했습니다. 😅 높은 GPU 사용률은 곧 높은 발열로 이어지고, 팬은 그 열을 식히기 위해 미친 듯이 돌아갑니다. 여름철에는 에어컨을 켜지 않으면 방 온도가 훅 올라갈 정도였어요. 조용한 환경을 선호하는 분들에게는 이것도 큰 단점으로 다가올 수 있습니다.

    3. 클라우드 LLM과의 성능 차이

    아무리 로컬 LLM이 발전했다고 해도, 클라우드에서 제공하는 최신, 최고 성능의 LLM(예: GPT-4, Claude 3)과는 아직 넘을 수 없는 벽이 존재합니다. 응답 속도, 답변의 품질, 복잡한 추론 능력 등 여러 면에서 차이가 컸어요. 오픈소스 LLM도 빠르게 발전하고 있지만, 최상위 모델들은 여전히 막대한 컴퓨팅 자원을 요구하거든요. 그래서 저는 중요한 작업이나 복잡한 문제는 클라우드 LLM을, 개인 학습이나 아이디어 검증은 Ollama를 활용하는 방식으로 병행하고 있습니다.

    GPU VRAM 및 CPU 사용률 모니터링 대시보드 (Ollama 성능, GPU 키워드 포함)

    로컬 LLM 개발에서 가장 중요한 요소 중 하나는 바로 하드웨어입니다. 특히 GPU의 VRAM은 모델의 성능과 직결되죠. 이 이미지는 제가 Ollama로 로컬 LLM을 실행했을 때 GPU와 CPU 사용률을 모니터링한 화면입니다.

    ✅ Ollama로 얻은 것과 아쉬웠던 점

    1년 동안 Ollama를 사용하면서 느꼈던 점들을 장단점으로 나눠서 정리해봤습니다. 로컬 LLM 사용 후기를 찾으시는 분들에게 객관적인 정보가 되었으면 좋겠네요.

    좋았던 점 (기대 이상)

    1. LLM 개념 학습 및 실험 환경 제공: 가장 큰 장점이라고 생각합니다. LLM이 어떻게 작동하는지, 프롬프트 엔지니어링은 어떻게 해야 하는지 등을 실제 로컬 LLM 모델을 직접 돌려보면서 체득할 수 있었습니다. 시행착오를 거치며 배우는 재미가 쏠쏠했어요.
    2. 데이터 프라이버시 유지: 민감한 데이터를 외부로 유출할 걱정 없이 내부망에서 로컬 LLM을 활용할 수 있다는 점은 큰 메리트입니다. 특히 기업 환경에서는 이 부분이 매우 중요하겠죠.
    3. 오프라인 환경 개발 가능: 인터넷이 연결되지 않은 환경에서도 로컬 LLM을 활용한 개발 및 테스트가 가능했습니다. 물론 모델 다운로드는 필요하지만요.
    4. 다양한 오픈소스 모델 탐색: Llama 2, Mistral, Gemma 외에도 Code Llama, Orca, TinyLlama 등 수많은 오픈소스 모델들을 쉽게 다운로드하고 비교 실험해볼 수 있었습니다. 각각의 모델이 어떤 특징을 가지는지 파악하는 데 큰 도움이 되었어요.

    아쉬웠던 점 (현실의 벽)

    1. 여전히 높은 하드웨어 요구사항: 앞서 언급했듯이, ‘괜찮은’ 성능을 내려면 고사양 GPU가 필수적입니다. 일반적인 게이밍 PC로는 로컬 LLM의 한계가 명확하더라고요.
    2. 대규모 모델 실행의 어려움: 70B(700억 파라미터) 이상의 로컬 LLM 모델은 실사용하기 어렵습니다. 최소 24GB 이상의 VRAM이 필요하며, 고가의 전문가용 GPU(예: A6000, H100)가 아니면 엄두를 내기 힘들죠.
    3. 클라우드 서비스 대비 부족한 성능: 답변의 품질, 속도, 일관성 등 여러 면에서 클라우드 LLM 서비스에 비해 아쉬움이 남습니다. 특히 장문의 글을 생성하거나 복잡한 추론을 요구할 때 차이가 두드러졌어요.
    4. 모델 간 전환 시 부하: 여러 로컬 LLM 모델을 동시에 띄우는 것도 VRAM 문제로 어렵고, 모델을 전환할 때마다 VRAM에 다시 로딩하는 과정이 필요해서 시간이 걸립니다.
    Ollama 장점과 한계 비교 인포그래픽 (Ollama 장점, 로컬 LLM 한계 키워드 포함)

    1년간 Ollama를 사용하며 느낀 점들을 장점과 단점으로 정리해봤습니다. 이 인포그래픽은 로컬 LLM 개발 환경으로서 Ollama가 제공하는 가치와 마주하게 되는 현실적인 한계를 시각적으로 보여줍니다.

    🚀 Ollama, 앞으로의 방향과 나의 활용법

    결론적으로, Ollama는 로컬 LLM 개발의 진입 장벽을 혁신적으로 낮춰준 아주 훌륭한 도구입니다. 제가 13년차 인프라 엔지니어로서 새로운 기술을 경험하고 학습하는 데 정말 큰 도움을 주었습니다. 🎉 하지만 아직은 명확한 한계도 가지고 있다는 점을 솔직히 말씀드리고 싶어요.

    그럼에도 불구하고 Ollama와 같은 도구들의 발전은 앞으로도 계속될 것이고, GPU 기술도 점점 더 발전할 것이기에 로컬 LLM의 미래는 밝다고 생각합니다. 제 홈랩에서도 지속적으로 Ollama를 활용하여 다음과 같은 방식으로 AI 개발 환경을 운영할 계획입니다.

    • 개인 학습 및 개념 증명(POC): 새로운 오픈소스 LLM이 나오면 Ollama로 빠르게 테스트해보고, 간단한 아이디어를 검증하는 용도로 사용.
    • RAG(Retrieval Augmented Generation) 실험: 사내 문서나 개인 데이터를 기반으로 로컬 LLM이 답변하도록 하는 RAG 시스템을 로컬에서 구축하고 실험하는 데 활용. (이 부분은 다음 글에서 자세히 다뤄볼까 합니다!)
    • 데이터 프라이버시가 중요한 소규모 서비스: 외부망 노출이 어려운 특정 기능에 로컬 LLM을 연동할 필요가 있을 때.

    물론 대규모 서비스나 높은 성능, 복잡한 추론 능력이 필요한 경우에는 여전히 클라우드 기반 LLM 서비스가 유리할 겁니다. 중요한 것은 각자의 상황과 필요에 맞게 로컬 LLM과 클라우드 LLM을 적절히 조합하여 사용하는 지혜가 필요하다는 점입니다.

    13년차 인프라 엔지니어의 Ollama 1년 실사용 후기, 어떠셨나요? 로컬 LLM 개발에 대한 기대와 현실 사이에서 저와 비슷한 고민을 하셨던 분들이라면, 제 경험이 조금이나마 길잡이가 되었으면 좋겠습니다. 다음에는 Ollama와 RAG를 연동하는 방법에 대해 더 깊이 있는 내용을 다뤄볼 예정이니, 많은 기대 부탁드립니다! 😉

    미래 로컬 LLM 개발 환경 상상도 (AI 개발 미래, 로컬 LLM 전망 키워드 포함)

    Ollama는 로컬 LLM 개발의 가능성을 열어주었지만, 아직 가야 할 길이 멀다고 생각합니다. 미래에는 더욱 강력한 개인 워크스테이션과 효율적인 로컬 LLM 모델로, 지금보다 훨씬 더 풍부한 경험을 할 수 있기를 기대해봅니다.