Tối ưu hóa hiệu suất serving mô hình LLM với vLLM và Ollama: So sánh và cấu hình
Trong bối cảnh bùng nổ của trí tuệ nhân tạo, việc triển khai các mô hình ngôn ngữ lớn (LLM) trên cơ sở hạ tầng sản xuất đòi hỏi sự cân bằng tinh tế giữa tốc độ suy luận, sử dụng bộ nhớ và khả năng mở rộng. Hai công cụ nổi bật nhất hiện nay trong lĩnh vực này là vLLM và Ollama. vLLM thường được biết đến như một giải pháp serving chuyên nghiệp dành cho môi trường cloud và doanh nghiệp, trong khi Ollama lại chiếm lĩnh thị trường về sự đơn giản và khả năng chạy mượt mà trên máy tính cá nhân. Bài viết này sẽ đi sâu phân tích cơ chế hoạt động của vLLM, cách cấu hình để đạt hiệu suất cao nhất, và so sánh trực tiếp với Ollama để giúp các kỹ sư phần mềm đưa ra quyết định kiến trúc phù hợp.
Cơ chế tối ưu bộ nhớ của vLLM với PagedAttention
vLLM không chỉ là một wrapper đơn thuần để chạy mô hình mà là một hệ thống serving được thiết kế từ gốc để giải quyết vấn đề tắc nghẽn bộ nhớ trong các LLM. Điểm cốt lõi làm nên sự khác biệt của vLLM chính là thuật toán PagedAttention. Trong các thư viện inference truyền thống, bộ nhớ KV cache (Key-Value Cache) thường được cấp phát một cách liên tục (contiguous memory) cho toàn bộ batch, dẫn đến tình trạng lãng phí lớn khi các token đầu vào có độ dài khác nhau hoặc khi một request kết thúc sớm hơn. PagedAttention mượn ý tưởng từ hệ điều hành để quản lý bộ nhớ, chia KV cache thành các page nhỏ và chỉ cấp phát page khi cần thiết.
Cách tiếp cận này giúp vLLM đạt được tỷ lệ sử dụng bộ nhớ GPU cao hơn đáng kể, cho phép xử lý đồng thời nhiều request (concurrency) với độ dài context lớn mà không gặp lỗi OOM (Out Of Memory). Để trải nghiệm sức mạnh này, bạn có thể khởi động server vLLM với mô hình Llama 3 8B qua lệnh sau:
vllm serve meta-llama/Llama-3-8b --tensor-parallel-size 1 --max-model-len 8192 --gpu-memory-utilization 0.95
Lệnh trên yêu cầu vLLM sử dụng 95% bộ nhớ GPU có sẵn, một tính năng rất quan trọng để tận dụng tối đa phần cứng. Tham số --max-model-len cho phép bạn giới hạn độ dài context để kiểm soát bộ nhớ, trong khi --tensor-parallel-size xác định số lượng GPU tham gia xử lý song song một mô hình duy nhất, giúp tăng tốc độ suy luận trên các cluster lớn.
Triển khai vLLM trong môi trường container hóa với Docker
Đối với các hệ thống sản xuất, việc đóng gói vLLM trong container là tiêu chuẩn vàng để đảm bảo tính nhất quán và khả năng di chuyển giữa các môi trường. Tuy nhiên, khi sử dụng Docker, bạn cần đặc biệt chú ý đến việc mapping thiết bị GPU và quyền truy cập vào thư mục chứa model weights. Nếu mô hình quá lớn và không nằm trong cache local của Docker, quá trình pull image sẽ rất lâu và tốn bandwidth. Một cấu hình Docker Compose hiệu quả cho vLLM sẽ yêu cầu mount thư mục dữ liệu và chỉ định runtime là NVIDIA.
Hãy xem xét kịch bản bạn muốn chạy vLLM trên một máy chủ có nhiều GPU. Bạn cần đảm bảo Docker có thể thấy được các GPU này. Sau khi có sẵn image vllm/vllm-openai, bạn có thể chạy container với các tham số cụ thể để tối ưu hóa hiệu năng mạng và bộ nhớ. Dưới đây là lệnh chạy container mẫu:
docker run --gpus all --shm-size 1g -p 8000:8000 -v ~/models:/models vllm/vllm-openai:latest --model /models/Llama-3-8B-Instruct --host 0.0.0.0
Tham số --shm-size 1g là cực kỳ quan trọng trong môi trường container vì PyTorch thường sử dụng shared memory để giao tiếp giữa các tiến trình trong trường hợp tensor parallel. Nếu bỏ qua tham số này, hệ thống có thể báo lỗi hoặc chạy với hiệu suất rất thấp. Việc mount thư mục ~/models vào container giúp bạn tránh phải pull lại model nặng nề mỗi khi khởi động lại container, đồng thời cho phép dễ dàng cập nhật model weights mà không cần rebuild image.
So sánh vLLM và Ollama: Khi nào nên dùng cái nào?
Mặc dù vLLM mạnh mẽ trong môi trường server, nhưng Ollama lại có một vị thế không thể thay thế trong các kịch bản phát triển, test địa phương và deploy trên các máy khách có tài nguyên hạn chế. Ollama đóng gói cả model weights và engine inference vào một tiến trình đơn giản, loại bỏ hoàn toàn các dependency phức tạp của Python hay CUDA. Ollama sử dụng backend llama.cpp đã được tối ưu hóa cho CPU và GPU, hỗ trợ cả phần cứng Mac (Metal) lẫn Linux/Windows. Ngược lại, vLLM đòi hỏi kiến thức sâu về hệ thống, GPU NVIDIA và Python, nhưng đổi lại là throughput (số lượng token/giây) cao hơn nhiều lần trong môi trường multi-user.
Nếu bạn cần xây dựng một API chatbot cho một ứng dụng web với hàng ngàn người dùng cùng lúc, vLLM là lựa chọn bắt buộc nhờ khả năng quản lý queue request và load balancing hiệu quả. Tuy nhiên, nếu bạn là một developer muốn chạy thử nghiệm các prompt khác nhau, fine-tune model nhỏ, hoặc deploy cho một nhóm 5-10 người trên một máy Mac mini hoặc laptop, Ollama sẽ tiết kiệm thời gian setup đáng kể. Lệnh để chạy Ollama rất trực quan:
ollama serve
Sau khi dịch vụ Ollama đang chạy, bạn có thể tải và chạy mô hình bằng lệnh đơn giản sau:
ollama run llama3:8b
Điều thú vị là vLLM và Ollama không phải là hai công cụ đối đầu mà có thể bổ sung cho nhau trong một pipeline phát triển. Bạn có thể dùng Ollama để phát triển và test logic prompt nhanh chóng, sau đó chuyển đổi mô hình đó sang vLLM khi đưa lên môi trường production để đảm bảo hiệu suất. Một số mô hình trong Ollama thậm chí hỗ trợ chạy backend vLLM nếu được cấu hình đúng, mở ra khả năng kết hợp linh hoạt.
Hướng dẫn cấu hình vLLM để tăng Throughput với Quantization
Để tối ưu hóa hiệu suất vLLM trên các GPU có bộ nhớ thấp (ví dụ: 12GB VRAM trên card RTX 3060), kỹ thuật lượng tử hóa (quantization) là chìa khóa. vLLM hỗ trợ sẵn các định dạng lượng tử hóa như AWQ, GPTQ, và FP8. Sử dụng mô hình 4-bit hoặc 8-bit giúp giảm đáng kể lượng bộ nhớ cần thiết, cho phép bạn chạy các mô hình lớn hơn hoặc tăng kích thước batch lên nhiều lần, từ đó tăng tổng throughput của hệ thống. Tuy nhiên, cần lưu ý rằng việc lượng tử hóa có thể gây ra suy giảm nhẹ về độ chính xác của mô hình, do đó cần kiểm tra kỹ (benchmark) trước khi triển khai chính thức.
Khi bạn muốn chạy một mô hình đã được lượng tử hóa ở định dạng AWQ bằng vLLM, bạn cần chỉ định định dạng đó trong tham số model hoặc đảm bảo file config của model đã chứa thông tin này. Dưới đây là ví dụ về việc chạy mô hình Qwen-7B-AWQ với vLLM, cho phép chạy mượt mà trên GPU 12GB:
vllm serve Qwen/Qwen-7B-Chat-AWQ --tensor-parallel-size 1 --max-num-seqs 64 --max-num-batched-tokens 2048
Tham số --max-num-seqs 64 cho phép vLLM duy trì 64 request đồng thời, và --max-num-batched-tokens 2048 giới hạn tổng số token trong mỗi batch để tránh quá tải GPU. Việc tinh chỉnh các con số này dựa trên tài nguyên thực tế của máy chủ là kỹ năng quan trọng của một Sysadmin. Nếu bạn muốn tích hợp vLLM vào ứng dụng Python của mình, bạn có thể sử dụng thư viện openai tương thích để gọi API:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")
response = client.chat.completions.create(model="qwen", messages=[{"role": "user", "content": "Xin chào"}])
print(response.choices[0].message.content)
Tổng kết lại, việc lựa chọn giữa vLLM và Ollama phụ thuộc hoàn toàn vào quy mô và mục tiêu dự án. vLLM mang lại hiệu suất cực đại cho các hệ thống production yêu cầu cao về concurrency và tốc độ, trong khi Ollama là giải pháp tối ưu cho sự đơn giản và khả năng tiếp cận. Hiểu rõ cơ chế hoạt động và biết cách cấu hình các tham số kỹ thuật như PagedAttention, quantization hay tensor parallelism sẽ giúp bạn khai thác tối đa sức mạnh của LLM trong mọi kịch bản.