Các mô hình ngôn ngữ lớn chạy cục bộ ban đầu mang lại cảm giác cực kỳ nhanh. Bạn tải một mô hình 7B hoặc 13B tham số, gửi một câu lệnh ngắn, và các token sẽ chạy tràn màn hình với tốc độ rất dễ chịu. Sau đó, bạn dán một khối mã dài, hoặc lịch sử trò chuyện của bạn tăng lên qua hàng chục lượt hội thoại, và mô hình bắt đầu chạy chậm chạp. Sự sụt giảm này hiếm khi diễn ra từ từ. Nó giống như một vực thẳm. Một khoảnh khắc GPU đang tạo ra các token liên tục; khoảnh khắc tiếp theo, trình giám sát hệ thống của bạn cho thấy áp lực bộ nhớ đang tăng lên và quá trình tạo văn bản bắt đầu giật lag. Bạn không thể dự đoán chính xác khi nào điều này sẽ xảy ra bằng một công thức gọn gàng. Chỉ có phần cứng là người dẫn đường đáng tin cậy duy nhất của bạn.

Chi phí ẩn của Context

Mỗi token bạn tạo ra đều thêm trạng thái vào KV cache. Bộ nhớ đệm này lưu trữ các key và value được tính toán trong các giai đoạn prefill và generation, và nó nằm trong bộ nhớ cùng với trọng số mô hình (model weights), bộ đệm attention và các chi phí vận hành (runtime overhead). Trên một GPU tiêu dùng điển hình với 12 GB hoặc 16 GB VRAM, KV cache cuối cùng sẽ phải cạnh tranh không gian với mọi thứ khác. Khi bộ nhớ video chuyên dụng đầy, hệ điều hành không báo lỗi và dừng lại. Nó âm thầm đẩy phần dư thừa vào bộ nhớ dùng chung (shared memory), chuyển dữ liệu giữa GPU và RAM hệ thống thông qua bus PCIe. Bus đó rất nhanh để truyền file, nhưng lại cực kỳ chậm so với băng thông bộ nhớ bên trong một card đồ họa. Kết quả không phải là một sự sụt giảm hiệu suất nhỏ. Đó là một sự sụp đổ.

Ba dấu hiệu cho thấy "vực thẳm" đã đến

Hãy quan sát các trình giám sát phần cứng trong khi mô hình đang chạy. Bạn sẽ thấy ba dấu hiệu rõ ràng khi hiệu suất chạm ngưỡng sụt giảm.

  • Shared VRAM tăng lên. Đây là bộ nhớ mà trình điều khiển GPU đã đẩy ra khỏi VRAM chuyên dụng vào vùng quản lý bởi hệ điều hành máy chủ. Ngay khi chỉ số này tăng lên trên mức 0, bạn đã vượt qua giới hạn.
  • Sử dụng System RAM tăng vọt. Phần dư thừa phải nằm ở đâu đó, và điểm đến đó chính là bộ nhớ chính của bạn. Nếu mức sử dụng RAM tăng lên trong khi mô hình đang tạo token, dữ liệu đang được chuyển từ GPU sang RAM.
  • Tốc độ eval giảm một nửa hoặc nhiều hơn. Giảm 10% có thể là do hạ xung do nhiệt (thermal throttling) hoặc các tiến trình chạy ngầm. Giảm 50% hoặc tệ hơn, có nghĩa là nút thắt cổ chai đã chuyển từ tensor cores sang băng thông bộ nhớ và độ trễ PCIe. Khi bạn thấy tốc độ tạo văn bản giảm từ hai chữ số xuống còn một chữ số, bạn đã rơi xuống vực thẳm.

Tại sao các bài kiểm tra nhanh của bạn có thể đang lừa dối bạn

Một bài kiểm tra nhanh (smoke test) sẽ mang lại cho bạn sự tự tin giả tạo. Nếu bạn benchmark mô hình với một câu lệnh chỉ khoảng một trăm token, thấy thông lượng (throughput) ổn định và kết luận xong, thì bạn mới chỉ đo lường được "giai đoạn trăng mật". KV cache lúc đó gần như trống rỗng. Các lớp (layers) chưa bị áp lực bởi quá trình prefill dài. Dấu chân (footprint) thực sự của nó chỉ lộ diện sau khi mô hình đã xử lý một câu lệnh lớn và bộ nhớ đệm đã đầy đến kích thước hoạt động thực tế. Bạn phải kiểm tra với quá trình prefill sâu và các lượt chạy generation dài. Hãy để context thực sự tích lũy. Chỉ khi đó, áp lực bộ nhớ mới ổn định và cho bạn thấy giới hạn thực sự.

Tìm giới hạn của bạn với llama.cpp

Nếu bạn đang chạy các mô hình thông qua llama.cpp, bạn có thể đo lường giới hạn của mình bằng các phép tính đơn giản và một bài kiểm tra kiên trì.

1. Đo lường mức sử dụng bộ nhớ dùng chung (shared memory).
Ghi lại mức VRAM chuyên dụng cơ bản với một câu lệnh tối thiểu, sau đó chạy một tác vụ ngữ cảnh dài và ghi lại mức cao nhất (peak). Lấy mức cao nhất trừ đi mức cơ bản. Sự chênh lệch chính là lượng dữ liệu đã bị tràn từ GPU sang bộ nhớ dùng chung của hệ thống.

