OpenArchLabOpen Architecture — From Models to Systems

Lab tổng hợp · 13 khối

Lab tổng hợp Inference Engineering · theo dõi một yêu cầu từ cổng tiếp nhận đến lúc dừng bằng NumPy

Chạy bằng NumPy một đường ống phục vụ LLM thu nhỏ để theo dõi bằng số thật một yêu cầu từ cổng tiếp nhận, lập lịch, prefill, KV cache, decode, lấy mẫu đến các số đo TTFT, TPOT và throughput.

LESSON ID inference-end-to-end-lab GROUP Bài Lab tổng hợp
Mục lục bài học

1Định nghĩa

Bài Lab tổng hợp này cho bạn chạy thử, bằng NumPy ngay trong trình duyệt, toàn bộ đường đi của một yêu cầu trong hệ thống phục vụ mô hình ngôn ngữ lớn (LLM, large language model): yêu cầu chạm cổng tiếp nhận, được cắt thành token (mảnh văn bản nhỏ mà mô hình xử lý), được xếp lịch, rồi được tính để sinh từng token trả lời cho đến khi dừng. Mỗi khối trong lab mang đúng tên một khối trên sơ đồ kiến trúc ở Tab 01, nên bạn nhìn thấy dữ liệu đi qua từng khối thay vì chỉ đọc mô tả rời rạc.

Cơ chế chạy như sau: năm yêu cầu A đến E đi vào một engine mô phỏng; bộ lập lịch theo vòng lặp cho yêu cầu vào chạy khi có chỗ trống, prefill xử lý cả prompt một lần và ghi key, value của từng token vào KV cache phân trang, rồi ở mỗi bước decode nhận một token mới và lấy mẫu token kế tiếp. Thời gian (TTFT là thời gian tới token đầu tiên, TPOT là thời gian trung bình giữa hai token liền sau, throughput là số token sinh ra mỗi giây) được đọc trên một đồng hồ mô phỏng theo công thức giả định, còn token, byte, số block và sai số là kết quả tính thật. Vì vậy lab kiểm chứng tính đúng của cơ chế, không đo tốc độ của GPU (chip tính toán chạy mô hình) thật.

Ví dụ đời thường: Lab giống một quầy gọi món giờ cao điểm. Nhân viên ở cửa nhận hoặc từ chối khách khi quầy đã đầy (cổng), ghi món thành mã số (tokenizer), bếp trưởng quyết định món nào lên bếp ở mỗi lượt (bộ lập lịch), đầu bếp sơ chế cả đơn rồi nấu từng món một (prefill rồi decode), kệ giữ phần nguyên liệu đã sơ chế để món sau khỏi sơ chế lại (KV cache) và bảng điện tử ghi thời gian chờ của từng bàn (telemetry). Phép so sánh dừng lại ở chỗ thời gian trong lab là con số giả định, không phải thời gian của một nhà bếp thật.

2Luồng

Sơ đồ luồng (13 bước)
  1. 1 · Gateway & admission

    Nhận A, B, E, C và từ chối D bằng mã 429 vì hệ thống đã có 3 yêu cầu, bằng giới hạn 2 chạy cộng 1 chờ.

  2. 2 · Input preparation

    BPE 10 phép gộp, từ vựng 22 token; C thành 9 token ID và E bị chặn mã 400 vì 8 cộng 30 vượt 24.

  3. 3 · Iteration scheduler

    12 bước; A chạy một mình lúc 0.50 ms, prompt của C chia thành hai đoạn 5 và 4 và C chờ chỗ chạy 14.55 ms mô phỏng.

  4. 4 · Prefill

    9 token của C ghi K và V có shape 2, 9, 4; hàng cuối có 22 logits và token đầu xuất hiện lúc 23.00 ms, TTFT 22.00 ms mô phỏng.

  5. 5 · Transformer × N layers

    Trọng số attention của token cuối có 9 phần tử và tổng bằng 1; logits là 22 điểm số khác; kết quả qua cache khớp tính lại trong 1e-9.

  6. 6 · KV state manager

    Cao điểm 7 trên 12 block, A giữ 0, 1, 5 và B giữ 2, 3, 4, 6; C dùng lại block 3, 4, 6 của B; cuối còn đủ 12 block trống.

  7. 7 · Decode

    C có 1 token từ prefill và 3 bước decode; dùng cache chỉ tính 41 vị trí, tính lại cả lịch sử cần 197 vị trí.

  8. 8 · Logits → sampling → stop check

    Softmax với temperature 0.8 cho d 0.260 và eos 0.152; lọc top-k và top-p giữ 5 token, rút ra o; C dừng vì eos sau 4 token.

  9. 9 · KV / weight precision

    8 ma trận trọng số xuống INT8: 3264 byte còn 1192 byte, sai số khôi phục 0.0056 trong nửa scale 0.0061.

  10. 10 · HBM memory budget

    128 byte KV mỗi token trong lab; cấu hình mẫu 32 lớp, 4096 token, 8 KV head, head 128, FP16 cần 0.50 GiB mỗi chuỗi.

  11. 11 · Telemetry & capacity feedback

    TTFT của C = 0.65 chuẩn bị + 14.55 chờ chỗ chạy + 6.80 prefill = 22.00 ms; thành phần lớn nhất là chờ chỗ chạy.

  12. 12 · Benchmark & SLO methodology

    Token giống hệt giữa ba chính sách lập lịch; TTFT trung bình và khoảng đứng lớn nhất (ms mô phỏng): không chia prompt 10.00 và 6.00, chia đoạn 12.67 và 3.60, static 12.48 và 1.20.

  13. 13 · Quality gates & rollout

    Bản INT8 khớp 40 trên 40 quyết định top-1 của bản gốc; quyết định sát nhất chênh 0.0304 ở bản gốc và 0.0326 ở bản INT8.

