Kỹ thuật căn chỉnh bộ nhớ trong C
Căn chỉnh bộ nhớ (Memory Alignment) trong C/C++
Trước khi đi sâu vào chi tiết, bạn nên đọc bài viết này:
👉 Data Structure Alignment trong C/C++ Bài viết đó sẽ giúp bạn hiểu căn chỉnh bộ nhớ (memory alignment) là gì và vì sao CPU lại quan tâm đến nó.
Tôi sẽ để công thức ở đây nếu bạn chỉ muốn dùng và không cần hiểu sâu về nó
#define mp_align(n, alignment) (((n)+(alignment-1)) & ~(alignment-1))
Công thức này giúp làm tròn lên giá trị n thành bội số gần nhất của alignment.
Điều đặc biệt là nó dùng phép toán bit, nên CPU xử lý rất nhanh, nhanh hơn nhiều so với cách tính toán bằng chia rồi nhân lại.
📌Hãy nhớ rằng: CPU rất thích thao tác bit!
Ví dụ
#include <stdio.h>
#define mp_align(n, alignment) (((n) + (alignment - 1)) & ~(alignment - 1))
int main() {
int n1 = 13;
int n2 = 16;
int aligned1 = mp_align(n1, 8);
int aligned2 = mp_align(n2, 8);
printf("n1 = 13 sau khi can chinh align 8 = %d\n", aligned1);
printf("n2 = 16 sau khi can chinh align 8 = %d\n", aligned2);
return 0;
}
Đầu ra chương trình
n1 = 13 sau khi can chinh align 8 = 16
n2 = 16 sau khi can chinh align 8 = 16
Trong ví dụ này, tôi lấy căn chỉnh cho 8, nghĩa là những số là bộ số của 8 (chia hết cho 8)
Bây giờ tôi sẽ viết các số dưới dạng nhị phân
32 16 8 4 2 1
0 0 0 0 0 0 = số 0
0 0 0 1 0 0 = số 4
0 0 1 0 0 0 = số 8
0 0 1 1 1 1 = số 15
0 1 0 0 0 0 = số 16
0 1 0 1 1 0 = số 22
0 1 0 1 1 1 = số 23
0 1 1 0 0 0 = số 24
1 0 0 0 0 0 = số 32
Nếu bạn để ý kỹ các số chia hết cho 8 là các số có bit 0 1 2
đều = 0.Điều này có nghĩa là nếu ta ép 3 bit cuối của một số bất kỳ về 0, số đó sẽ trở thành bội của 8.
Ví dụ: số 10
1 0 1 0 = số 10
ta lấy:
1 0 1 0
& 0 0 0
-------
1 0 0 0 -> 8
Giá trị nhận được là 8 chia hết cho 8, OK rồi nhỉ. Bạn có thể lấy
nhiều ví dụ khác để kiểm chứng
Lúc này đây ta sẽ triển khai được
n & ~(align - 1) (1) với align = 8
=> n & 1 000 ( 3 bit cuối vấn đảm bảo là 0 để chia hết cho 8).
👉Đây được gọi là căn chỉnh xuống
số 23 thì về 16
số 10 thì về 8
Vây giờ ta muốn căn chỉnh lên thì sao
17 thì lên 24 thay vì về 16
lúc này ta cần phải + align
để căng chỉnh lên
(n + algn) & ~(align - 1) (2)
OK, tới đây bạn nghĩ đã tạm ổn rồi ư. Chưa đâu, hãy cho một ví dụ để xem xét
Với n = 16
, algn = 8
Ráp vào công thức tính (2) phía trên ta được: 24 ò đây là kết quả không mong muốn tại vì ở đây 16 đã chia hết cho 8 rồi, nên không cần phải căng chỉnh nữa.
Lúc này ta cần phải -1
để khắc phục được nhược điểm của công thức (2)
(n + (algn - 1)) & ~(align - 1) (3)
Bây giờ bạn có thể dùng (3) để căn chỉnh rồi.
Chắc có một vài bạn thắc mắc tại vì sao phải căng chỉnh lên chứ không phải là căng chỉnh xuống ❓
Trong ví dụ trước tôi chỉ nói đơn giản là làm tròn một số chia hết cho 8 thôi. Nhưng trong thực tế điều này được ứng dụng cho việc đặt địa chỉ dữ liệu ở đâu.
Khi bạn khai báo một biến x
nào đó trong chương trình có địa chỉ là số 10
chẳng hạn. Điều đó có nghĩa là những địa chỉ ở phía trước đã có dữ liệu rồi nên CPU không thể cấp cho bạn để lưu trữ x
.
- Nếu bây giờ ta làm căng chỉnh địa chỉ xuống: chỉ sẽ ở vị trí
số 8
thì dữ liệu sẽ bị ghi đè cho nên ta cần phải căng chỉnh lên để tránh lỗi này xảy ra.
Tôi hi vọng qua bài viết này, có thể giúp ích gì đó cho bạn.
Tất nhiên mỗi bài viết trong seri C thực tế này thường bạn sẽ hk thấy dc giá trị của nó. Bởi vì được tôi viết ra trong 1 phần của dự án nên không thể bao quát được hết. Nhưng tôi vẫn viết để hi vọng sau này nếu bạn gặp vấn đề cần căn chỉnh memory thì có thể quay lại xem lúc đó chắc chắn nó sẽ hữu ích.
All rights reserved