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

Cấu trúc nói 'có thể có' này thay 25 MB bằng 1,2 MB — và không bao giờ nói dối một chiều

Bloom filter trả lời 'phần tử này có trong tập không' một cách không chính xác: dương tính giả 0,81% ở 10 bit/phần tử, 14,7% ở 4 bit, giảm theo hàm mũ khi thêm bit nhưng không bao giờ về 0. Đổi lại nó không bao giờ báo âm tính giả và chỉ tốn 1,2 MB thay vì 25 MB. Có số hàm băm tối ưu. Tôi đo.

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

Cùng dữ liệu, chỉ xếp lại trong bộ nhớ, một vòng lặp nhanh gấp 4,7 lần

Gói các trường của một object lại thành struct (AoS) là cách tự nhiên nhất. Nhưng khi chỉ cộng một trường của struct rộng, AoS chậm tới 4,7 lần SoA vì mỗi dòng cache kéo cả struct về mà chỉ dùng một trường. Đọc hết mọi trường thì AoS lại ngang. Mẫu truy cập quyết định bố cục, không phải cái nào nghe tự nhiên hơn. Tôi đo.

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

Sắp mảng trước khi chạy, cùng code lại nhanh gấp 1,7 lần — vì sao?

Cùng một đoạn code, cùng những con số, chỉ khác mảng được sắp trước hay chưa: bản chưa sắp chạy 660 ms, bản đã sắp 379 ms — chênh 1,7 lần. Không phải ít phép tính hơn, không phải cache. Thủ phạm là dự đoán nhánh của CPU, thứ big-O không hề thấy. Viết không nhánh xóa hẳn (183 ms). Tôi đo.

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

Bật -O3 nhanh gấp 4 lần — rồi đổi một dấu, tụt xuống chậm gấp 11

Bật -O3, vòng nhân mảng float nhanh 3,9 lần nhờ NEON làm 4 float mỗi lệnh. Nhưng chỉ thêm một phụ thuộc chuỗi là trình biên dịch lặng lẽ bỏ vector hóa, tụt về scalar chậm 11,6 lần — không một dòng cảnh báo. SIMD không phải phép màu của cờ, mà là phần thưởng cho vòng viết đúng dạng. Tôi đo.

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

'Thêm phần tử là O(1)' — vậy sao có một lần nó ngốn 392 micro giây?

Thêm vào mảng động là O(1) khấu hao, nhưng đo đường cong từng thao tác thấy gai O(n): lần cấp phát cuối sao chép 2,1 triệu phần tử mất 392µs, gấp 400.000 lần một lần thêm thường. Và tăng dung lượng mỗi lần +1 để 'tiết kiệm bộ nhớ' hóa ra là O(n²), chậm 17.851 lần. Tôi đo cả đường cong.