Mã Mermaid
flowchart TD
  step1["1 · Gateway #amp; admission"]
  step2["2 · Input preparation"]
  step3["3 · Iteration scheduler"]
  step4["4 · Prefill"]
  step5["5 · Transformer × N layers"]
  step6["6 · KV state manager"]
  step7["7 · Decode"]
  step8["8 · Logits → sampling → stop check"]
  step9["9 · KV / weight precision"]
  step10["10 · HBM memory budget"]
  step11["11 · Telemetry #amp; capacity feedback"]
  step12["12 · Benchmark #amp; SLO methodology"]
  step13["13 · Quality gates #amp; rollout"]
  step1 --> step2
  step2 --> step3
  step3 --> step4
  step4 --> step5
  step5 --> step6
  step6 --> step7
  step7 --> step8
  step8 --> step9
  step9 --> step10
  step10 --> step11
  step11 --> step12
  step12 --> step13

Cách đọc: một yêu cầu đi từ khối 1 đến khối 8 theo đúng thứ tự thời gian của nó (cổng, chuẩn bị đầu vào, xếp lịch, prefill, tính một lớp, ghi cache, decode, lấy mẫu và dừng). Các khối 9 đến 13 là khối đi kèm: nén trọng số, ngân sách bộ nhớ, số đo, so sánh chính sách và cổng chất lượng; chúng dùng lại dữ liệu của engine chứ không phải bước tiếp theo của một yêu cầu. Mọi số lấy nguyên văn từ inference-end-to-end.py (seed 7), trừ số ms là kết quả trên đồng hồ mô phỏng.

3Giải pháp

Một yêu cầu gửi tới LLM không đi thẳng vào mô hình. Văn bản người dùng gửi (gọi là prompt) phải qua cổng tiếp nhận, được đổi thành token (mỗi token có một số thứ tự gọi là token ID), được xếp lịch cùng các yêu cầu khác rồi mới được tính. Mỗi khối có thể làm yêu cầu chậm đi hoặc bị từ chối, nên đọc từng bài riêng rất khó thấy chúng nối nhau ra sao. Lab gom 13 khối vào một đường ống để bạn theo dõi một yêu cầu cụ thể, tên là C, và thấy vì sao nó mất 22.00 ms mô phỏng mới nhận được token đầu tiên.

Đầu vào là một trace (danh sách các yêu cầu cùng thời điểm đến) gồm năm yêu cầu, đến vào những thời điểm khác nhau trên đồng hồ mô phỏng: A (xin chao model, được sinh tối đa 10 token, mỗi bước chọn token có điểm cao nhất, gọi là greedy), B (model hoc token, chọn token ngẫu nhiên có kiểm soát), E (xin chao ban nhưng xin sinh tới 30 token), C (ban hoc model, yêu cầu được theo dõi) và D (xin chao token). Engine có 2 chỗ chạy cùng lúc và 1 chỗ chờ; mỗi bước xử lý tối đa 6 token; KV cache gồm 12 block, mỗi block chứa 4 token; một chuỗi dài tối đa 24 token. Mô hình là một lớp decoder với 2 head attention (mỗi head là một nhóm tính attention độc lập, có K và V riêng), kích thước ẩn 8 và bảng từ vựng 22 token. Trọng số ngẫu nhiên với seed 7 nên chạy lại ra cùng số, nhưng chưa huấn luyện nên văn bản sinh ra không có nghĩa.

Đi theo từng khối. Ở khối 1, cổng chỉ cho tối đa 3 yêu cầu có mặt trong hệ thống; D đến khi đã có 3 yêu cầu nên nhận mã từ chối 429. Ở khối 2, văn bản đổi thành token: E qua được cổng nhưng bị chặn mã 400 vì prompt 8 token cộng 30 token xin sinh là 38, vượt giới hạn 24. Ở khối 3, bộ lập lịch quyết định mỗi bước chạy việc gì: A chạy một mình lúc 0.50 ms vì B còn đang chuẩn bị đầu vào, và prompt 9 token của C được chia thành hai đoạn 5 và 4 để không chiếm trọn một bước. Khối 4 là prefill, tức xử lý cả prompt một lần và ghi key (K) cùng value (V) của từng token vào cache; hàng cuối của kết quả là 22 điểm số (logits), mỗi điểm cho một token ứng viên.

