I/O, Lưu trữ & Tài nguyên/Tổng kết module — Đường đi I/O & GPU
18/26
Bài 18 / 26~6 phútĐường đi I/O & GPUMiễn phí lượt xem

Tổng kết module — Đường đi I/O & GPU

Cheat sheet đường đi I/O từ thiết bị tới ứng dụng, glossary, pitfall tổng hợp và self-assessment đối chiếu bảy learning outcome của module.

TL;DR: Module này lần theo một tuyến đường duy nhất, từ chỗ CPU chạm vào thanh ghi thiết bị cho tới chỗ dữ liệu nằm trong biến của chương trình bạn. Trên tuyến ấy có bốn câu hỏi lặp đi lặp lại ở mọi tầng: ai chủ động, ai tốn chu kỳ CPU, dữ liệu bị chép mấy lần, và cái gì đang chờ cái gì. Trả lời được bốn câu đó ở một tầng bất kỳ thì bạn chẩn đoán được tầng ấy. Đây là một trang để đánh dấu và quay lại.

Đã đi qua những gì

Bạn bắt đầu ở chỗ thấp nhất mà phần mềm còn với tới: vài thanh ghi nằm trong không gian địa chỉ vật lý, và một cái cây PCIe nối chúng về CPU. Từ đó lên một nấc, câu hỏi đổi thành ai trả tiền cho việc chờ và việc chép, và câu trả lời hoá ra không cố định — polling thắng interrupt ở tốc độ cao, đúng chỗ trực giác nói ngược lại.

Bài 03 ráp mọi thứ thành một tuyến đường liền mạch, và từ đó hai bài kế tiếp tấn công hai trục tối ưu khác nhau: cách chờ, và số bản sao. Hai bài cuối lấy GPU làm ví dụ cực đoan — một cỗ máy tính toán mạnh tới mức chính đoạn dây nối nó với CPU trở thành nút thắt.

Sau khai niem cua module nam tren cung mot tuyen duong, moi cai chot mot cau hoi

🗺️ Cheat sheet

Khái niệmCốt lõiPitfall hay gặp
Thanh ghi thiết bịÔ nhớ có side effect, không phải RAMDereference con trỏ trần, trình biên dịch tối ưu mất lần đọc
MMIO so với port I/OThiết bị chiếm dải địa chỉ vật lý, dùng lệnh load/store thườngTưởng cần lệnh CPU đặc biệt mới điều khiển được phần cứng
PCIeCây điểm-nối-điểm, băng thông theo lane từng kheTưởng là bus dùng chung nên cắm thêm card là chậm cả máy
Posted writeGhi ra PCI được xác nhận từ lúc rời CPUCoi hàm ghi trả về là bằng chứng thiết bị đã nhận
PollingĐốt lõi CPU, đổi lại độ trễ thấp nhấtCoi polling luôn là dấu hiệu code kém
InterruptTrả CPU cho việc khác, mỗi lần tốn một chuyển ngữ cảnhBỏ qua livelock khi tần suất lên cao
DMAThiết bị tự chuyển dữ liệu vào RAM sau khi đọc descriptorChạm buffer đang thuộc quyền thiết bị trong streaming DMA
NAPITắt ngắt rồi poll theo lô có budgetNghĩ polling và interrupt loại trừ nhau
Page cacheCổng rẽ sớm nhất trên đường read()Benchmark I/O mà quên xoá cache
blk-mqHai tầng hàng đợi, tag cho hoàn tất ngoài thứ tựĐổi I/O scheduler như một mẹo tăng thông lượng
BlockingDễ viết đúng, đắt theo số kết nốiDùng cho hàng vạn kết nối
select/pollChi phí theo tổng số fd mỗi lượtTưởng khác epoll chỉ ở cú pháp
epollTập fd sống trong kernel, trả về fd sẵn sàngEdge-triggered mà không đọc tới EAGAIN
io_uringMô hình hoàn tất, hàng đợi chia sẻ với user spaceTưởng epoll async được file thường
Zero-copysendfile bỏ hai bản sao do CPUKỳ vọng zero-copy trong khi vẫn bật TLS
SIMT / warp32 thread dùng chung một luồng lệnhNghĩ GPU chạy song song cả hai nhánh if
Warp divergenceHai nhánh bị tuần tự hoáSo GPU với CPU bằng số nhân
Host-device copyPCIe hẹp hơn hẳn đường GPU tới bộ nhớ của nóĐo tốc độ kernel rồi kết luận về tốc độ pipeline

