Vì sao CPU cần cache?
Tốc độ xử lý của CPU tăng nhanh hơn rất nhiều so với tốc độ phản hồi của RAM trong vài chục năm qua — khoảng cách này thường được gọi là "Memory Wall".
Một CPU 3 GHz thực hiện xong một chu kỳ trong khoảng 0.33 ns. Nhưng một lần truy cập RAM mất khoảng 60–100 ns — tương đương 200–300 chu kỳ CPU. Nếu mọi lệnh đọc/ghi bộ nhớ đều phải đợi RAM, CPU sẽ đứng im phần lớn thời gian chỉ để chờ dữ liệu.
Cache giải quyết vấn đề này bằng cách chèn các lớp bộ nhớ nhỏ hơn nhưng nhanh hơn nhiều giữa CPU và RAM, dựa trên một quan sát thực nghiệm rất ổn định: chương trình có tính cục bộ (locality) — dữ liệu vừa dùng thường được dùng lại sớm, và dữ liệu nằm gần nhau trong bộ nhớ thường được dùng gần nhau về thời gian. Phần "Locality of reference" ở cuối tài liệu sẽ nói kỹ hơn về điều này.
Phân cấp bộ nhớ: gần CPU thì nhanh và nhỏ, xa CPU thì chậm và lớn
Mỗi cấp trong sơ đồ ở đầu trang là một sự đánh đổi giữa tốc độ, dung lượng và chi phí trên mỗi byte.
| Cấp | Dung lượng điển hình | Độ trễ điển hình | Vị trí vật lý |
|---|---|---|---|
| Thanh ghi CPU | Vài trăm byte | ~0 chu kỳ | Ngay trong ALU của lõi CPU |
| L1 Cache | 32–64 KB / lõi | ~4 chu kỳ (~1 ns) | Bên trong mỗi lõi, tách riêng I-cache/D-cache |
| L2 Cache | 256 KB – 1 MB / lõi | ~12 chu kỳ (~3–4 ns) | Riêng cho mỗi lõi hoặc từng cặp lõi |
| L3 Cache | Vài MB – vài chục MB | ~40 chu kỳ (~10–20 ns) | Dùng chung cho toàn bộ chip |
| RAM | Vài GB – vài trăm GB | ~200 chu kỳ (~60–100 ns) | Gắn ngoài CPU, qua bus bộ nhớ |
| Ổ đĩa (SSD/HDD) | Hàng trăm GB – vài chục TB | SSD ~10–100 µs HDD ~1–10 ms | Thiết bị lưu trữ ngoài, qua PCIe/SATA |
Số liệu mang tính minh hoạ cho các CPU x86 phổ biến gần đây; con số thực tế thay đổi theo từng dòng chip.
Cache hit & cache miss
Khi CPU cần một địa chỉ bộ nhớ, nó tìm từ cấp gần nhất (L1) rồi mới đi xa dần. Hit là tìm thấy ngay; miss là phải hỏi cấp tiếp theo — mỗi lần miss cộng dồn thêm độ trễ ("miss penalty").
yêu cầu dữ liệu
Ba nguyên nhân gây miss ("3 C's"): compulsory miss (lần đầu truy cập, chưa từng được nạp vào cache), capacity miss (cache quá nhỏ để giữ hết dữ liệu đang dùng), và conflict miss (nhiều vùng nhớ tranh nhau cùng một vị trí trong cache — liên quan trực tiếp tới cách ánh xạ ở phần tiếp theo).
Một khối nhớ được đặt vào cache như thế nào?
Cache được chia thành các "dòng" (cache line, thường 64 byte). Ba chiến lược ánh xạ khối nhớ vào dòng cache dưới đây đánh đổi giữa tốc độ tra cứu và tỷ lệ miss.
Direct-mapped
Set-associative (2-way)
Fully associative
Địa chỉ được "cắt" thành 3 phần
Ví dụ minh hoạ (không theo đúng tỉ lệ pixel) cho một L1 cache 32 KB, 8-way, dòng 64 byte, địa chỉ 64-bit → 64 tập → cần 6 bit index, 6 bit offset, còn lại 52 bit tag.
CẤU TRÚC VẬT LÝ CỦA MỘT DÒNG CACHE (CACHE LINE BLOCK ON SRAM):
Minh hoạ (không theo đúng tỉ lệ pixel) cho một L1 cache 32 KB, 8-way, 52 bit tag, 1 bit Valid Bit, 1 bit Dirty Bit, vài MESI/LRU Bits, KHỐI DỮ LIỆU THÔ 64 BYTES.
Vì sao thứ tự khai báo field cũng ảnh hưởng tới hiệu năng
CPU đọc bộ nhớ theo từng "word" (4/8 byte) hoặc theo cả cache line, không đọc từng byte lẻ. Một giá trị được coi là aligned khi địa chỉ của nó chia hết cho kích thước của chính nó (một int 4 byte nên nằm ở địa chỉ chia hết cho 4).
Truy cập misaligned — dữ liệu nằm vắt qua ranh giới word/cache line — buộc CPU phải đọc 2 lần rồi ghép lại, chậm hơn hẳn; một số kiến trúc (ARM cũ, chế độ strict) còn từ chối thẳng và bắn lỗi bus. Vì vậy trình biên dịch tự thêm các byte đệm (padding) vào struct để mọi field đều rơi đúng vào địa chỉ hợp lệ — và thứ tự khai báo field quyết định việc đệm nhiều hay ít.
Tổng kích thước: 24 byte — trong đó 10 byte là padding (~42% lãng phí).
Tổng kích thước: 16 byte — chỉ còn 2 byte padding. Nhỏ hơn 33% so với bản gốc, chỉ nhờ đổi thứ tự field.
Quy tắc chung: khai báo field theo thứ tự kích thước giảm dần (lớn → nhỏ) để trình biên dịch cần chèn ít padding nhất.
Locality of reference
Temporal locality: dữ liệu vừa dùng có khả năng cao được dùng lại sớm (ví dụ biến đếm vòng lặp). Spatial locality: dữ liệu nằm gần nhau trong bộ nhớ thường được dùng gần nhau về thời gian — đây là lý do cache luôn nạp nguyên một cache line 64 byte thay vì chỉ 1 byte cần dùng.
Cache mang lại hiệu quả bao nhiêu?
Hai cách nhìn: so sánh trực tiếp độ trễ giữa các cấp, và công thức AMAT (Average Memory Access Time) cho thấy chỉ vài phần trăm hit rate cũng đổi khác biệt rất lớn.
So sánh độ trễ (thang log)
Máy tính AMAT — độ trễ trung bình mỗi lần truy cập bộ nhớ
Giả định độ trễ cố định: L1 = 1 ns, L2 = 4 ns, L3 = 15 ns, RAM = 80 ns. Kéo thanh trượt để đổi hit rate từng cấp.
Những điều đáng nhớ nhất
- 1Cache tồn tại vì khoảng cách tốc độ giữa CPU và RAM quá lớn (Memory Wall) — mỗi cấp cache là một điểm cân bằng giữa tốc độ, dung lượng và chi phí.
- 2Hit rate ảnh hưởng tới hiệu năng nhiều hơn trực giác — vài phần trăm hit rate thấp hơn ở L1/L2 có thể làm AMAT tăng gấp nhiều lần.
- 3Set-associative là lựa chọn thực tế phổ biến nhất, cân bằng giữa tốc độ tra cứu (direct-mapped) và tỷ lệ miss thấp (fully associative).
- 4Alignment không phải chi tiết vặt: sắp field theo thứ tự kích thước giảm dần có thể giảm 1/3 kích thước struct, giảm số cache line cần nạp.
- 5Viết code tận dụng spatial/temporal locality (duyệt tuần tự, gom dữ liệu hay dùng cùng nhau) gần như luôn nhanh hơn code "đúng thuật toán nhưng truy cập bộ nhớ ngẫu nhiên".