Khối 5 nhắc một điều rất dễ nhầm: trọng số attention và xác suất từ vựng là hai phân phối khác nhau. Với token cuối của prompt C, mỗi head chia trọng số cho 9 vị trí đã có, tổng bằng 1, để cho biết lấy thông tin từ đâu. Chỉ sau khi qua phần còn lại của lớp và lớp LM head mới có 22 logits trên token ứng viên, và phải đến khối 8 logits mới được đổi thành xác suất. Khối 6 cho thấy KV cache không nằm trong một mảng liền: mỗi chuỗi có một bảng block nối block logic với block vật lý, ví dụ lúc cao điểm A giữ các block 0, 1, 5 còn B giữ 2, 3, 4, 6; khi B xong, block 3, 4 và 6 được C dùng lại. Khối 7 là vòng decode: mỗi bước chỉ đưa vào một token mới và đọc lại K, V cũ từ cache, nên cả lab chỉ phải tính 41 vị trí thay vì 197 vị trí nếu mỗi bước tính lại toàn bộ lịch sử. Đây là số vị trí phải tính, chưa phải thời gian tiết kiệm được.

Khối 8 biến logits của C thành xác suất bằng softmax (phép đổi một dãy điểm số thành dãy không âm có tổng bằng 1) sau khi chia cho temperature 0.8 (temperature nhỏ hơn 1 làm phân phối nhọn hơn): token d có xác suất cao nhất, 0.260, còn eos có 0.152. Bộ lọc top-k = 5 (chỉ giữ 5 token điểm cao nhất) và top-p = 0.9 (giữ nhóm nhỏ nhất có tổng xác suất đủ 0.9) còn lại 5 token, được chuẩn hóa lại để tổng bằng 1, rồi rút ra token o. Sau mỗi token, engine kiểm tra điều kiện dừng: C dừng vì gặp eos (token báo kết thúc) sau 4 token. Khối 9 nén 8 ma trận trọng số xuống số nguyên 8 bit (INT8) với một hệ số tỉ lệ (scale) cho từng cột; dung lượng tính trên định dạng giảm từ 3264 byte (nếu lưu bằng số thực 32 bit, FP32) còn 1192 byte, sai số khôi phục trọng số lớn nhất 0.0056, nằm trong nửa scale là 0.0061. Khối 10 lập ngân sách cho HBM (bộ nhớ băng thông cao gắn trên GPU): mỗi token cần 128 byte K và V trong lab; với cấu hình mẫu 32 lớp, 4096 token, 8 KV head, mỗi head 128 chiều, số thực 16 bit (FP16), một chuỗi cần 0.50 GiB.

Khối 11 đến 13 đọc kết quả. TTFT (time to first token, thời gian từ lúc yêu cầu đến tới token đầu tiên) của C là 22.00 ms mô phỏng, gồm 0.65 ms chuẩn bị đầu vào, 14.55 ms chờ chỗ chạy và 6.80 ms prefill. Thành phần lớn nhất là chờ chỗ chạy, nên muốn giảm TTFT của C thì phải nhìn vào hàng đợi chứ không phải vào prefill. TPOT (time per output token) là khoảng cách trung bình giữa các token sau token đầu; throughput là số token sinh ra trên mỗi giây. Khối 12 chạy cùng một trace với ba chính sách lập lịch và thấy token sinh ra giống hệt nhau, chỉ thời gian khác: không chia prompt cho TTFT trung bình 10.00 ms nhưng có lúc một yêu cầu đang decode phải đứng 6.00 ms, còn chia đoạn cho 12.67 ms và khoảng đứng chỉ 3.60 ms. Khối 13 so token được chọn của bản gốc với bản INT8 trên cùng ngữ cảnh và thấy khớp cả 40 quyết định.

Giới hạn của kết luận: mọi số ms đến từ đồng hồ mô phỏng với chi phí giả định, nên chúng không cho biết GPU thật nhanh bao nhiêu; chỉ số token, byte, block và sai số là kết quả tính thật. Ba yêu cầu được phục vụ chưa đủ để nói về p95 hay p99 (giá trị mà 95% hoặc 99% yêu cầu không vượt quá). Con số 40 trên 40 của cổng chất lượng chỉ cho thấy bản INT8 chọn cùng token trong 40 trường hợp của một mô hình ngẫu nhiên, không nói gì về chất lượng ngôn ngữ. Router, xác thực, hủy yêu cầu, prefix cache, preemption, mô hình nhiều lớp và nhiều GPU không được mô phỏng.