📖 Glossary module

Thuật ngữĐịnh nghĩa một câu
Thanh ghi thiết bịÔ nhớ do phần cứng bày ra, chạm vào là gây ra hành động chứ không chỉ đọc ghi dữ liệu.
MMIOCách gán cho thanh ghi thiết bị một dải trong không gian địa chỉ vật lý chung với RAM.
ioremapHàm kernel tạo ánh xạ từ dải địa chỉ vật lý của thiết bị vào không gian ảo của kernel.
BARThanh ghi qua đó endpoint PCIe khai báo nó cần bao nhiêu byte không gian địa chỉ.
Posted writeLệnh ghi ra bus PCI được xác nhận ngay khi rời CPU, chưa chắc đã tới thiết bị.
PollingĐọc lặp lại thanh ghi trạng thái cho tới khi thiết bị báo xong.
IRQĐường tín hiệu thiết bị dùng để yêu cầu CPU ngưng việc đang làm và chạy trình xử lý ngắt.
DMACơ chế thiết bị tự chuyển dữ liệu giữa nó và RAM mà không qua CPU.
DescriptorCấu trúc trong RAM mô tả cần chuyển dữ liệu gì, đi đâu, để thiết bị tự đọc.
IOMMUTầng ánh xạ địa chỉ cho thiết bị, vừa cách ly vừa gom trang rời rạc thành vùng liền mạch.
Bounce bufferVùng trung gian kernel phải chép qua khi thiết bị không địa chỉ hoá được buffer gốc.
NAPICơ chế mạng của Linux tắt ngắt rồi chuyển sang vòng poll có budget khi tải cao.
LivelockTrạng thái máy bận 100% xử lý thông báo mà thông lượng hữu ích về gần không.
VFSLớp trừu tượng khiến read() chạy như nhau trên mọi loại filesystem.
bioĐơn vị mô tả một yêu cầu I/O ở block layer: đọc hay ghi, sector nào, vào trang nhớ nào.
blk-mqKiến trúc hàng đợi hai tầng của block layer, staging ở phần mềm và dispatch ở phần cứng.
TagĐịnh danh gắn kèm mỗi yêu cầu để thiết bị báo lại đúng yêu cầu nào đã hoàn tất.
O_NONBLOCKCờ khiến thao tác trả về EAGAIN ngay thay vì nằm chờ dữ liệu.
Level-triggeredChế độ epoll báo chừng nào còn dữ liệu chưa đọc hết.
Edge-triggeredChế độ epoll chỉ báo một lần mỗi khi trạng thái đổi sang sẵn sàng.
io_uringGiao diện I/O dựa trên hai hàng đợi chia sẻ giữa kernel và user space, làm việc theo hoàn tất.
Zero-copyNhóm kỹ thuật giữ dữ liệu trong kernel để bỏ các bản sao do CPU thực hiện.
Scatter-gather DMAKhả năng thiết bị tự gom dữ liệu từ nhiều vùng nhớ rời rạc, bỏ nốt bản sao gom sẵn.
WarpNhóm 32 thread GPU cùng chạy một lệnh tại mỗi thời điểm.
SIMTMô hình thực thi cho phép mỗi thread có luồng điều khiển riêng trong khi vẫn chạy như một phần của warp.
Warp divergenceHiện tượng hai nhánh rẽ trong một warp bị chạy tuần tự, mỗi lượt một phần warp ngồi không.
CoalescingViệc phần cứng gộp các truy cập bộ nhớ của một warp thành ít giao dịch nhất có thể.
Pinned memoryBộ nhớ host bị ghim không cho page out, để DMA nhắm thẳng vào mà không cần trung chuyển.

⚠️ Pitfall tổng hợp

Coi thanh ghi thiết bị như biến thường → ✅ Dùng readl/writel với con trỏ __iomem; con trỏ trần thì trình biên dịch bỏ mất lần đọc thứ hai và vòng lặp chờ quay vô tận.

Tin rằng ghi ra PCI xong là thiết bị đã nhận → ✅ Ghi là posted; đọc ngược lại một thanh ghi của chính thiết bị đó mới có bảo đảm.

Coi polling luôn là lãng phí → ✅ Ở tần suất cao, interrupt gây livelock; NAPI cố ý chuyển sang polling, và driver độ trễ thấp chấp nhận đốt một lõi.

