C++ 用于内存对齐的自定义数据大小
基于硬件,每个数据类型都有一定的范围。例如,在32位计算机上,int的范围为-2147483648到2147483647 C++编译器“填充”对象内存以适应特定的大小。我很确定是2,4,8,16,32,64等等。这也可能取决于机器 我想手动对齐对象以满足填充要求。有没有办法:C++ 用于内存对齐的自定义数据大小,c++,memory-management,types,memory-alignment,C++,Memory Management,Types,Memory Alignment,基于硬件,每个数据类型都有一定的范围。例如,在32位计算机上,int的范围为-2147483648到2147483647 C++编译器“填充”对象内存以适应特定的大小。我很确定是2,4,8,16,32,64等等。这也可能取决于机器 我想手动对齐对象以满足填充要求。有没有办法: 确定程序在哪台机器上运行 确定填充大小 根据位大小设置自定义数据类型 我以前在java中使用过位集,但是我不熟悉C++。至于机器需求,我知道不同硬件集的程序通常在C++中编译,所以我想知道它是否可能。 示例-> /*g
- 确定程序在哪台机器上运行
- 确定填充大小
- 根据位大小设置自定义数据类型
我以前在java中使用过位集,但是我不熟悉C++。至于机器需求,我知道不同硬件集的程序通常在C++中编译,所以我想知道它是否可能。
示例->/*getHardwarePack size obviously doesn't exist, just here to explain. What I'm trying to get
here would be the minimum alignment size for the machine the program is running on*/
#define PACK_SIZE = getHardwarePackSize();
#define MONTHS = 12;
class date{
private:
//Pseudo code that represents making a custom type
customType monthType = MONTH/PACK_SIZE;
monthType.remainder = MONTH % PACK_SIZE;
monthType months = 12;
};
其思想是能够将每个变量放入最小位大小,并跟踪剩余的位数
从理论上讲,可以利用每一个未使用的位并提高内存效率。显然,这样做是行不通的,但是这个示例只是为了解释这个概念。这比您试图描述的要复杂得多,因为需要对对象和对象中的项进行对齐。例如,如果编译器决定一个整数项在
结构
或类
中是16字节,那么它很可能会决定“啊,我可以使用对齐的SSE指令来加载此数据,因为它是16字节对齐的”(或者类似于ARM、PowerPC等)。因此,如果您的代码中至少没有满足这种对齐方式,您将导致程序出错(崩溃或误读数据,具体取决于体系结构)
通常,编译器使用和给出的对齐方式对于编译器所针对的任何体系结构都是“正确的”。更改它通常会导致性能下降。当然,不总是这样,但在你摆弄它之前,你最好确切地知道你在做什么。并在之前/之后测量性能,彻底测试无任何损坏
填充通常仅限于下一个“最大类型的最小对齐”-例如,如果一个结构
仅包含int
和两个char
变量,它将被填充到4字节[在结构内部和末尾,视需要]。对于double
,填充到8个字节是为了确保,但是三个double
通常会占用8*3个字节,而无需进一步填充
此外,在编译期间确定正在(或将在)上执行的硬件可能比在运行时更好。在运行时,您的代码已经生成,并且代码已经加载。此时无法真正更改对象的偏移和对齐
如果您使用的是gcc或clang编译器,则可以使用,例如intx[4]\uuuu属性(aligned(32))
将创建一个与32字节对齐的16字节数组。这可以在结构或类内部完成,也可以在您使用的任何变量中完成。但这是一个编译时选项,不能在运行时使用
在C++11以后的版本中,还可以找到类型或变量与的对齐方式
请注意,它提供了类型所需的对齐方式,因此如果您执行了以下愚蠢的操作:
int x;
char buf[4 * sizeof(int)];
int *p = (int *)buf + 7;
std::cout << alignof(*p) << std::endl;
部分代码是从另一个项目中提取的,所以它可能不是最有史以来最新的C++代码,但它却帮我从头开始编写代码,与项目无关[
然后结果是:$ ./a.out
align A 8, size 32
align B 1, size 25
Run 0
ARR1 5091 sum=3218410893518
ARR2 5051 sum=3218410893518
Run 1
ARR1 3922 sum=3218410893518
ARR2 4258 sum=3218410893518
Run 2
ARR1 3898 sum=3218410893518
ARR2 4241 sum=3218410893518
Run 3
ARR1 3876 sum=3218410893518
ARR2 4184 sum=3218410893518
Run 4
ARR1 3875 sum=3218410893518
ARR2 4191 sum=3218410893518
Run 5
ARR1 3876 sum=3218410893518
ARR2 4186 sum=3218410893518
Run 6
ARR1 3875 sum=3218410893518
ARR2 4189 sum=3218410893518
Run 7
ARR1 3925 sum=3218410893518
ARR2 4229 sum=3218410893518
Run 8
ARR1 3884 sum=3218410893518
ARR2 4210 sum=3218410893518
Run 9
ARR1 3876 sum=3218410893518
ARR2 4186 sum=3218410893518
如您所见,使用arr1
对齐的代码大约需要3900个时钟周期,而使用arr2
对齐的代码大约需要4200个时钟周期。所以大约4000个循环中有300个循环,如果我的“薄荷醇算法”正确的话,大约7.5%
当然,就像许多不同的东西一样,它实际上取决于具体的情况,对象如何使用,缓存大小是多少,它到底是什么处理器,周围其他地方有多少其他代码和数据也在使用缓存空间。唯一确定的方法是对代码进行实验
[我运行了几次代码,虽然我并不总是得到相同的结果,但我总是得到类似的比例结果]这比您试图描述的要复杂得多,因为需要对对象和对象中的项进行对齐。例如,如果编译器决定一个整数项在
结构
或类
中是16字节,那么它很可能会决定“啊,我可以使用对齐的SSE指令来加载此数据,因为它是16字节对齐的”(或者类似于ARM、PowerPC等)。因此,如果您的代码中至少没有满足这种对齐方式,您将导致程序出错(崩溃或误读数据,具体取决于体系结构)
通常,编译器使用和给出的对齐方式对于编译器所针对的任何体系结构都是“正确的”。更改它通常会导致性能下降。当然,不总是这样,但在你摆弄它之前,你最好确切地知道你在做什么。并在之前/之后测量性能,彻底测试无任何损坏
填充通常仅限于下一个“最大类型的最小对齐”-例如,如果一个结构
仅包含int
和两个char
变量,它将被填充到4字节[在结构内部和末尾,视需要]。对于double
,填充到8个字节是为了确保,但是三个double
通常会占用8*3个字节,而无需进一步填充
此外,在编译期间确定正在(或将在)上执行的硬件可能比在运行时更好。在运行时,您的代码已经生成,并且代码已经加载。在这个点上,你不能真正改变物体的偏移和对齐
$ ./a.out
align A 8, size 32
align B 1, size 25
Run 0
ARR1 5091 sum=3218410893518
ARR2 5051 sum=3218410893518
Run 1
ARR1 3922 sum=3218410893518
ARR2 4258 sum=3218410893518
Run 2
ARR1 3898 sum=3218410893518
ARR2 4241 sum=3218410893518
Run 3
ARR1 3876 sum=3218410893518
ARR2 4184 sum=3218410893518
Run 4
ARR1 3875 sum=3218410893518
ARR2 4191 sum=3218410893518
Run 5
ARR1 3876 sum=3218410893518
ARR2 4186 sum=3218410893518
Run 6
ARR1 3875 sum=3218410893518
ARR2 4189 sum=3218410893518
Run 7
ARR1 3925 sum=3218410893518
ARR2 4229 sum=3218410893518
Run 8
ARR1 3884 sum=3218410893518
ARR2 4210 sum=3218410893518
Run 9
ARR1 3876 sum=3218410893518
ARR2 4186 sum=3218410893518