2011-10-17 27 views
10

我们的标题在我们的大多数结构(用于网络和文件I/O)使用#pragma pack(1)。我知道它会将结构的对齐从8个字节的默认值更改为1个字节的对齐。使用杂注包(1)时是否存在性能问题?

假设一切都在32位Linux(也许是太的Windows)上运行,有没有即来源于此包装对准任何性能影响?

我不关心移植的库,但多与文件和网络I/O不同的#pragma pack和性能问题的兼容性。

+0

我甚至不知道GCC支持'#pragma pack'。不是我现在要使用它。 –

+0

@larsmans是的,由于Windows:http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas。html – Nicolas

回答

11

当字对齐的内存地址发生时,内存访问速度最快。最简单的例子是下面的结构(其@Didier也使用):

struct sample { 
    char a; 
    int b; 
}; 

默认情况下,GCC插入填充,所以是在偏移0,并且b在偏移4(字对齐)。没有填充,b不是字对齐的,并且访问速度较慢。

慢多少?

  • 对于32位的x86,根据Intel 64 and IA32 Architectures Software Developer's Manual
    处理器需要两个存储器 访问使未对齐的内存访问;对齐的访问只需要一个内存访问。跨越4字节边界的字或双字操作数或跨越8字节边界的双字操作数被认为是未对齐的,并且 需要两个单独的存储器总线周期来访问。
    与大多数性能问题一样,您必须对您的应用程序进行基准测试,以查看实际中存在多少问题。
  • 根据Wikipedia,x86扩展如SSE2 需要字对齐。
  • 许多其他架构需要字对齐(如果数据结构不是字对齐的,将会产生SIGBUS错误)。

关于可移植性:我假设你正在使用#pragma pack(1)这样就可以发送跨线及到磁盘结构,无需担心不同的编译器或平台包装结构不同。这是有效的,但是,有几个问题需要记住:

  • 这没有任何处理大端序与小端序问题。您可以通过调用htons函数族来处理这些函数,这些函数在任何ints,unsigned等等的结构中。
  • 根据我的经验,在应用程序代码中使用打包的可序列化结构并不是很有趣。在不破坏向后兼容性的情况下,它们很难修改和扩展,并且如前所述,存在性能损失。考虑将你的打包的,可序列化的结构体的内容转换为等效的非打包的,可扩展的结构体进行处理,或考虑使用像Protocol Buffers(它有C bindings)这样的完整序列化库。
+1

+1表示出色的答案,并指出某些非x86体系结构实际上*需要*针对某些数据类型进行适当对齐。 –

+0

Endinaness实际上并没有处理,但它是“OK”,因为我们的整个后台都是Linux驱动的。我会实际运行一个基准测试,并且可能会在这里回报。谢谢你的答案。 – Nicolas

3

当你声明一个结构时,大多数编译器会在成员之间插入填充字节,以确保它们与内存中的适当地址对齐(通常填充字节是该类型大小的倍数)。这使编译器能够对这些成员进行优化访问。

#pragma pack(1)指示编译器包与特定对准结构的成员。这里的1告诉编译器不要在成员之间插入任何填充。

所以是有一定的性能损失,因为你强制编译器做一些超出它自然会为性能optimization.Also做,一些平台的需求,该物体在特定边界和使用unalighed对齐结构可能会给你分段错误。

理想的情况下,最好是避免更改默认的自然对齐规则。但是,如果根本无法避免'包装'指令(就像你的情况那样),那么在定义需要紧密包装的结构之后,必须恢复原包装方案。

对于如:

//push current alignment rules to internal stack and force 1-byte alignment boundary 
#pragma pack(push,1) 

/* definition of structures that require tight packing go in here */ 

//restore original alignment rules from stack  
#pragma pack(pop) 
+1

或者更好,使用gcc的本地['aligned'属性](http://gcc.gnu.org/onlinedocs/gcc-3.2.3/gcc/Type-Attributes.html)来标记当前结构。 –

2

这取决于底层架构和它的方式处理未对齐的地址。尽管性能价格昂贵,但其他体系结构(如ARM)可能会调用对齐错误(SIGBUS),或者甚至将错位地址“轮回”到最近的边界,在这种情况下,您的代码将会以合适的方式处理未对齐的地址以可怕的方式失败。

底线是,只有在确定底层体系结构将处理未对齐的地址并且网络I/O的成本高于处理成本时才打包。

+0

这确实是x86。 – Nicolas

+0

你的建议是什么?如果要在ARM和X86机器之间发送数据,我应该使用什么样的包格式? – Benny

6

是的。绝对有。

举例来说,如果你定义一个结构:

struct dumb { 
    char c; 
    int i; 
}; 

那么每当你访问我时,CPU减慢成员,因为32位值我不是本地人,对齐方式访问。为了简单起见,假设CPU必须从存储器获得3个字节,然后从下一个位置获得另外1个字节,以将值从存储器传输到CPU寄存器。

0

从技术上讲,是的,它会影响性能,但仅限于内部处理。如果您需要为网络/文件IO打包的结构,则打包的需求和内部处理之间会有一个平衡。通过内部处理,我的意思是,你在IO之间的数据上做的工作。如果你做的处理很少,你不会在性能方面损失太多。否则,您可能希望在正确对齐的结构上进行内部处理,并且只在执行IO时“打包”结果。或者您可以切换为仅使用默认的对齐结构,但您需要确保每个人都以相同的方式对齐它们(网络和文件客户端)。

0

某些机器码指令可以在32位或64位(甚至更多)上运行,但希望数据在内存地址上对齐。如果不是,他们必须在内存上执行多个读/写操作才能执行任务。 该性能如何影响很大程度上取决于您对数据所做的操作。如果您构建大型结构数组并对其执行大量计算,则可能会变得很大。但是,如果您只是将数据存储一次,然后再将其转换为字节流,那么它可能几乎不可知。

相关问题