4Profiling

  • Kiểm tra tính đúng trước khi nói tới tốc độ

    Trước khi đọc bất kỳ con số thời gian nào, hãy xem hai phép kiểm tra số học. Một: logits tính qua KV cache khớp logits tính lại cả chuỗi từ đầu ở mọi bước prefill và decode của cả ba yêu cầu, với sai lệch lớn nhất không vượt 1e-9 (phần dư chỉ là sai số làm tròn). Hai: K và V gom từ các block rời rạc khớp K và V tính lại từ đầu, cũng trong ngưỡng 1e-9. Ngoài ra, ba chính sách lập lịch phải cho cùng token cho mọi yêu cầu chạy ở cả ba chính sách. Ba điều này chứng minh cache, block và lập lịch không làm đổi nội dung; chúng không chứng minh lab chạy nhanh hơn.

  • Đọc TTFT, TPOT và throughput trên đồng hồ mô phỏng

    Workload là cùng một trace năm yêu cầu, ba yêu cầu được phục vụ, cùng seed và cùng công thức chi phí; baseline là chính sách static (đợi đủ nhóm rồi chạy hết nhóm). Với mỗi yêu cầu, hãy đọc TTFT = thời điểm token đầu trừ thời điểm đến, TPOT = (thời điểm token cuối trừ thời điểm token đầu) chia số khoảng cách, và phân rã TTFT thành chuẩn bị đầu vào, chờ chỗ chạy, prefill để biết nút thắt. Khi so ba chính sách, đọc cả cột khoảng đứng lớn nhất giữa hai token liền kề, vì TPOT trung bình có thể che một lần đứng lâu. Sai khác giữa các chính sách là hệ quả của công thức chi phí giả định trên 3 yêu cầu; đổi trace hoặc hằng số COST có thể đổi thứ hạng, nên đừng viết chúng thành tốc độ của hệ thống thật.

  • Đo áp lực bộ nhớ KV

    Đọc cột KV theo từng bước ở khối 11 và dòng cao điểm ở khối 6: lúc dùng nhiều nhất là 7 trên 12 block, tức 28 chỗ token để lưu 24 token, còn cấp một mảng liền 24 token cho mỗi chỗ chạy sẽ cần 48 chỗ. Phần thừa 4 chỗ là phần cuối của block chưa đầy. Trên GPU thật, hãy đo bộ nhớ đỉnh với đúng workload; công thức ở khối 10 chỉ cho phần KV, chưa gồm trọng số và vùng làm việc.

  • Phân biệt sai số số học với thay đổi quyết định

    Sai số logits giữa bản gốc và bản INT8 lớn nhất ở C là 0.0385, nhưng điều ảnh hưởng đến đầu ra là liệu hai token đứng đầu có đảo thứ tự không. Quyết định sát nhất của lab (prompt B, bước 6) có chênh lệch 0.0304 ở bản gốc và 0.0326 ở bản INT8, nên token được chọn giữ nguyên dù sai số logits lớn nhất của B là 0.1317. Hãy luôn báo cả sai số lẫn tỉ lệ quyết định đổi, và nhớ rằng 40 quyết định của mô hình ngẫu nhiên không thay cho bộ đánh giá nhiệm vụ thật.

Mẹo đo lường: ghim số liệu trên workload thật, ghi cả p50/p95/p99 thay vì chỉ trung bình, và luôn ghi lại điều kiện chạy (batch, cache, đồng thời, phần cứng) trước và sau khi tối ưu. Số đo thiếu điều kiện chạy thì không so sánh được.

5Ví dụ

Vì sao C chờ tới 14.55 ms mà không vào chạy sớm hơn?

Hãy dự đoán trước: C đến lúc 1.00 ms và chuẩn bị xong lúc 1.65 ms, vậy điều gì giữ nó lại? Mở khối 3 và đọc từng bước. Hai chỗ chạy đang do A và B giữ. B kết thúc ở bước 7 (lúc 16.20 ms) vì gặp eos sau 4 token, nên C mới có chỗ ở bước 8. Quan sát này suy ra từ số liệu: 16.20 ms trừ 1.65 ms bằng 14.55 ms. Tự kiểm tra: nếu một trong hai yêu cầu đang chạy xong sớm hơn thì C chờ ít hơn hay nhiều hơn? Đối chiếu: ít hơn hoặc bằng, vì C lấy chỗ trống đầu tiên; phần chờ chỗ chạy trong TTFT phụ thuộc vào lúc có chỗ trống đầu tiên, tức vào độ dài các yêu cầu đứng trước, chứ không phụ thuộc vào độ dài prompt của C.

Một token, hai phân phối khác nhau

