读书人

关于内存对齐的几个小问题

发布时间: 2013-04-20 19:43:01 作者: rapoo

关于内存对齐的几个问题
之前面试被问到内存对齐的相关知识,最近就大致查了查看了看
感觉自己算是一知半解了
我想问一下 之前的开发中基本没用过内存对齐的操作
关于内存对齐的设定一般在什么情况下使用呢?
对齐系数的设定 遵循什么原则?这是不是要看要具体看结构体的定义了?
[解决办法]

#pragma pack(push,1) //2,4,8,16,32
#pragma pack(pop)

[解决办法]
#include <stdio.h>
struct PD {
int c4;char c2;short c1;int c3;
} pd;
#pragma pack(push,1)
struct P1 {
int c4;char c2;short c1;int c3;
} p1;
#pragma pack(pop)
#pragma pack(push,2)
struct P2 {
int c4;char c2;short c1;int c3;
} p2;
#pragma pack(pop)
#pragma pack(push,4)
struct P4 {
int c4;char c2;short c1;int c3;
} p4;
#pragma pack(pop)
#pragma pack(push,8)
struct P8 {
int c4;char c2;short c1;int c3;
} p8;
#pragma pack(pop)
int main() {
printf("Default pack:%d\n",sizeof(pd));
printf(" 1 pack:%d\n",sizeof(p1));
printf(" 2 pack:%d\n",sizeof(p2));
printf(" 4 pack:%d\n",sizeof(p4));
printf(" 8 pack:%d\n",sizeof(p8));
return 0;
}

[解决办法]
引用:
引用:C/C++ code?12#pragma pack(push,1) //2,4,8,16,32#pragma pack(pop)
大神我觉得你就会回帖
对齐系数的设定 遵循什么原则?这是不是要看要具体看结构体的定义?
一般在什么级别的代码或软件上能体现出来?

如果结构体变量的内容要通过二进制文件读写或网络收发,pack 1
[解决办法]
一般网络数据传输的时候 会设置1字节对齐来减小一些传输量
[解决办法]
引用:
一般网络数据传输的时候 会设置1字节对齐来减小一些传输量

重要补丁:减小传输量不是重要的;重要的是收发双方如果不采用1字节对齐,可能根本无法传输结构化数据。
[解决办法]
这个程序太没效率了,在嵌入式系统中,这样是绝对不行的。
有些CPU取数据时是从地址是4的倍数取数据的,如果上面这样传入的地址不是4的倍数,那么很浪费CPU资源的。
所以我想面试你的这家公司应该是嵌入式相关的公司。

[解决办法]
引用:
引用:一般网络数据传输的时候 会设置1字节对齐来减小一些传输量
重要补丁:减小传输量不是重要的;重要的是收发双方如果不采用1字节对齐,可能根本无法传输结构化数据。


1字节对齐只是双方的一个约定

只要双方都是按照约定的格式来发送数据,难道2字节对齐的就发不了了?
[解决办法]
为啥就没人认真回答下呢?难道都不知道么?也怨不得8楼了
以32位系统为例,总线是32位的,也就说每次读取4个字节,如果你按4字节对齐,这样保证了读写1个指令就可以完成,如果没有,比如说起始地址是3,这样就需要读写2次,降低了效率
[解决办法]
引用:
引用:引用:一般网络数据传输的时候 会设置1字节对齐来减小一些传输量
重要补丁:减小传输量不是重要的;重要的是收发双方如果不采用1字节对齐,可能根本无法传输结构化数据。

1字节对齐只是双方的一个约定

只要双方都是按照约定的格式来发送数据,难道2字节对齐的就发不了了?
……

用不同的CPU或操作系统或编译环境,就算都设定2字节对齐,实际结果也可能各不相同。


[解决办法]


[解决办法]
引用:
引用:引用:引用:引用:引用:一般网络数据传输的时候 会设置1字节对齐来减小一些传输量
重要补丁:减小传输量不是重要的;重要的是收发双方如果不采用……


这个结构如果是2字节对齐,网络传输会有问题?会有什么问题呢?
[解决办法]
推荐使用WinHex软件查看硬盘或文件或内存中的原始字节内容。

[解决办法]
lz,你看17楼的回复就行了

他们都在瞎侃呢,不要跟着他们的思路走
[解决办法]
引用:
引用:引用:引用:引用:引用:一般网络数据传输的时候 会设置1字节对齐来减小一些传输量
重要补丁:减小传输量不是重要的;重要的是收发双方如果不采用……




