Alignment và padding struct

Alignment data và padding data

Data alignment là cách mà trình biên dịch sắp xếp dữ liệu trong memory. Việc sắp xếp dữ liệu liên quan tới 2 khái niệm: data alignment và data padding.

Đối với những PC hiện nay, việc đọc/ghi dữ liệu trong bộ nhớ được với kích thước của  WORD (OS 32 bit: kích thước WORD: 4 byte) hoặc lớn hơn. Data alignment là cách sắp xếp dữ liệu sao cho kích thước bằng bội số của kích thước 1 word = 4k (byte). (k = 0, 1, 2,…). Để alignment data, đôi khi chúng ta cần phải add thêm các byte giả (dummy) vào vị trí thích hợp để đảm bảo data alignment, được gọi là padding data.

Data Alignment  làm tăng performance do việc đọc/ghi thao tác trên block data có kích thước bằng bội số của WORD.

Data Alignment trên Visual Studio C++

Khi chúng ta định nghĩa struct trong project Visual Studio C++, trình biên dịch (complier) sẽ cố gắng allocate dữ liệu theo alignment data.

Trình biên dịch sẽ lấy kiểu dữ liệu có kích thước lớn nhất trong struct làm kích thước đường biên cho việc alignment data.

Ví dụ 1:

#include <stdio.h>
#include <conio.h>

struct A
{
    char a;     // 1 byte
    int b;      // 4 bytes
    short c;    // 2 bytes
    char d;     // 1 byte
};

void main()
{
    printf("size = %d", sizeof(A));
    getch();
}

Kết quả:

Khi mới học lập trình C/C++, mình nghĩ kích thước của struct A bằng: 1+4+2+1 = 8 (byte). Wrong!!!

Kích thước của struct A chỉ bằng tổng các thành phần cộng lại khi bạn sử dụng packing. Mình sẽ trình bày ở mục bên dưới. Thực tế kích thước của struct A bằng 12. Lý do, trình biên dịch đã padding data (add thêm một số byte giả trong lúc biên dịch để đảm bảo data alignment) như sau:

struct A
{
    char a;     // 1 byte
    char dummy1[3]; // padding 3 bytes
    int b;      // 4 bytes
    short c;    // 2 bytes
    char d;     // 1 byte
    char dummy2[1]; // padding 1 byte
};

Trình biên dịch chọn kích thước kiểu int (4 bytes) để align data.

  • 3 byte dummy1[3] được add thêm vào để: sizeof(a) + sizeof(dummy1) = 4 bytes
  • biến int b có kích thước 4 byte nên không cần padding.
  • 1 byte dummy2[1] được add thêm vào để: sizeof(c) + sizeof(d) + sizeof(dummy2) = 4 bytes.
Data Alignment
Data Alignment

Struct packing và Alignment

Trình biên dịch tự động alignment data làm tăng performance của hệ thống. Tuy nhiên, kích thước struct lớn hơn dự định ban đầu. (do padding thêm một số byte) → làm tăng kích thước chương trình.

Để kiểm soát kích thước của struct mà vẫn đảm bảo performace, một khái niệm mới struct packing ra đời.

Mối liên hệ giữa alignment và packing data

  • Nếu packsize >= kích thước đường biên, giá trị packsize bị bỏ qua và trình biên dịch lấy kích thước đường biên để align struct
  • Nếu packsize < kích thước đường biên, trình biên dịch align struct dựa vào packsize.

Packsize là kích thước đóng gói struct. Được setting bằng 2 cách:

Cách 1: Chọn properties → Code Generation → Struct Member Alignment

Setting packsize
Setting packsize

packsize có các giá trị 1, 2, 4, 8, 16 bytes. (Defaut: 8 bytes)

Như ví dụ 1, packsize là default (8 bytes), nên trình biên dịch lấy sizeof(int) = 4 bytes làm đường biên. Dưới đây, mình đưa thêm một ví dụ packsize = 2.

 Ví dụ 2: source code giống ví dụ 1 + packsize = 2, kết quả size = 10. Trình biên dịch lấy 2 bytes làm đường biên để align data như sau:

struct A
{
    char a;     // 1 byte
    char dummy1[1]; // padding 1 byte
    int b;      // 4 bytes
    short c;    // 2 bytes
    char d;     // 1 byte
    char dummy2[1]; // padding 1 byte
};
Padding data
Padding data

Cách 2: Dùng tiền xử lí #pragma pack([n]) (n = 1, 2, 4, 8, 16)

Ví dụ 3:

#include <stdio.h>
#include <conio.h>

# pragma pack (1)
struct A
{
    char a;     // 1 byte
    int b;      // 4 bytes
    short c;    // 2 bytes
    char d;     // 1 byte
};
# pragma pack ()

void main()
{
    printf("size = %d", sizeof(A));
    getch();
}

Kết quả: size = 8

Be the first to comment

Leave a Reply