2. Tính toán độ chênh lệch RAM (RAM delta).
Thực hiện phép trừ tương tự cho RAM hệ thống. Lấy mức RAM cao nhất trong quá trình chạy dài trừ đi mức RAM cơ bản. Con số này cho bạn biết chính xác bao nhiêu dữ liệu đã được đẩy từ card đồ họa sang bộ nhớ chính. Nó định lượng sự rò rỉ qua bus.

3. Tính thời gian sụt giảm tốc độ eval.
So sánh tốc độ tokens-per-second cơ bản với tốc độ sau khi mô hình đã xử lý xong một tài liệu dài. Bạn có thể thấy một mô hình chạy mượt mà ở mức 17 tokens mỗi giây khi ngữ cảnh còn mới, sau đó chỉ còn 2 tokens mỗi giây khi bộ nhớ đệm đã phình to. Sự sụt giảm 15 token đó chính là dấu hiệu cảnh báo sớm của bạn.

Định vị điểm gãy

Để lập bản đồ đường cong một cách chính xác, đừng chỉ hài lòng với một điểm dữ liệu đơn lẻ. Hãy thực hiện ba lần thử nghiệm riêng biệt tại các mức 16.000 token, 32.000 token65.000 token. Hai điểm có thể gợi ý một đường thẳng, nhưng hai dấu chấm chỉ là một sự phỏng đoán. Điểm thứ ba sẽ chứng minh liệu bạn đang nhìn thấy nhiễu phép đo hay là một bức tường bộ nhớ (memory wall) thực sự. Hãy trừ kết quả giữa các lần chạy để tính toán xem mỗi một nghìn token bổ sung tiêu tốn thêm bao nhiêu bộ nhớ trên sự kết hợp cụ thể giữa mô hình, lớp lượng tử hóa (quantization layer) và GPU của bạn.

Khi đã có độ dốc đó, bạn có thể dự phóng về phía trước. Lấy chi phí trên mỗi token, nhân với độ dài ngữ cảnh mục tiêu, chia cho 1024 để chuyển đổi giữa các đơn vị, và cộng kết quả đó vào mức tải VRAM cơ bản của mô hình. Phương trình trông như sau:

Mức tải VRAM của mô hình + (số token × bộ nhớ mỗi token ÷ 1024) = Mức sử dụng VRAM lý thuyết

Sự dự phóng này không phải là lời tiên tri. Nó là một cột mốc được rút ra từ hành vi thực tế. Hãy sử dụng nó để ước tính giới hạn tối đa của bạn trước khi bạn bắt đầu triển khai thực tế (production run) hoàn chỉnh.

Tại sao các công thức trên giấy lại thất bại, và Lượng tử hóa có thể khắc phục điều gì

Các công thức trong sách giáo khoa thường bỏ qua thực tế phức tạp của việc suy luận cục bộ (local inference). Các kiến trúc khác nhau phân bổ các bộ đệm chú ý (attention buffers) khác nhau. Hệ điều hành của bạn dành riêng VRAM cho trình điều khiển hiển thị (display driver), bộ tổng hợp (compositor) và ngữ cảnh CUDA (CUDA context). Các phiên bản trình điều khiển làm thay đổi mức độ chiếm dụng bộ nhớ dùng chung (shared memory). Một phương trình lý thuyết không thể biết được thực tế có bao nhiêu VRAM còn trống trên máy của bạn vào lúc 2 giờ chiều khi trình duyệt đang mở đầy các tab. Bạn phải chạy mô hình trên phần cứng cụ thể của mình và theo dõi các chỉ số.

Lượng tử hóa mang lại sự giải tỏa một phần. Việc chuyển KV cache từ f16 sang q8_0 giúp giảm một nửa dung lượng bộ nhớ chiếm dụng trong khi vẫn giữ được độ chính xác đủ cao cho hầu hết các tác vụ thực tế. Sự thay đổi đó giúp bạn có thêm khoảng trống dự phòng (headroom), nhưng nó không mang lại sự miễn nhiễm. Bộ nhớ đệm vẫn tăng tuyến tính với mỗi token mà bạn đưa vào. Cuối cùng, ngay cả kích thước đã giảm cũng sẽ làm quá tải bộ nhớ chuyên dụng hiện có và việc tràn dữ liệu sang RAM hệ thống sẽ bắt đầu diễn ra. Áp lực chỉ dừng lại khi cửa sổ ngữ cảnh bị giới hạn hoặc dữ liệu ngừng chuyển động.

Bài học thực tế rút ra

Đừng tin vào các slide tiếp thị, số lượng tham số, hay những phép tính nhẩm sơ sài. Hãy tải mô hình lên. Mở trình giám sát hệ thống. Chạy một luồng 65.000 token, quan sát RAM tăng lên và đếm số token mỗi giây. Những con số xuất hiện trên màn hình cụ thể của bạn, trên GPU cụ thể của bạn, mới là những con số duy nhất quan trọng. Ngữ cảnh luôn là yếu tố quyết định. Việc của bạn là biết chính xác khi nào nó thắng thế trên máy của mình.