网络传输如果不考虑网络上的报文长度,只要两边对齐设置成一样就不会有问题,当然大小端必须转,不过你一个字节对齐,一样要转
[解决办法]
引用:
引用:引用:引用:引用:引用:引用:一般网络数据传输的时候 会设置1字节对齐来减小一些传输量
重要补丁:……

那如果一边是Apple II,一边是Windows 64bit呢?
[解决办法]
引用:
引用:引用:引用:引用:引用:引用:引用:一般网络数据传输的时候 会设……

字节对齐跟这些有什么关系?假定你结构里面有long,假定两边long长度都不一样,单字节对齐你能用这个结构体传数据?跟你传输用几个字节对齐有P关系啊?
[解决办法]
传输的数据是字节流,大小端和怎么读是上层逻辑的问题,直接传结构体内存是一种很烂的实现
32位系统 你读8位,实际上读了32位
如果你有16的数据,在2个32位内存中间,将要读2次,对齐了则不会有这种情况
[解决办法]
编译器会自己调整这个行为, 如果你要改请先想清楚.
这个是bug的源泉, 不建议修改这个. 如果要压缩数据请用专业的压缩算法.

给你讲个例子, doc是微软自己的数据结构里面就是各种改对齐和位运算什么的. docx则是明文xml+zip压缩. 结果就是docx不但比doc逻辑简单,而且体积还小很多.
[解决办法]
现在基本不用考虑对齐问题,除非想弄成二进制格式,用于通信协议
[解决办法]
网络传输是按照双方约定的协议执行的,看看tcp/ip协议栈,什么一字节对齐,胡扯
[解决办法]
字节对齐不是1字节时,不同编译器或不同平台生成的结构对应的内存布局是不一定相同的,所以不同编译器或不同平台无法保证将整个结构所占内存用send发送recv接收后得到逻辑一致的数据。
[解决办法]
我来说两句关于字节对齐的问题吧:
1.结构体对齐方式是由编译器决定的,比如VS2008默认对齐是4字节,当然这个可以在编译器里面设置,也可以用program来设置,那么如果我们编程模块要将结构体给外部使用时,如果对方采用的是相同字节的对齐方式那么就不会出现内存访问的异常,如果对方调整了编译器对齐方式(或者采用其他的编译器),那么这个结构体所表示的内存布局就不一样了,访问的时候就会出现问题(网络同理,下面会说网络上的问题)。
比如结构体A VS2008数据结构布局如下:
struct A
{
char a;
int b;
}
假设A现在的地址为0x4100,那么A.a的地址也是0x4100,A.b的地址为0x4104,这个时候就是对齐方式引起的问题,当然你也可以强制1字节对齐方式,那么A.b的地址为0x4101,这就是网络传输上为什么要采用1字节对齐的一个原因,当然网络上用1直接对齐也是为了节约网络流量,比如上面的结构体采用4字节对齐那么就浪费了3个字节的空间,而且你没办法保证网络接收端也同样采用的是跟你一样的对齐方式,那么一般在网络协议头文件上都会强制采用如下命令来保证只要使用了协议头定义的结构体一定是采用1字节对齐的方式来处理网络数据的。
#pragma pack(push,1)
struct A{}; ....
#pragma pack(pop)

2.字节对齐在一些嵌入式运行环境上还是比较注重的,寻址效率是个问题,比如指令都是采用4字节寻址方式的。

3.合理的使用内存布局对程序空间和效率也是有提升的。
比如以下两个结构体的内存空间大小问题:
struct A
{
char a;
int b;
char c;
long d;
byte e;
}

struct B
{
char a;
char c;
byte e;
int b;
long d;
}

结构体A和B在不考虑内存对齐上其实空间大小是一样的,但是如果人为没有设置强制采用1字节对齐,那么这两个结构体的大小有很大的差别,采用4字节对齐方式,结构体A的大小为20字节,而B的大小为12,那么我们在程序的时候就得注意下这个内存布局的问题了。
[解决办法]
我写了linux kernel 的memcpy, memmove, glibc 的 memcpy,
1)内存对齐从代码的角度来看,是为了最大化满足前端取指需求,但是从Intel Sandybridge引入 L0 cache之后
我们更关注code size.

2) 从数据角度看避免产生cross cache line 和 cache bank conflict,所以我们关注16bytes 或者32bytes对齐避免跨越64bytes的cache line.

Thanks
Ling


引用:
看来CSDN上全是菜鸟,神马专家都是假的。

你看看memcpy的源代码怎么的写的,就知道为什么要内存对齐了。


------解决方案--------------------


????

[解决办法]
引用:
引用:一般网络数据传输的时候 会设置1字节对齐来减小一些传输量