Mở khối 5 và khối 8 cho cùng yêu cầu C. Ở khối 5, token cuối của prompt (vị trí 8) nhìn lại 9 vị trí và head 0 chia trọng số [0.109, 0.141, 0.080, 0.146, 0.128, 0.100, 0.072, 0.102, 0.121], tổng bằng 1: nó nhìn nhiều nhất vị trí 3. Ở khối 8, cùng token đó cho ra 22 logits; softmax với temperature 0.8 đổi chúng thành xác suất trên 22 token, trong đó d có 0.260 và eos có 0.152. Hai dãy có cùng tính chất tổng bằng 1 nhưng trả lời hai câu hỏi khác nhau: dãy đầu là lấy thông tin từ vị trí nào, dãy sau là chọn token nào. Tự kiểm tra: dãy nào có 9 phần tử và dãy nào có 22? Đối chiếu: attention có 9 phần tử vì prompt có 9 vị trí; xác suất có 22 phần tử vì từ vựng có 22 token.

Đổi QUANT_BITS từ 8 thành 4 và đọc lại cổng chất lượng

Dự đoán: nén xuống 4 bit làm sai số lớn hơn nhiều; liệu token được chọn có bắt đầu đổi không? Sửa dòng QUANT_BITS = 4 ở đầu file rồi bấm Chạy. Kết quả đã chạy với cấu hình này: sai số khôi phục trọng số lớn nhất 0.1000 (so với 0.0056 ở 8 bit), sai số logits của C là 1.2911, và cổng chất lượng khớp 33 trên 40 quyết định, tức 82.5%, thấp hơn ngưỡng giả định 95% nên báo KHÔNG ĐẠT. Dung lượng định dạng chỉ giảm thêm từ 1192 xuống 784 byte. Kết luận: giảm bit đổi bộ nhớ lấy sai số, và cổng chất lượng là chỗ phát hiện cái giá đó; ngưỡng 95% và 40 quyết định chỉ là giả định để minh họa.

6Thuật ngữ

TTFT (time to first token)
Thời gian từ lúc yêu cầu đến tới lúc token đầu tiên được sinh. Trong lab, TTFT của C = chuẩn bị đầu vào + chờ chỗ chạy + prefill, tính trên đồng hồ mô phỏng.
TPOT (time per output token)
Khoảng cách trung bình giữa hai token liền nhau sau token đầu tiên, bằng (thời điểm token cuối trừ thời điểm token đầu) chia số khoảng cách.
Đồng hồ mô phỏng
Bộ đếm thời gian do công thức chi phí giả định (COST) tạo ra thay cho GPU thật. Các số ms của lab chỉ có ý nghĩa so sánh trong chính lab, không phải tốc độ phần cứng.
Bảng block (block table)
Bảng tra của một chuỗi, nối block logic 0, 1, 2... với block vật lý trong bể KV; hai chuỗi có thể xen kẽ block vật lý và block được trả sẽ cấp lại cho chuỗi khác.
Cổng chất lượng (quality gate)
Phép kiểm tra tự động quyết định có chấp nhận một thay đổi hay không; trong lab là tỉ lệ quyết định top-1 phải giữ nguyên giữa bản gốc và bản INT8 so với ngưỡng giả định 95%.
Glossary (6)
Token ID
Số thứ tự của một token trong bảng từ vựng; mô hình dùng số này để tra vector nhúng, bản thân số chưa mang ý nghĩa.
Trọng số attention
Phân phối softmax trên các vị trí đã thấy, cho biết một token lấy thông tin từ vị trí nào; khác với xác suất chọn token kế tiếp.
Logits
Điểm số chưa chuẩn hóa của mọi token trong từ vựng ở một vị trí; softmax đổi chúng thành xác suất.
Continuous batching
Cách lập lịch nhận yêu cầu mới vào ở ranh giới từng bước ngay khi có chỗ trống, thay vì đợi cả nhóm cũ xong.
Chunked prefill
Chia một prompt dài thành các đoạn và xử lý dần qua nhiều bước để các yêu cầu đang decode không phải đứng chờ quá lâu.
Lượng tử hóa đối xứng theo kênh
Lưu mỗi cột trọng số bằng số nguyên cùng một hệ số tỉ lệ riêng của cột đó, scale = max|w| chia cho mức lớn nhất của số nguyên.

7Bài liên quan

8Nguồn

9Bài lab

Theo dõi một yêu cầu qua 13 khối của đường ống phục vụ LLM bằng NumPy

Thực hành NumPy · đường ống phục vụ LLM thu nhỏ (13 khối)

Đọc phần hướng dẫn rồi bấm Chạy. Mỗi khối [n · Tên] khớp một khối trên sơ đồ kiến trúc ở Tab 01; hãy đọc dòng Quan sát, đối chiếu các con số, rồi mới đọc Kết luận.

Bạn sẽ học được gì?

