Căn chỉnh bộ nhớ: làm thế nào để sử dụng alignof / alignas?


81

Tôi làm việc với bộ nhớ được chia sẻ ngay bây giờ.

Tôi không thể hiểu alignofalignas.

cppreference không rõ ràng: alignoftrả về "alignment" nhưng "alignment" là gì? số byte cần thêm để khối tiếp theo được căn chỉnh? kích thước đệm? Stack tràn / các mục blog cũng không rõ ràng.

Ai đó có thể giải thích rõ ràng alignofalignas?


1
cppreference đang cố gắng để trở thành một tài liệu tham khảo chứ không phải là một hướng dẫn
Cubbi

1
@Cubbi: bạn cũng có thể kiểm tra tại cplusplus.com, có cuộc tranh luận Mà trang web tốt hơn, đối với một số chủ đề cplusplus là tốt hơn, cho cppreference khác là tốt hơn, tôi thấy rằng thời gian cả hai trang web nhất định không phải là eonugh
CoffeDeveloper

2
@DarioOO Tôi chỉ trả lời tại sao cppreference không giải thích khái niệm căn chỉnh trên alignoftrang (nó hiện có, trên trang đối tượng đang tiến hành ). Tôi không thấy cplusplus.com có ​​liên quan như thế nào.
Cubbi

Câu trả lời:


81

Căn chỉnh là một hạn chế về vị trí bộ nhớ mà byte đầu tiên của giá trị có thể được lưu trữ. (Cần phải cải thiện hiệu suất trên bộ xử lý và cho phép sử dụng một số hướng dẫn chỉ hoạt động trên dữ liệu có căn chỉnh cụ thể, ví dụ: SSE cần được căn chỉnh thành 16 byte, trong khi AVX là 32 byte.)

Căn 16 có nghĩa là địa chỉ bộ nhớ là bội số của 16 là địa chỉ hợp lệ duy nhất.

alignas

buộc căn chỉnh đến số byte yêu cầu. Bạn chỉ có thể căn chỉnh theo lũy thừa của 2: 1, 2, 4, 8, 16, 32, 64, 128, ...

#include <cstdlib>
#include <iostream>

int main() {
    alignas(16) int a[4];
    alignas(1024) int b[4];
    printf("%p\n", a);
    printf("%p", b);
}

đầu ra ví dụ:

0xbfa493e0
0xbfa49000  // note how many more "zeros" now.
// binary equivalent
1011 1111 1010 0100 1001 0011 1110 0000
1011 1111 1010 0100 1001 0000 0000 0000 // every zero is just a extra power of 2

từ khóa khác

alignof

rất thuận tiện, bạn không thể làm điều gì đó như

int a[4];
assert(a % 16 == 0); // check if alignment is to 16 bytes: WRONG compiler error

nhưng bạn có thể làm

assert(alignof(a) == 16);
assert(alignof(b) == 1024);

lưu ý rằng trong thực tế, điều này nghiêm ngặt hơn một hoạt động đơn giản "%" (modulus). Trên thực tế, chúng tôi biết rằng thứ gì đó được căn chỉnh đến 1024 byte nhất thiết phải được căn chỉnh thành 1, 2, 4, 8 byte nhưng

 assert(alignof(b) == 32); // fail.

Vì vậy, chính xác hơn, "alignof" trả về sức mạnh lớn nhất của 2 để điều gì đó được căn chỉnh.

Ngoài ra alignof là một cách hay để biết trước yêu cầu căn chỉnh tối thiểu cho các kiểu dữ liệu cơ bản (nó có thể sẽ trả về 1 cho ký tự, 4 cho float, v.v.).

Vẫn hợp pháp:

alignas(alignof(float)) float SqDistance;

Một thứ gì đó có căn chỉnh là 16 sau đó sẽ được đặt trên địa chỉ có sẵn tiếp theo là bội số của 16 (có thể có một phần đệm ẩn từ địa chỉ được sử dụng gần đây nhất).


10
Không giống như sizeof, alignofchỉ có thể được áp dụng cho a type-id.
neverhoodboy

được đánh giá alignof()(và đối tác alignas()) tại thời điểm biên dịch, do đó không có chi phí thời gian chạy?
nonsensation

Không. Điều đó là không thể, trình biên dịch có thể làm điều đó như là tối ưu hóa trong một số ít trường hợp, nhưng nói chung nó sẽ không biết cách các địa chỉ bộ nhớ được căn chỉnh trước khi đánh giá 2 hàm. Chỉ cần nhìn vào assembly được tạo bởi ví dụ của tôi: goo.gl/ZbemBF
CoffeDeveloper

1
@Serthy Để làm rõ alignof một hằng số thời gian biên dịch. alignaskhông, và sẽ phải được hỗ trợ bởi việc bạn triển khai new(yêu cầu của tiêu chuẩn), hoặc bởi một trình phân bổ std tùy chỉnh .
Aidiakapi

Câu trả lời tốt, nhưng nó cần được xử lý structvà các thành viên của cấu trúc đó static. alignashóa ra còn phức tạp hơn nhiều __attribute__((aligned)), đặc biệt là dưới các trình biên dịch như Clang.
jww

11

Căn chỉnh không phải là đệm (mặc dù đệm đôi khi được giới thiệu để đáp ứng các yêu cầu về căn chỉnh). Nó là một thuộc tính intrisic của kiểu C ++. Để đặt nó trong standardese ( 3.11[basic.align])