我是菜鸟,网络传输的字节对齐与内存对齐是一个概念上的东西吗?

网络传输的字节对齐是相对网络传输协议而言的吧。

通常上讲的内存对齐都是指数据的内存分布吧?

为什么采用内存对齐?当然是想要通过减少读取内存的次数来提高计算机性能。这个在计算机体系……


但是采用1字节对齐时,结构所占的内存大小是会减到最小,这一点是没有疑问的吧?
[解决办法]
汗一下,关于内存对齐解释如下:
1)在嵌入系统中,如RISC 32系统中,32为对齐访问效率最高:因为RISC每次访问固定是32位,对于低于32位的操作会隐含与操作 ---- 仅仅因为效能原因
2)在一些早期的CPU,如早期的MC68K,对于数据地址有对齐要求,奇地址访问会产生异常错误;
3)在系统内核程序,多数因为分页机制会要求对齐,如常见的4K对齐
这只是针对嵌入系统,在PC上除驱动程序编写外,应用层内存对齐多是指 struct的对齐,如常见的32位和 64 位对齐,这仅仅是出于效能要求,及类似嵌入系统的1)点;另外一些应用程序因为特殊原因所要求的对齐(如某些 VM会使用地址的低位保存一些特殊属性而要求的对齐) 我们可以先不去管他
[解决办法]
引用:
引用:引用:引用:引用:引用:引用:一般网络数据传输的时候 会设置1字节对齐来减小一些传输量
重要补丁:……

在基于流式的应用,尤其考虑是不同平台,数据共享(传输)不可能使用数据结构,这不仅仅是对齐问题和减少数据传输的原因:
1)大小端问题(big-endian/little-endian)
2)不同编译器针对类似struct的处理不同
[解决办法]
引用:
引用:引用:引用:引用:引用:引用:引用:一般网络数据传输……


好吧 1字节对齐网络传输的问题是我引起的,
是我说的不清楚

lz问“关于内存对齐的设定一般在什么情况下使用”
我理解的是,不使用默认字节对齐,而采用手动设置的情况下,在哪些场景下会用到

我说,“在网络传输中,如果双方约定1字节对齐,可以减少网络数据的传输量”
指在你直接对结构体进行memcpy的情况下,可以减少字节对齐造成的浪费
如果说你使用的语言没有结构体,或者说你一直是一个字段一个字段的copy的,
甚至是一个字节一个字节的copy的
那也就跟字节对齐无关了

如果说,不同平台的数据传输,比如c++与java之间的网络通信,存在大小端的问题,这个时候你该转换还是得转换的,这些特殊情况,不是你设置几字节对齐就可以解决的

但是对于,如果双发约定字节对齐是可以解决问题的情况下,那么双方都设置成1字节对齐,可以减少浪费


[解决办法]
引用:
引用:引用:引用:引用:引用:引用:引用:引用 9 楼 zilaish……


更正一些问题:我们所说的内存对齐多数特指底层操作,即使是VM这样的程序,也是针对VM的实现。因此,特别说明:
1)不同平台间,是指不同的硬件设备间。大小端在计算机教材中是源自不同的两派CPU(Intel和摩托罗拉)之间,本人才疏学浅,第一次听说大小端是指C++与Java这样的情况 --- 先请从概念上明确什么是大小端;因此,针对网络或流我们同样理解为不同设备间;
2)在PC系统中,字节对齐结构是会取得最小的size;但PC中所谓32位对齐和64位对齐多数是出于效能原因
某些系统中,如MC68k的系统,一个整型数如果没有对齐会产生奇地址错;一些更古旧的CPU(印象应该是6502还是6502C)甚至会有页对齐之说-----但这都是古董级的东西,大可不需要去理会
3)对齐不仅仅是在编译器设定的问题,我们自己在写VM时,因为每个Atom会保存特殊属性,因此一定存在对齐问题,但因为跨平台的缘故,这些对齐实际是在代码中实现,与编译器设定没有任何关系(出于效率的RISC内存管理也是这样 ----- 不会有人傻到在编译器去设定然后还指望跨平台)
4)"但是对于,如果双发约定字节对齐是可以解决问题的情况下,那么双方都设置成1字节对齐,可以减少浪费"这样说法是没有错的,但如果是传输浮点数、双精度浮点数呢?因此,对于基于Stream的应用,多数会在SPEC中作约定,不是统一为一个字节这样简单
[解决办法]


32位CPU,字节对其后,一次就能获取到一个4字节的数,
若字节不对其,如:0x00000002~0x00000006存储一个4字节的数字,则CPU需要读取2次才能得到该数字。
[解决办法]
引用:

之前面试被问到内存对齐的相关知识,最近就大致查了查看了看
感觉自己算是一知半解了
我想问一下 之前的开发中基本没用过内存对齐的操作
关于内存对齐的设定一般在什么情况下使用呢?
对齐系数的设定 遵循什么原则?这是不是要看要具体看结构体的定义了?


首先,现代的CPU,根据32位或64位差别,其对齐方式不同。下面都按照64位对齐来说
如果访问没有对齐的数据,则有可能在一级缓存里的数据,被放在两个缓存上。这个时候,CPU读取数据就要多花费10+时钟周期才能读到数据。因此,效率可能因此而降低。如果是大量数据要密集计算(如图形,不是数据库这种),这个降低的客观了。
部分指令为了提高效率,要求数据是按照64位对齐的。不对齐则CPU触发异常中断,操作系统就认为你的程序异常了。
有的CPU为了提高效率,干脆所有32位/64位数据都要求按照对应的位对齐。如ARM和安腾

这是情况之一。一般编译器会根据你的程序是32位或64位,按照对应的位做默认对齐处理。这就是你问的一般原则。

但这种默认情况不是任何地方都试用。对于网络传输,或者规定文件格式来说,占用空间是第一紧要的。这个时候可能就需要一字节对齐了。
[解决办法]
引用:
编译器会自己调整这个行为, 如果你要改请先想清楚.
这个是bug的源泉, 不建议修改这个. 如果要压缩数据请用专业的压缩算法.

给你讲个例子, doc是微软自己的数据结构里面就是各种改对齐和位运算什么的. docx则是明文xml+zip压缩. 结果就是docx不但比doc逻辑简单,而且体积还小很多.


但解析docx比解析doc慢。用过一些开源的XML解析代码,漫得我吐血。4M多的XML内容(不在一个文件里),解析要10来秒。OMG!后来忍不住,把XML好多特性去掉(毕竟我没有用),自己写了一个简易的XML解析代码,才把问题解决了。
[解决办法]
结构体中内存对齐遵循的原则:
1. 如果代码中没有用#pragma pack说明的,则以结构体中最长字节对齐;
2. 如果代码中有用#pragma pack来标定,则以#pragma pack设定的来对齐
3. 如果#pragma pack设定的值比结构体中字节长度最大的成员还大,则以机构体中最大的那个为准。
即:取两者中最小的那个为对齐标准。
[解决办法]
引用:
Quote: 引用:

编译器会自己调整这个行为, 如果你要改请先想清楚.
这个是bug的源泉, 不建议修改这个. 如果要压缩数据请用专业的压缩算法.

给你讲个例子, doc是微软自己的数据结构里面就是各种改对齐和位运算什么的. docx则是明文xml+zip压缩. 结果就是docx不但比doc逻辑简单,而且体积还小很多.


但解析docx比解析doc慢。用过一些开源的XML解析代码,漫得我吐血。4M多的XML内容(不在一个文件里),解析要10来秒。OMG!后来忍不住,把XML好多特性去掉(毕竟我没有用),自己写了一个简易的XML解析代码,才把问题解决了。


不是解析xml慢, 只是你用的那个东西慢, 速度快的也有,比如你写的那个. 这不能怪到xml上
[解决办法]
引用:
Quote: 引用:

看来CSDN上全是菜鸟,神马专家都是假的。

你看看memcpy的源代码怎么的写的,就知道为什么要内存对齐了。



void * __cdecl memcpy (
void * dst,
const void * src,
size_t count
)
{
void * ret = dst;
#if defined (_M_IA64)
{
__declspec(dllimport)
void RtlCopyMemory( void *, const void *, size_t count );
RtlCopyMemory( dst, src, count );
}
#else /* defined (_M_IA64) */
/*
* copy from lower addresses to higher addresses
*/
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;


src = (char *)src + 1;
}
#endif /* defined (_M_IA64) */
return(ret);
}


我看不太懂- -


在windows环境下32位汇编语言程序中有提到过,那个mencopy函数里面不是通过dll调用Rtlmemcopy函数,DLL调用就是为了内存对齐.

我理解是这样
[解决办法]
内存对齐重点不是影响速度 如果不遵守内存对齐规则送信内容和受信内容可能就会不一致。
如果送收信双发都采用结构体的方式 问题应该不大
如果采用字符数组的方式接收 然后按照报文中的字段依次取得 很可能取得的数据和送信的数据不一致。

[解决办法]
内存对齐只要在定义结构体的时候把数据按类型从小到大排列就没问题了。平时也就用到这些,具体的理论看了就头痛。

读书人网 >C++

热点推荐