Sau bài lab, bạn giải thích được bằng số cụ thể chín điều: (1) vì sao cổng từ chối một số yêu cầu ngay từ đầu; (2) văn bản trở thành token ID như thế nào và vì sao có yêu cầu bị chặn vì quá ngân sách; (3) bộ lập lịch chọn việc cho mỗi bước ra sao; (4) prefill khác decode ở đâu; (5) trọng số attention khác xác suất từ vựng thế nào; (6) KV cache phân trang dùng bảng block ra sao; (7) logits trở thành xác suất, rồi token, rồi quyết định dừng như thế nào; (8) TTFT, TPOT và throughput gồm những phần nào; (9) nén trọng số xuống INT8 đổi bộ nhớ lấy sai số ra sao và cách kiểm tra.

Bạn không cần biết trước về LLM. Mỗi thuật ngữ được giải thích ở chỗ nó xuất hiện đầu tiên; bạn chỉ cần đọc được Python và biết một mảng NumPy có shape, ví dụ (2, 9, 4) là 2 nhóm, mỗi nhóm 9 hàng, mỗi hàng 4 số.

Đầu vào và cấu hình thu nhỏ của bài lab

Đầu vào là năm yêu cầu (request: một lời gọi gửi văn bản tới mô hình để nhận trả lời). A "xin chao model" đến 0.00 ms, tối đa 10 token sinh ra, greedy. B "model hoc token" đến 0.00 ms, tối đa 7 token, lấy mẫu T = 0.8, top-k = 4, top-p = 0.9. E "xin chao ban" đến 0.50 ms nhưng xin tới 30 token. C "ban hoc model" đến 1.00 ms, tối đa 8 token, lấy mẫu T = 0.8, top-k = 5, top-p = 0.9; đây là yêu cầu được theo dõi qua mọi khối. D "xin chao token" đến 1.50 ms, greedy. Thời điểm đến tính trên đồng hồ mô phỏng; mỗi yêu cầu có seed riêng nên lấy mẫu lặp lại được.

Cấu hình thu nhỏ: 2 chỗ chạy cùng lúc và 1 chỗ chờ (SLOTS = 2, QUEUE_LIMIT = 1); mỗi bước tối đa 6 token (TOKEN_BUDGET = 6); KV cache có 12 block, mỗi block 4 token; một chuỗi tối đa 24 token. Mô hình có một lớp, 2 head attention (MHA, multi-head attention: mỗi head có K và V riêng) với mỗi head 4 chiều, d_model = 8 (chiều của vector ẩn), RoPE (mã hóa vị trí bằng phép xoay) và SwiGLU (mạng feed-forward có cổng) là hai chi tiết của lớp mà bạn chỉ cần biết là có mặt, bảng từ vựng 22 token (gồm token bắt đầu bos và token kết thúc eos), trọng số ngẫu nhiên seed 7. Token của lab do một bộ BPE (byte pair encoding: gộp dần cặp ký tự hay gặp) 10 phép gộp học từ 5 câu mẫu. Đồng hồ mô phỏng dùng các hằng số COST giả định (đơn vị micro-giây) ở đầu file.

Bảng ánh xạ giữa sơ đồ Tab 01 và 13 khối của lab

Khối 1 Gateway & admission = hàm admit; khối 2 Input preparation = train_bpe và encode; khối 3 Iteration scheduler = vòng lặp chính của run_engine (nhận yêu cầu, chọn việc mỗi bước, chia prompt theo TOKEN_BUDGET); khối 4 Prefill và khối 7 Decode = hai nhánh task của run_engine; khối 5 Transformer × N layers = hàm forward (attention, RoPE, SwiGLU, LM head); khối 6 KV state manager = lớp KVPool.

Khối 8 Logits → sampling → stop check = hàm sample và điều kiện dừng trong emit; khối 9 KV / weight precision = quantize_weights; khối 10 HBM memory budget = phép tính byte trên KVPool và công thức mẫu; khối 11 Telemetry & capacity feedback, khối 12 Benchmark & SLO methodology = metrics và summary; khối 13 Quality gates & rollout = greedy_teacher_forced. Tên khối trong output khớp tên khối hoặc năng lực trên sơ đồ.

Cách thực hiện và đọc từng khối output

1. Trước khi bấm Chạy, hãy dự đoán: trong năm yêu cầu, yêu cầu nào bị từ chối và vì sao? C sẽ phải chờ lâu hay không? Viết dự đoán ra giấy.

2. Bấm Chạy. Ở khối 1 và 2, đối chiếu dự đoán: D nhận 429 vì lúc xét đã có 3 yêu cầu trong hệ thống; E qua cổng nhưng bị chặn 400 vì 8 + 30 = 38 lớn hơn 24. Ở khối 3, đọc từng dòng bước: p là prefill một đoạn prompt, d là decode một token; tìm bước mà một yêu cầu đang decode dùng chung bước với prefill của yêu cầu khác.