Các kiểu đối tượng có các yêu cầu về căn chỉnh (3.9.1, 3.9.2) đặt ra các hạn chế đối với các địa chỉ mà tại đó đối tượng của kiểu đó có thể được cấp phát. Căn chỉnh là một giá trị số nguyên do triển khai xác định đại diện cho số byte giữa các địa chỉ liên tiếp mà tại đó một đối tượng nhất định có thể được cấp phát. Một kiểu đối tượng đặt ra yêu cầu căn chỉnh cho mọi đối tượng của kiểu đó; có thể yêu cầu căn chỉnh chặt chẽ hơn bằng cách sử dụng công cụ căn chỉnh (7.6.2).


1
Rất thú vị. Bạn có vui lòng đưa ra một số ví dụ? Có phải alignof (struct X) == sizeof (struct X) không? Tại sao không ?
Offirmo

1
@Offirmo không, ngoại trừ bởi concidence: struct X { char a; char b}có kích thước 2 và liên kết yêu cầu 1, trên các hệ thống lành mạnh (nó có thể được phân bổ tại bất kỳ địa chỉ vì một char có thể được phân bổ tại bất kỳ địa chỉ)
Cubbi

yêu cầu căn chỉnh của 1 ???? Ồ, hiểu rồi: Tôi đã nghĩ rằng sự liên kết luôn nằm trên ranh giới 32bits / 64bits "tự nhiên" nhưng dường như không phải. Điều đó giải thích những điều ... Vì vậy, trên các máy thông thường, kết quả alignof () sẽ luôn tối đa ở 4 (32bits) hoặc 8 (64bits) Tôi có đúng không?
Offirmo

@Offirmo "tự nhiên" alignof sẽ ra tối đa tại alignof(std::max_align_t), đó là 16trên linux của tôi (bất kể biên soạn -m32 hoặc -m64), nhưng bạn có thể làm cho nó nghiêm khắc hơn vớialignas
Cubbi

7

Mỗi loại có một yêu cầu căn chỉnh. Nói chung, điều này là để các biến của kiểu có thể được truy cập một cách hiệu quả mà không cần phải khiến CPU tạo ra nhiều hơn một quyền truy cập đọc / ghi để tiếp cận bất kỳ thành viên nhất định nào của kiểu dữ liệu. Hơn nữa, nó cũng đảm bảo sao chép hiệu quả toàn bộ biến. alignofsẽ trả về yêu cầu căn chỉnh cho kiểu đã cho.

alignasđược sử dụng để buộc một căn chỉnh trên một kiểu dữ liệu (miễn là nó không ít nghiêm ngặt hơn rằng alignofkiểu dữ liệu đã nói sẽ trả về)


3

Căn chỉnh là một thuộc tính liên quan đến địa chỉ bộ nhớ. Chúng ta có thể nói đơn giản hơn là nếu một địa chỉ X được liên kết với Z thì x là bội số của Z, đó là X = Zn + 0. Ở đây điều quan trọng là Z luôn là lũy thừa của 2.

Căn chỉnh là một thuộc tính của địa chỉ bộ nhớ, được biểu thị bằng môđun địa chỉ số có lũy thừa là 2. Ví dụ: địa chỉ 0x0001103F modulo 4 là 3. Địa chỉ đó được cho là được căn chỉnh thành 4n + 3, trong đó 4 cho biết sức mạnh đã chọn của 2. Sự liên kết của một địa chỉ phụ thuộc vào công suất được chọn của 2. Mô-đun địa chỉ tương tự 8 là 7. Một địa chỉ được cho là được căn chỉnh thành X nếu sự liên kết của nó là Xn + 0.

Câu lệnh trên được tìm thấy trên tham chiếu microsoft c ++.

Nếu một mục dữ liệu được lưu trữ trong bộ nhớ với một địa chỉ được căn chỉnh theo kích thước của nó, thì mục dữ liệu đó được cho là đã căn chỉnh tự nhiên , nếu không sẽ bị lệch. Ví dụ: nếu một biến số nguyên có kích thước 4 byte được lưu trữ trong một địa chỉ được căn chỉnh thành 4, thì chúng ta có thể nói rằng biến đó được căn chỉnh tự nhiên, đó là địa chỉ của biến đó phải là bội số của 4.

Các trình biên dịch luôn cố gắng tránh sai lệch. Đối với các kiểu dữ liệu đơn giản, các địa chỉ được chọn sao cho nó là bội số của kích thước của biến tính bằng byte. Trình biên dịch cũng phù hợp trong trường hợp cấu trúc để liên kết và truy cập tự nhiên. Ở đây cấu trúc sẽ được căn chỉnh theo kích thước tối đa của các mục dữ liệu khác nhau trong cấu trúc. Ví dụ:

    struct abc
   {
        int a;
        char b;
   };

Ở đây cấu trúc abc được căn chỉnh thành 4 là kích thước của thành viên int rõ ràng là lớn hơn 1 byte (kích thước của thành viên char).

alignas

Bộ chỉ định này được sử dụng để căn chỉnh các kiểu do người dùng xác định như cấu trúc, lớp, v.v. thành một giá trị cụ thể là lũy thừa của 2.

alignof

Đây là một loại toán tử để lấy giá trị mà cấu trúc hoặc loại lớp được căn chỉnh. ví dụ:

#include <iostream>
struct alignas(16) Bar
{
    int i; // 4 bytes
    int n; // 4 bytes
    short s; // 2 bytes
};
int main()
{
    std::cout << alignof(Bar) << std::endl; // output: 16
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.