본문 바로가기
IT/AI

[AI] Mac에서 로컬 LLM 성능 최적화: MLX vs GGUF 벤치마크 비교

by 수누다 2026. 6. 16.

Mac에서 로컬 LLM 성능 최적화: MLX vs GGUF 벤치마크 비교

안녕하세요, 13년차 서버실을 지키는 인프라 엔지니어입니다. 요즘 AI 정말 핫하잖아요? 저도 새로운 기술 트렌드는 항상 설레게 하더라고요. 특히 로컬 LLM (Large Language Model, 거대 언어 모델) 기술은 개인 정보 보호나 비용 절감, 그리고 인터넷 연결 없이도 AI를 활용할 수 있다는 점에서 홈랩 운영자들에게는 정말 매력적입니다. 근데 막상 Mac에서 로컬 LLM을 돌리려고 하면, MLXGGUF라는 두 가지 주요 선택지 앞에서 고민하게 되더라고요. '과연 어떤 방식이 내 Mac에서 최고의 성능을 낼까?'

저도 이 질문에 대한 답을 찾기 위해 직접 삽질 좀 했습니다. M 시리즈 칩셋이 탑재된 Mac에서 로컬 LLM 성능을 최적화하는 방법을 고민하며 MLX와 GGUF를 비교 벤치마킹해본 경험을 오늘 여러분과 공유하려 합니다. 과연 누가 더 빠르고 효율적일까요? 제 경험과 함께 그 답을 찾아보시죠!

Mac에서 로컬 LLM 구동을 위한 MLX 및 GGUF 기반의 전체 시스템 아키텍처 다이어그램

로컬 LLM 구동 환경의 주요 구성 요소를 나타내는 다이어그램입니다.

1. 로컬 LLM, 왜 Mac에서 돌려야 할까요?

사실 클라우드 서비스에서 LLM을 이용하는 게 훨씬 편하고 강력하죠. 하지만 저처럼 개인적인 프로젝트나 민감한 데이터를 다룰 때는 로컬 환경이 주는 장점이 큽니다. 개인 정보 보호(Privacy)는 물론이고, API 호출 비용 걱정 없이 마음껏 실험해볼 수 있다는 점, 그리고 인터넷 연결이 끊겨도 AI 모델을 사용할 수 있다는 점이 가장 큰 매력이더라고요. 특히 Apple Silicon (애플 실리콘) 칩셋이 탑재된 Mac은 통합 메모리 아키텍처 (Unified Memory Architecture) 덕분에 CPU와 GPU가 메모리를 공유하면서 매우 효율적인 연산이 가능하거든요. 그래서 Mac은 로컬 LLM 구동에 상당히 유리한 환경을 제공합니다.

2. MLX와 GGUF: 로컬 LLM의 두 거대 산맥

본격적인 벤치마크에 앞서, 오늘 비교할 두 주인공인 MLX와 GGUF에 대해 간단히 짚고 넘어갈게요. 쉽게 말해, LLM을 Mac에서 돌리기 위한 두 가지 주요 '기술 스택'이라고 보시면 됩니다.

2.1. Apple MLX: Mac을 위한 맞춤 옷

MLX는 Apple이 직접 개발한 머신러닝 프레임워크입니다. Apple Silicon (애플 실리콘) 칩셋에 최적화되어 있어서, Mac 하드웨어의 성능을 최대한 끌어낼 수 있도록 설계되었죠. Pythonic API를 제공해서 개발자들이 쉽게 사용할 수 있고, 통합 메모리 덕분에 CPU와 GPU 간의 데이터 전송 오버헤드(Overhead)가 거의 없다는 장점이 있습니다.

제가 처음 MLX를 봤을 때는 'Apple이 또 뭘 만들었네?' 싶었는데, 직접 써보니 정말 물건이더라고요. 특히 M 시리즈 칩셋의 성능을 극한까지 활용하려는 분들에게는 최적의 선택지라고 생각합니다. 처음엔 모델 변환 과정이 좀 복잡했지만, 요즘은 `mlx-lm` 같은 도구들 덕분에 훨씬 편해졌어요. 🎉

2.2. GGUF: 범용성과 유연성의 대표주자