3. Ở khối 4 đến 7, theo dõi C: shape (2, 9, 4) của K nghĩa là 2 head, 9 token, 4 số mỗi vị trí; so hai dãy số của khối 5 và khối 4 (trọng số attention có 9 phần tử và tổng bằng 1; logits có 22 phần tử và tổng không bằng 1); đọc bảng block của khối 6 và tìm block được dùng lại; so 41 với 197 ở khối 7 nhưng nhớ rằng đó là số vị trí, chưa phải thời gian.

4. Ở khối 8 đến 10, đọc xác suất trước và sau khi lọc của C, thấy token được rút và lý do dừng của từng yêu cầu; sau đó đọc dung lượng trước và sau INT8 và so sai số khôi phục với nửa scale.

5. Ở khối 11 đến 13, cộng ba thành phần của TTFT để kiểm tra đúng bằng tổng, tìm thành phần lớn nhất, rồi đọc bảng ba chính sách lập lịch và dòng quyết định sát nhất của cổng chất lượng. Cuối cùng, đối chiếu dự đoán ban đầu với kết quả và sửa các hằng số ở đầu file (xem bài tập) để thấy output thay đổi.

Kết quả này chứng tỏ điều gì và giới hạn ở đâu?

Các phép kiểm tra trong lab xác nhận, cho trace năm yêu cầu này: cache và bảng block cho cùng logits với tính lại toàn bộ (sai lệch không vượt 1e-9); số yêu cầu offered bằng served cộng rejected; mọi xác suất và mọi hàng attention có tổng bằng 1; mọi block được trả đủ khi các chuỗi kết thúc; đổi chính sách lập lịch không đổi token sinh ra.

Lab không chứng minh: tốc độ trên GPU thật (mọi số ms là đồng hồ mô phỏng giả định); chất lượng ngôn ngữ (trọng số ngẫu nhiên, văn bản vô nghĩa); p95 hay p99 (chỉ có ba yêu cầu được phục vụ); chất lượng của INT8 trên tác vụ thật (cổng chất lượng chỉ có 40 quyết định); và hành vi của router, xác thực, hủy yêu cầu, prefix cache, preemption hay nhiều GPU, vì các phần đó không được mô phỏng.

Kết quả mong đợiOutput gồm phần mở đầu, 13 khối đánh số 1 đến 13 và dòng TỔNG KẾT. Mốc kiểm tra nhanh: D bị từ chối 429 ở cổng và E bị từ chối 400 ở bước chuẩn bị đầu vào, còn A, B, C được phục vụ; prompt của C có 9 token, chia thành các đoạn [5, 4]; TTFT của C = 22.00 ms mô phỏng = 0.65 + 14.55 + 6.80; cao điểm KV dùng 7 trên 12 block; dùng cache phải tính 41 vị trí so với 197 nếu tính lại; C dừng vì eos sau 4 token; bản INT8 khớp 40 trên 40 quyết định; cuối cùng là dòng PASS.

Tải inference-end-to-end.py ↓

inference-end-to-end.pyPYTHON
Python outputSẵn sàng.

Bài tập 1 · Vì sao D bị từ chối ở cổng còn E bị chặn ở bước chuẩn bị đầu vào?

Dựa vào khối 1 và khối 2: D và E đều bị từ chối, nhưng mã khác nhau (429 và 400) và ở hai khối khác nhau. Hãy nêu điều kiện cụ thể khiến mỗi yêu cầu bị từ chối, rồi cho biết nếu sửa QUEUE_LIMIT thành 2 thì yêu cầu nào hết bị từ chối.

Đối chiếu sau khi tự trả lời

D đến lúc đã có 3 yêu cầu trong hệ thống (A, B, C) bằng giới hạn SLOTS + QUEUE_LIMIT = 2 + 1, nên cổng trả 429 trước khi tốn công tokenize. E đến khi mới có 2 yêu cầu nên qua cổng; sau khi tách token, prompt có 8 token cộng 30 token xin sinh là 38, lớn hơn MAX_CONTEXT = 24, nên bị chặn 400 ở khâu chuẩn bị đầu vào. Khi QUEUE_LIMIT = 2 giới hạn thành 4, D được nhận (có 3 yêu cầu trong hệ thống khi nó được xét) còn E vẫn bị 400 vì lỗi ngân sách token không liên quan đến hàng đợi.

Bài tập 2 · Tách TTFT của C và thử SLOTS = 3

Từ khối 11, cộng ba thành phần của TTFT cho C và chỉ ra thành phần lớn nhất. Dự đoán rồi kiểm tra: nếu sửa SLOTS thành 3 thì TTFT của C giảm hay tăng, và D còn bị từ chối không?

Đối chiếu sau khi tự trả lời

Mặc định: 0.65 ms chuẩn bị đầu vào + 14.55 ms chờ chỗ chạy + 6.80 ms prefill = 22.00 ms; chờ chỗ chạy là lớn nhất. Với SLOTS = 3, giới hạn hệ thống thành 3 + 1 = 4 nên D không bị 429 nữa; C chỉ chờ 8.95 ms nhưng prefill kéo dài 9.00 ms vì dùng chung các bước với nhiều chuỗi hơn, nên TTFT là 18.60 ms (giảm so với 22.00 ms). Thêm chỗ chạy giảm thời gian chờ nhưng làm mỗi bước nặng hơn; vì vậy TTFT của B, C, D đều vượt ngưỡng giả định 8.00 ms trong lần chạy này.

