内存对齐和alignas
对齐(alignment)就是保证某个变量存储位置的首地址满足某个整数的倍数,通过设定特定的整数倍对齐位置可以提升处理器的性能,以及执行要求特定对齐的指令(例如SSE需要对齐为16bytes,AVX需要对齐到32bytes)。按照16对齐意味着,内存地址是16的整数倍。
alignas可以强制对齐的字节为2的n次方(n=1,2,3,4,5.....),下面通过代码来测试。
代码1:
#include <iostream>
#define CHECK_ALIGN(ptr,alignment) \
do{\
size_t status \
=reinterpret_cast<uintptr_t>(ptr) % alignment; \
if (status != 0) { \
std::cout << "It is not alligned! " << std::endl; \
} \
}while(0)
int main() {
long long a[4];
int b[8];
CHECK_ALIGN(a, 64);
CHECK_ALIGN(b, 1024);
std::cout << a << std::endl;
std::cout << b << std::endl;
return 0;
}
这种情况下,大概率是对不齐的,通常会打印(当然也可能出现对齐情况,但概率很低):
It is not alligned!
It is not alligned!
0x7fff77c99490
0x7fff77c994b0
如果我们加上alignas:
#include <iostream>
#define CHECK_ALIGN(ptr,alignment) \
do{\
size_t status \
=reinterpret_cast<uintptr_t>(ptr) % alignment; \
if (status != 0) { \
std::cout << "It is not alligned! " << std::endl; \
} \
}while(0)
int main() {
alignas(64) long long a[4];
alignas(1024) int b[4];
CHECK_ALIGN(a, 64);
CHECK_ALIGN(b, 1024);
std::cout << a << std::endl;
std::cout << b << std::endl;
return 0;
}
就可以保证绝对的对齐,打印结果:
0x7ffe38fdb7c0
0x7ffe38fdb400
alignof
了解了alignment的概念,我们来介绍关键字alignof,alignof返回的是对齐要求,如果没有通过alignas指定,则会返回满足对齐条件下的最小对齐长度,先给个例子:
#include <iostream>
#define CHECK_ALIGN(ptr,alignment) \
do{\
size_t status \
=reinterpret_cast<uintptr_t>(ptr) % alignment; \
if (status != 0) { \
std::cout << "It is not alligned! " << std::endl; \
} \
}while(0)
struct Foo {
char a;
int b;
int c;
long long d;
};
struct alignas(64) Foo1 {
char a;
int b;
int c;
long long d;
};
struct Foo2 {
char a;
long long d;
};
int main() {
alignas(64) long long a[4];
alignas(1024) int b[4];
CHECK_ALIGN(a, 64);
CHECK_ALIGN(b, 1024);
Foo f1;
alignas(128) Foo f2;
std::cout << alignof(Foo) << std::endl;
std::cout << alignof(Foo1) << std::endl;
std::cout << &f1 << std::endl;
std::cout << sizeof(f1) << std::endl;
std::cout << alignof(f1) << std::endl;
std::cout << &f2 << std::endl;
std::cout << sizeof(f2) << std::endl;
std::cout << alignof(f2) << std::endl;
std::cout << alignof(a) << std::endl;
std::cout << alignof(b) << std::endl;
std::cout << alignof(int) << std::endl;
std::cout << alignof(int*) << std::endl;
std::cout << alignof(Foo2) << std::endl;
return 0;
}
输出结果:
8
64
0x7ffcf0abc7e0
24
8
0x7ffcf0abc780
24
128
64
1024
4
8
8
其他比较容易理解,重点说一下结构体的例子,对于结构体必须要保证每个变量都要满足对齐要求,那么只要要求最严格的满足,其他也就满足了,所以会返回要求最严的变量的对齐要求,进一步解释,对于结构体Foo,我们看以下不同起始地址下,那么各个变量的起始地址为:
struct Foo { // 0 1 2 3 4 5 6 7 不同的起始地址
char a; // 4 5 6 7 8 9 10 11 a的起始地址
int b; // 8 9 10 11 12 13 14 15 b的起始地址
int c; // 12 13 14 15 16 17 18 19 c的起始地址
long long d; // 20 21 22 23 24 25 26 27 d的起始地址
};
再看下结构体Foo2:
struct Foo2 { // 0 起始地址
char a; // 8 a的起始地址
long long d; // 16 d的起始地址
};
注意这里,机构体本身有自动对齐的功能,后面接long long的话,char a位置会自动补为8字节。当然可以通过#pragma pack来控制对齐要求,例如:
#include<iostream>
#pragma pack(2)
struct Foo2 {
char a;
long long d;
};
int main() {
std::cout << sizeof(Foo2) << std::endl;
std::cout << alignof(Foo2) << std::endl;
return 0;
}
输出结果:
10
2