Chạm buffer đang thuộc quyền thiết bị trong streaming DMA → ✅ Gỡ ánh xạ hoặc đồng bộ theo DMA API trước; lỗi loại này hỏng dữ liệu rời rạc chứ không crash, nên cực khó tái hiện.

Benchmark read() mà không xoá page cache → ✅ Lần thứ hai gần như luôn trúng cache, và bạn đang đo RAM chứ không đo thiết bị.

Dùng epoll edge-triggered mà đọc một lần rồi quay lại epoll_wait → ✅ Lặp read() tới khi gặp EAGAIN; đọc được ít hơn sức chứa không phải dấu hiệu đã cạn.

Đăng ký file thường vào epoll để "cho khỏi chặn" → ✅ File thường luôn được coi là sẵn sàng; dùng thread riêng hoặc io_uring.

Kỳ vọng zero-copy trong khi bật TLS → ✅ Mã hoá buộc dữ liệu ra user space, đường đi quay về đủ bốn bản sao. Zero-copy mất hẳn chứ không mất một phần.

Nghĩ GPU chạy song song cả hai nhánh if → ✅ Thread cùng warp rẽ hai hướng thì hai nhánh chạy tuần tự; song song thật nằm giữa các warp.

Đo tốc độ kernel GPU rồi kết luận về tốc độ pipeline → ✅ Đo trọn vòng gồm cả hai đầu chép; kernel 4 ms trong pipeline 40 ms nghĩa là 90% thời gian ở chỗ khác.

Thêm thread hoặc thêm GPU khi nút thắt nằm ở tầng dưới → ✅ Bus và thiết bị có trần cứng; thêm worker chỉ tăng tranh chấp. Xác định tầng đang nghẽn trước khi thêm bất cứ thứ gì.

✅ Self-assessment

Bạn đã đạt module này nếu trả lời được:

  • Giải thích được CPU điều khiển thiết bị qua thanh ghi thiết bị, memory-mapped I/O và bus PCIe — nếu chưa: bài 01 mục 2, 3 và 4.
  • So sánh được polling, interrupt và DMA về việc ai tốn chu kỳ CPU khi dữ liệu di chuyển — nếu chưa: bài 02 mục 2 và 3.
  • Lần theo được một lời gọi read() qua syscall, VFS, page cache, block layer và driver tới thiết bị rồi quay về — nếu chưa: bài 03.
  • Chọn được mô hình chờ I/O cho server nhiều kết nối dựa trên số fd và chi phí syscall — nếu chưa: bài 04 mục 2 và 3.
  • Giải thích được zero-copy loại bỏ những bản sao nào và giới hạn của nó — nếu chưa: bài 05 mục 2, 3 và 4.
  • Giải thích được GPU đạt thông lượng cao nhờ đâu — SIMT, băng thông bộ nhớ, đánh đổi độ trễ — nếu chưa: bài 06.
  • Chẩn đoán được vì sao một pipeline GPU nghẽn ở khâu chuyển dữ liệu thay vì ở phép tính — nếu chưa: bài 07 mục 4.

Chưa chắc mục nào thì cách kiểm tra thật nhất là lấy một service bạn đang chạy, chạy strace -T lên nó, và tự hỏi mỗi lời gọi đang dừng ở tầng nào trên tuyến đường của bài 03.

🚀 Tiếp theo

Hai module đầu trả lời dữ liệu nằm ở đâudữ liệu đi đường nào. Module cuối cùng của khoá đổi sang câu hỏi thực dụng nhất: làm sao biết máy đang nghẽn ở đâu. Bạn sẽ đọc tophtop cho đúng, phân biệt RSS với VSZ, đo I/O bằng iostat, hiểu cgroups chặn tài nguyên container thế nào, và đọc được một lần OOM kill từ dmesg.

Đó cũng là chỗ mọi thứ của hai module trước được đem ra dùng: khi iostat báo độ sâu hàng đợi cao, bạn sẽ biết nó ứng với chặng nào; khi một container bị giết, bạn sẽ biết trang nào đáng lẽ được thu hồi trước.

📚 Tài liệu mở rộng

Bài tiếp theo: Tổng quan module — Tài nguyên & đo lường

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Bài này có giúp bạn hiểu bản chất không?

Hỏi đáp về bài này

Chưa có câu hỏi

Đặt câu hỏi

Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).

Đặt câu hỏi đầu tiên

Bài tiếp theo

Tổng quan module — Tài nguyên & Đo lường