Bài tập 3 · Dãy số nào là trọng số attention, dãy nào là xác suất từ vựng?

Với C, khối 5 in hai dãy trọng số của hai head và khối 8 in xác suất trước và sau khi lọc. Hãy nói mỗi dãy có bao nhiêu phần tử, tổng bằng bao nhiêu, và mỗi dãy dùng để làm gì. Vì sao không thể đọc 0.306 ở khối 7 là xác suất token kế tiếp?

Đối chiếu sau khi tự trả lời

Trọng số attention của một head có số phần tử bằng số vị trí đã thấy (9 ở khối 5, 10 ở khối 7 sau khi thêm token mới), tổng bằng 1, và dùng để pha trộn các vector V: nó trả lời lấy thông tin từ vị trí nào. Xác suất từ vựng có 22 phần tử (một cho mỗi token), tổng bằng 1 sau softmax, và dùng để chọn token kế tiếp. Giá trị 0.306 ở khối 7 là trọng số của vị trí 1 trong attention của head 0, không gắn với một token ứng viên nào nên không thể là xác suất token kế tiếp.

Bài tập 4 · Đổi TOKEN_BUDGET từ 6 lên 20

Dự đoán: prompt của C còn bị chia thành mấy đoạn? TTFT trung bình, khoảng đứng lớn nhất giữa hai token và token sinh ra sẽ đổi thế nào? Sửa dòng TOKEN_BUDGET rồi chạy lại để kiểm tra.

Đối chiếu sau khi tự trả lời

Prompt 9 token nằm gọn trong ngân sách 20 nên C chỉ có một đoạn [9]. Bảng khối 12 cho dòng continuous + chunked (20 token/bước) trùng dòng không chia prompt: TTFT trung bình 10.00 ms (thay vì 12.67 ms), TTFT lớn nhất 17.20 ms, nhưng khoảng đứng lớn nhất giữa hai token của một yêu cầu là 6.00 ms (thay vì 3.60 ms). Token sinh ra không đổi. Chia nhỏ prompt đổi một phần TTFT lấy khoảng đứng ngắn hơn cho các yêu cầu đang decode; đây vẫn là hệ quả của công thức chi phí giả định.

Bài tập 5 · Đổi QUANT_BITS thành 4

Sửa QUANT_BITS = 4 và chạy lại. Hãy ghi: dung lượng định dạng, sai số khôi phục trọng số lớn nhất, tỉ lệ quyết định top-1 giữ nguyên ở khối 13 và kết luận của cổng chất lượng. Điều đó cho phép khẳng định gì về chất lượng ngôn ngữ?

Đối chiếu sau khi tự trả lời

Dung lượng định dạng giảm còn 784 byte (nhỏ hơn 4.16 lần so với FP32), sai số khôi phục trọng số lớn nhất tăng lên 0.1000 (chặn nửa scale là 0.1102). Cổng chất lượng khớp A 8/8, B 7/8, C 7/8, D 5/8, E 6/8, tổng 33/40 = 82.5%, thấp hơn ngưỡng giả định 95% nên báo KHÔNG ĐẠT. Điều đó cho thấy 4 bit làm đổi quyết định chọn token của mô hình ngẫu nhiên này, nhưng không cho phép khẳng định gì về chất lượng ngôn ngữ của mô hình thật, vì lab không có mô hình đã huấn luyện và không có bộ đánh giá tác vụ.

Bài tập 6 · Kích thước block ảnh hưởng thế nào đến phần thừa của KV cache?

Sửa BLOCK_SIZE = 8 và POOL_BLOCKS = 6 (giữ nguyên tổng 48 chỗ token) rồi chạy lại. Đọc khối 6: lúc cao điểm dùng bao nhiêu block, bao nhiêu chỗ token, đang lưu bao nhiêu token và thừa bao nhiêu? So với cấu hình mặc định, block lớn có lợi và có hại gì?

Đối chiếu sau khi tự trả lời

Lúc cao điểm lab dùng 4 trên 6 block, tức 32 chỗ token để lưu 19 token, thừa 13 chỗ; với block 4 token mặc định là 28 chỗ, lưu 24 token, thừa 4 chỗ. Block lớn thì bảng block ngắn hơn và mỗi lần cấp phát phục vụ nhiều token hơn, nhưng block cuối của mỗi chuỗi dễ còn nhiều chỗ trống hơn nên lãng phí bộ nhớ nhiều hơn. Số liệu này chỉ đúng với trace nhỏ của lab; chọn kích thước block cho hệ thống thật cần đo trên workload thật.