Q

Quản trị viên

Là một lập trình viên yêu công nghệ, thích viết blog để chia sẻ kiến thức, kinh nghiệm lập trình và những điều thú vị trong thế giới tech.

2300 bài viết · Website
Giải thuật 03/09/2026 8 phút

Đọc 1 byte, CPU nạp cả 128: cái giá của đọc thưa

Đọc một byte thì chỉ tốn một byte băng thông? Đo ra: CPU nạp cả một dòng cache mỗi lần chạm bộ nhớ, nên đọc thưa (mỗi phần tử một dòng) chậm gấp 10 lần đọc liền. Và ngưỡng bão hòa lộ kích thước dòng thật của máy: host ARM này (Apple Silicon) dùng dòng 128 byte, không phải 64 byte như x86 quen thuộc.

Giải thuật 03/09/2026 8 phút

SIMD nhanh gấp 4 lần — hoặc gấp 0, tùy vòng lặp

Trình biên dịch -O2 tự vector hóa hết mọi vòng lặp số? Đo ra: một kernel tính từng phần tử độc lập nhanh 3,9 lần khi vector hóa (NEON 128-bit gói 4 float mỗi lệnh), nhưng một vòng có phụ thuộc — y[i] cần y[i-1] — thì không vector hóa được, vector và scalar bằng nhau y hệt. SIMD nhanh N lần chỉ khi vòng vector hóa được và compute-bound.

Giải thuật 03/09/2026 8 phút

Cùng số phép cộng, tách 8 biến ra nhanh gấp 6,7 lần

Cùng số phép tính thì cùng thời gian? Đo ra: cộng dồn một mảng bằng một biến tích lũy tốn 0,569 ns mỗi phép cộng, nhưng dùng 8 biến độc lập rồi gộp cuối chỉ tốn 0,085 ns — nhanh 6,7 lần dù y hệt số phép cộng. Khác biệt là đồ thị phụ thuộc: chuỗi phụ thuộc phải chờ từng bước, còn các phép độc lập chạy song song trong một lõi (ILP).

Giải thuật 03/09/2026 9 phút

Vòng O(n) trông cong như O(n log n): thủ phạm là cache

Cứ đo thời gian là biết ngay độ phức tạp? Đo ra: ở n nhỏ hằng số và overhead lấn át nên tỉ số T(2n)/T(n) lung tung (1,0/4,0/1,75), và cache tạo gãy khúc — truy cập ngẫu nhiên nhảy từ 0,9ns (L1) lên 96ns (RAM), khiến vòng O(n) trông siêu tuyến tính. Nhưng đo đúng cách thì tỉ số hội tụ chính xác: O(n)→2,00, O(n log n)→2,07.

Giải thuật 03/09/2026 9 phút

Gói 4 trường vào 1 số: nhỏ 4 lần, shift/mask miễn phí

Gói nhiều trường vào một từ chỉ tiết kiệm bộ nhớ mà làm chậm CPU vì shift/mask? Đo ra: một bản ghi 4 trường nhỏ gói vào một uint32 nhỏ hơn 4 lần (16 → 4 byte), và shift/mask khi đọc gần như miễn phí — duyệt khối lớn còn nhanh hơn 1,1 lần nhờ cache, mảng nóng thì hòa. Nhưng lợi ích chính là bộ nhớ, không phải tốc độ: throughput chỉ nhanh 1,1 lần chứ không tỉ lệ với mức nén.

Giải thuật 03/09/2026 8 phút

Chia cho 7 gần như miễn phí, chia cho một biến đắt gấp 2,7 lần — cùng một dấu gạch chéo

Chia là chia, tốn như nhau? Tôi đo và thấy cùng dấu / cho hai chi phí khác hẳn: chia cho hằng số biến thành nhân magic + dịch (vector hóa được, gần như miễn phí), còn chia cho biến là udiv đắt 2,7 lần. Nhưng trong chuỗi phụ thuộc, cái mẹo magic lại không thắng — lợi ở thông lượng, không ở độ trễ.