GGUF (GGML UniFied Format)는 사실 GGML (Georgi Gerganov's Machine Learning)에서 발전한 파일 포맷입니다. LLM 모델을 다양한 하드웨어에서 효율적으로 실행하기 위해 설계되었죠. CPU, GPU, 그리고 NPU(Neural Processing Unit) 등 다양한 장치에서 동작할 수 있도록 범용성을 높인 것이 특징입니다.

GGUF의 핵심은 바로 양자화 (Quantization)입니다. 양자화는 모델의 정밀도를 낮춰 메모리 사용량과 연산량을 줄이는 기술입니다. 쉽게 말해, 32비트 부동소수점(float32)으로 표현되던 모델 가중치를 8비트 정수(int8)나 4비트 정수(int4) 등으로 줄이는 거죠. 이렇게 하면 모델 크기가 확 줄어들어서, 적은 메모리를 가진 Mac에서도 큰 모델을 돌릴 수 있게 됩니다. `llama.cpp` 프로젝트가 GGUF 모델을 활용하는 대표적인 예시이고요.

GGUF 덕분에 제 오래된 MacBook Air M1 (16GB RAM)에서도 Llama 3 8B 같은 거대 모델을 돌려볼 수 있었죠. 처음엔 양자화된 모델의 성능이 많이 떨어질까 걱정했는데, 생각보다 괜찮아서 놀랐던 기억이 납니다. 💡

특징 MLX GGUF
개발 주체 Apple Georgi Gerganov (커뮤니티 기반)
최적화 대상 Apple Silicon (M 시리즈 칩셋) 다양한 CPU, GPU, NPU (범용)
주요 장점 최고의 Mac 성능, 통합 메모리 활용, Pythonic 범용성, 다양한 모델 지원, 양자화 효율
주요 단점 Apple 생태계 한정, 모델 변환 필요(과거), 상대적으로 적은 모델 Mac 최적화는 MLX 대비 낮을 수 있음, `llama.cpp` 빌드 필요
핵심 기술 통합 메모리 아키텍처, Apple Metal API 양자화, `llama.cpp`

3. 실전 구현: Mac에서 로컬 LLM 환경 구축 및 벤치마크 준비

이제 이론은 충분하니, 제 홈랩에서 직접 진행했던 벤치마크 환경 구축 과정을 공유해드릴게요. Llama 3 8B 모델을 기준으로 설명하겠습니다.

3.1. 환경 준비: 파이썬 가상 환경 구축

인프라 엔지니어라면 환경 분리의 중요성을 잘 아시겠죠? 파이썬 프로젝트마다 가상 환경을 만들어주는 것이 정말 중요합니다. 저는 `venv`를 사용했어요.

mkdir local-llm-benchmark
cd local-llm-benchmark
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip

이렇게 하면 깨끗한 환경에서 시작할 수 있습니다. 💡

3.2. MLX 기반 LLM 실행: Llama 3 8B (MLX 버전)

MLX 기반 모델을 실행하려면 `mlx-lm` 라이브러리를 설치해야 합니다. Llama 3 8B Instruct 모델을 사용했는데, MLX 공식 모델 저장소나 Hugging Face의 mlx-lm 관련 모델들을 활용하면 됩니다.

pip install mlx-lm

# 모델 다운로드 및 실행 (첫 실행 시 모델 자동 다운로드)
# 주의: 모델명은 mlx-lm 문서에서 지원하는 정확한 ID를 사용하세요
mlx_lm generate --model meta-llama/Llama-3-8B-Instruct --prompt "Explain local LLM to me in simple terms." --max-tokens 128

처음엔 모델 찾는 것도 일이었는데, `mlx-lm`이 Hugging Face의 다양한 모델을 지원하면서 정말 편해졌어요. Llama 3 8B Instruct는 Meta의 Llama 3 8B를 MLX 포맷으로 변환한 모델이며, mlx-lm에서 직접 지원합니다.

3.3. GGUF 기반 LLM 실행: llama.cpp와 Llama 3 8B (GGUF 버전)

GGUF 모델은 주로 `llama.cpp` 프로젝트를 통해 실행합니다. 먼저 `llama.cpp`를 클론하고 빌드해야 합니다.

git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
make

빌드가 완료되면 GGUF 모델을 다운로드해야 합니다. Hugging Face에서 'Llama 3 8B GGUF'로 검색하면 다양한 양자화 레벨의 모델들을 찾을 수 있습니다. 저는 `Meta-Llama-3-8B-Instruct-GGUF` 저장소에서 <code>llama-3-8b-instruct.Q4_K_M.gguf 파일을 다운로드했습니다.

# 예시: curl이나 직접 다운로드 툴을 사용해 모델 파일 다운로드
# (Hugging Face에서 직접 다운로드 링크를 찾아야 합니다)
curl -L -o models/llama-3-8b-instruct.Q4_K_M.gguf \
  https://huggingface.co/bartowski/Meta-Llama-3-8B-Instruct-GGUF/resolve/main/llama-3-8b-instruct.Q4_K_M.gguf

# GGUF 모델 실행
./main -m models/llama-3-8b-instruct.Q4_K_M.gguf -p "Explain local LLM to me in simple terms." -n 128

GGUF 모델은 Hugging Face (허깅페이스)에 정말 많아서 선택지가 넓더라고요. 양자화 레벨(Q4_K_M, Q5_K_M 등)에 따라 성능과 메모리 사용량이 달라지니, 본인 Mac의 램 용량에 맞춰 선택하는 것이 중요합니다. ⚠️

3.4. 벤치마크 스크립트: 성능 측정 도구 만들기

정확한 벤치마크를 위해 간단한 Python 스크립트를 만들었습니다. `tokens/sec` (초당 생성 토큰 수)를 측정하는 방식입니다.

import time
import subprocess

def run_benchmark(command, model_name, prompt, max_tokens=128, iterations=3):
    total_tokens_per_sec = 0
    print(f"\n--- Benchmarking {model_name} ---")
    for i in range(iterations):
        start_time = time.time()
        process = subprocess.run(command, shell=True, capture_output=True, text=True)
        end_time = time.time()
        duration = end_time - start_time

        # llama.cpp 출력에서 tokens/sec 파싱 (MLX는 직접 계산)
        tokens_per_sec = 0
        if "llama.cpp" in model_name:
            for line in process.stderr.splitlines():
                if "tokens / sec" in line:
                    try:
                        tokens_per_sec = float(line.split('(')[1].split(' tokens / sec')[0])
                        break
                    except ValueError:
                        pass
            if tokens_per_sec == 0: # Fallback for llama.cpp if parsing fails
                tokens_per_sec = max_tokens / duration
        else: # For MLX, approximate using max_tokens / duration
            tokens_per_sec = max_tokens / duration

        print(f"Iteration {i+1}: Duration = {duration:.2f}s, Tokens/sec = {tokens_per_sec:.2f}")
        total_tokens_per_sec += tokens_per_sec

    avg_tokens_per_sec = total_tokens_per_sec / iterations
    print(f"Average {model_name} Tokens/sec: {avg_tokens_per_sec:.2f}\n")
    return avg_tokens_per_sec

# MLX Command (adjust as needed)
mlx_command = "mlx_lm generate --model meta-llama/Llama-3-8B-Instruct --prompt \"Explain local LLM to me in simple terms.\" --max-tokens 128 --stream --no-progress"

# GGUF Command (adjust model path and other params)
gguf_command = "./llama.cpp/main -m ./llama.cpp/models/llama-3-8b-instruct.Q4_K_M.gguf -p \"Explain local LLM to me in simple terms.\" -n 128 --temp 0.0 --seed 0 --silent-prompt"

# Run benchmarks
mlx_result = run_benchmark(mlx_command, "MLX Llama 3 8B", "Explain local LLM to me in simple terms.")
gguf_result = run_benchmark(gguf_command, "GGUF Llama 3 8B (Q4_K_M)", "Explain local LLM to me in simple terms.")

print("\n--- Final Comparison ---")
print(f"MLX Average Tokens/sec: {mlx_result:.2f}")
print(f"GGUF Average Tokens/sec: {gguf_result:.2f}")

이 스크립트는 각 모델을 지정된 프롬프트로 여러 번 실행하고, 걸린 시간을 측정하여 초당 토큰 수를 계산합니다. `llama.cpp`는 자체적으로 `tokens / sec`를 출력해주지만, MLX는 직접 계산해야 합니다. 프롬프트와 `max-tokens`는 동일하게 유지해서 공정한 비교가 되도록 했습니다.

Mac에서 MLX와 GGUF 로컬 LLM 환경을 구축하고 설정하는 단계별 다이어그램

MLX와 GGUF 기반 LLM을 Mac에서 실행하기 위한 기본적인 환경 설정 과정을 보여주는 다이어그램입니다.

4. 삽질의 흔적들: 로컬 LLM 구축 중 겪은 문제와 해결법

13년차 엔지니어도 삽질은 피할 수 없죠. 저도 이번 벤치마크를 진행하면서 몇 가지 문제에 부딪혔고, 이를 해결하는 과정에서 많은 것을 배웠습니다. ⚠️

4.1. "메모리 부족 에러 (Out Of Memory)"

가장 흔하게 겪는 문제입니다. 처음엔 '어? 왜 안 돌아가지?' 싶었는데, 램(RAM) 용량 때문이더라고요. 특히 8GB 램을 가진 Mac에서는 큰 모델을 돌리기가 정말 어렵습니다. 7B(70억 파라미터) 모델만 해도 최소 10~12GB 정도의 램을 요구하는 경우가 많거든요.

  • 해결책: 양자화 레벨이 낮은 GGUF 모델을 사용하거나, 더 작은 파라미터 수의 모델 (예: 2B, 3B)을 선택했습니다. Q2_K나 Q3_K 같은 더 극단적인 양자화 모델은 메모리 사용량을 더욱 줄여줍니다. MLX도 모델을 `float16` 대신 `int8` 등으로 양자화할 수 있는 옵션을 제공하니 활용해보세요.

4.2. 느린 추론 속도: 기대보다 느릴 때

'M 시리즈 Mac인데 왜 이렇게 느리지?' 하고 답답했던 적이 있습니다. 아무리 최적화가 잘 되어 있다고 해도, 모델 자체가 크거나 설정이 잘못되면 속도는 실망스러울 수밖에 없죠.

  • 해결책: `llama.cpp`의 경우 -n (생성할 토큰 수), -t (쓰레드 수), -b (배치 사이즈)와 같은 파라미터를 조절하여 최적의 값을 찾아야 합니다. 보통 M 시리즈 Mac은 코어 수가 많으니, `num_threads`를 Mac의 물리 코어 수에 가깝게 설정하면 성능 향상에 도움이 됩니다. MLX의 경우 `batch_size`를 조절해볼 수 있습니다.

4.3. 설치 의존성 충돌: 가상 환경의 중요성

수많은 `pip install`과 `brew install` 속에서 헤매다가, 결국 파이썬 라이브러리 버전 충돌을 겪었습니다. 특히 `tensorflow`, `pytorch` 같은 다른 ML 라이브러리와 함께 사용하면 더 심하죠.

  • 해결책: 항상 파이썬 가상 환경(Python Virtual Environment)을 사용하는 것이 중요합니다. `venv`나 `conda`를 사용하면 각 프로젝트마다 독립적인 환경을 구축할 수 있어서, 이런 의존성 문제를 깔끔하게 해결할 수 있습니다. 제가 위에 환경 준비 섹션에서 `venv`를 강조한 이유이기도 합니다. ✅

5. MLX vs GGUF: 벤치마크 결과 분석

자, 이제 가장 궁금해하실 벤치마크 결과입니다. 저는 제 홈랩에 있는 Mac Studio M2 Ultra (128GB RAM)와 MacBook Air M1 (16GB RAM)에서 Llama 3 8B 모델(MLX 버전과 GGUF Q4_K_M 버전)을 대상으로 테스트해봤습니다. 동일한 프롬프트와 `max-tokens=128`을 사용하여 초당 생성 토큰 수(tokens/sec)를 측정했습니다.

결론부터 말씀드리자면, 제 환경에서는 MLX가 전반적으로 더 높은 `tokens/sec`를 기록했습니다.

  • MLX는 M 시리즈 칩셋의 통합 메모리 아키텍처를 최대한 활용하는 덕분인지, CPU와 GPU 사이의 데이터 전송 병목 현상 없이 매우 효율적인 연산을 보여줬습니다. 특히 M2 Ultra와 같은 고성능 칩셋에서는 MLX의 최적화가 더욱 빛을 발하더라고요. LLM 추론(inference) 시 메모리와 컴퓨팅 리소스를 동시에 사용하는 패턴에서 MLX의 통합 메모리 이점이 극대화되는 것을 체감했습니다.
  • GGUF (llama.cpp)도 꾸준한 최적화 덕분에 MLX와 큰 차이를 보이지 않는 경우도 있었지만, 동일한 양자화 레벨에서는 MLX가 조금 더 우위를 점하는 경우가 많았습니다. 하지만 GGUF는 다양한 양자화 레벨을 지원하고, `llama.cpp`의 지속적인 업데이트 덕분에 범용성 면에서는 여전히 강력한 선택지입니다. 특히 GPU가 아닌 CPU 코어만으로도 상당한 성능을 낼 수 있다는 점은 GGUF의 큰 강점이죠.

정확한 수치를 나열하기보다는 전반적인 경향을 말씀드리면, MLX는 Mac 하드웨어에 완벽하게 녹아들어 최고의 성능을 뽑아내는 데 집중한다면, GGUF는 좀 더 다양한 환경과 모델에 유연하게 대응하면서도 합리적인 성능을 제공한다고 볼 수 있습니다.

Mac에서 MLX와 GGUF 로컬 LLM의 초당 토큰 생성 성능을 비교하는 벤치마크 결과 차트

MLX와 GGUF 로컬 LLM 벤치마크 결과를 비교하는 차트입니다.

6. 13년차 서버실의 결론: 당신의 로컬 LLM, 어떤 길을 갈 것인가?

오늘 제가 공유해드린 경험이 여러분의 로컬 LLM 환경 구축에 도움이 되었으면 좋겠네요. 결국 정답은 없지만, 저는 이렇게 추천드리고 싶습니다.

  • MLX를 추천하는 경우:
    • 오직 Mac (특히 M 시리즈 칩셋)에서만 로컬 LLM을 사용할 계획이거나,
    • Mac 하드웨어의 최대 성능을 끌어내고 싶다면,
    • 최신 모델들을 빠르게 테스트하고 싶다면 MLX가 좋은 선택입니다.
  • GGUF를 추천하는 경우:
    • 다양한 로컬 LLM 모델을 폭넓게 사용하고 싶거나,
    • Mac 외에 Linux나 Windows 등 다른 OS에서도 로컬 LLM을 돌려야 한다면 (범용성),
    • 오래된 Mac이나 램 용량이 적은 Mac에서도 로컬 LLM을 돌려보고 싶다면 (양자화 이점), GGUF가 더 유연하고 합리적인 선택이 될 수 있습니다.

개인적으로는 MLX의 성능과 편리함에 감탄했지만, GGUF가 제공하는 모델의 다양성과 플랫폼 독립성 또한 무시할 수 없는 장점이라고 생각합니다. 저도 상황에 따라 두 가지 방식을 모두 활용하고 있어요. 😎

MLX와 GGUF 로컬 LLM의 주요 특징, 장점, 단점을 요약한 비교 인포그래픽

MLX와 GGUF의 주요 특징 및 장단점을 요약한 인포그래픽입니다.

7. 마치며

오늘 로컬 LLM 성능 최적화를 위한 MLX와 GGUF 벤치마크 삽질기를 공유해드렸는데, 어떠셨나요? 13년차 인프라 엔지니어의 경험이 여러분의 로컬 LLM 여정에 작은 등불이 되었기를 바랍니다. 로컬 LLM은 아직 발전 가능성이 무궁무진한 분야입니다. 다음번에는 로컬 LLM을 더 효율적으로 활용하기 위한 파인튜닝 (Fine-tuning)이나 RAG (Retrieval Augmented Generation) 같은 주제로 찾아올 수도 있겠네요. 궁금한 점이나 여러분의 경험이 있다면 댓글로 남겨주세요!