数据对齐总结

必须注意:对齐是多少字节对齐不是多少位对齐。


对齐原因:

如上图片,内存一般是四个单位一列,CPU在读取内存数据的时候,通过总线并行读取每个单位的数据。对于CPU 32bit的寄存器而言。

  • 0-7bit是来自于内存芯片0的位
  • 8-15bit是来自内存芯片1的位
  • 16-23bit是来自芯片2
  • 24-31来自芯片3

而内存结构是,内存地址一个单位也就是基本单位是1字节,也就是8位二进制数。

在读取数据的时候实际上是0-7位从芯片0读取,8到15位从芯片2读取,以此类推。

所以读取一个4字节数据,是四个芯片同时往寄存器里面读。

如下图:

左边是地址偏移。用于总线高效传输地址,芯片上的数字是内存地址,每个内存地址需要读取几位,就在本芯片上向后读取几位。注意这里说的是物理地址,而不是虚拟地址,物理地址不是线性的,是混乱的。

如果读取4位int,就会从芯片0读取8bits填充寄存器的0--7bit,芯片1读取8bits填充8--15bits,以此类推。

如果数据没有对齐,要从地址1读取4字节,读取进去之后,需要CPU循环左移8bits来达到正确。但是CPU在读取数据的是后需要向内存发送地址,如果没有对齐,发送的地址偏移(左边的地址偏移)量是不同的,1、2、3地址偏移为0,而0芯片接受的地址偏移却为1,这样一来,发送地址就无法使用1根总线来完成了,而是需要4个总线,每个地址总线需要32个CPU引脚,而32bitCPU总共才400多个引脚,这样的设计,增加硬件设计复杂性,是不必要的。如果不改变设计复杂性,访问未对齐数据就需要两次访问才能得到数据。

例如:

访问地址是1、2、3、4的4字节数据,先要读取地址0-3的数据,在读取4--7的数据,再把1--4的数据取出来放入目标寄存器。所以不对其就要多次访问内存。某些CPU比如Power PC直接禁止访问不对其数据。

总结一下为什么需要数据对齐。为了增加CPU读取数据的带宽,内存系统通常都采用并 行结构使得可以并行传输数据。这样的并行结构使得访问对齐的数据速度快,但是若 要使访问不对奇的数据也一样快会使CPU与内存系统的接口变得更复杂,而这是划不来 的。经过权衡之后,最终的结果是:访问对齐的数据速度快,访问不对奇的数据速度 慢(需要2次访问)或干脆禁止访问不对奇数据。

对齐就是地址必须能够整除一个整数,这个整数就是对齐参数。对齐的合法取值范围是:1、2、4、8、16、。。。、8192

对于基本 类型(不包括结构体,联合体和数组)不同的数据类型的对齐模数通常等于这个数据 类型的大小,比如char的对齐模数为1,short的对齐模 数为2,int的对齐模数为4,float的对齐模数是 4,double的对齐模数为8

当然,这个对齐模数每一个编译器可能有差 异,并且可以设置。在Visual C++中可以通过/Zp选项 获#pragma设置。

综合起来,基本类型的对齐模数是这个类型的大小和 设置的对齐限制的较小者。对于结构体,联合体和数组,对齐模数是它的成员的对齐 模数的最大值。

结构体对齐:

__declspec( align(#) )和#pragma pack( n )

一、#pragma pack(n)

#pragma pack ( 16 )

typedef struct {

int a;

char b;

double c;

}test;

结构体中的数据成员,除了第一个是始终放在最开始的地方,其它数据成员的地址必须是它本身大小或对齐参数两者中较小的一个的倍数。

结构体的数据成员的地址必须是本身大小和对齐参数中较小的那一个。

简单类型组成的结构体,大小由每个成员变量的地址决定。按照定义顺序,分别求出地址开始的地方,从地址0开始,每个变量都采取min(对齐参数,sizeof(type))来确定起始地址是多少的倍数,然后填满该数据,最后求出总大小。在pack大等于2的时候,最终大小要是2的倍数,需要向上取到为2的倍数。pack为1则不需要。

对于含有结构体的结构体,方法同上,结构体变量的对其参数为min(pack对齐参数,结构体成员最大元素大小)。

__declspec( align(#) )

当一个变量或结构体同时受__declspec( align(#) )和#pragma pack( n )影响时,前者的优先级高。

每个成员的地址是前者n的倍数或者后者m的倍数或者是成员大小的倍数。即:

min(n,m,sizeof(成员变量类型));

结构体的最终大小要么是__declspec( align(m) )中m的倍数,要么是结构体中最大类型大小的倍数,去最大。即:

max(m,sizeof(最大类型大小));

结构体中有结构体的例子:

#include <stdio.h>

#include "stdafx.h"
#include <stdlib.h>
//using namespace std;

#pragma pack( push, 4 )

__declspec( align(32) )struct D
{
    int i1;
    double d1;
    int i2;
    int i3;
};

__declspec( align(16) ) struct E
{
     int i1;
     D m_d;
     int i2;
};

int main()
{
    cout << "sizeof(int) = "<<sizeof(int) << endl;
    cout << "sizeof(char) = " << sizeof(char) << endl;
    cout << "sizeof(double) = " << sizeof(double) << endl;
    cout << sizeof(D) << endl;
    cout << sizeof(E) << endl;
    system("PAUSE");
    return 0;
}

最后运行的结果是sizeof(E)为96,为何会是这个结果呢?

对于结构体E,第一个元素为int类型,所以占据[0~3]地址单元。

第二个元素是一个结构体,该结构体由于受上面__declspec( align(32) )的影响,优先级高,所以起始地址是32的倍数,而且大小为32B,从而应该放置在[32~63]单元处。

最后一个是int类型的变量,大小为4,所以应该是4的倍数,地址为[64~67]。

故结构体E的大小应该是从[0~67],占据68B,而由于前面还有限制__declspec( align(16) ),同时成员变量的最大偏移是sizeof(D)=32,所以我们最后这个结构体的大小应该是他们中最大值的倍数,也就是32的倍数,68向上取32的倍数应该是96.故结果为96.

注意:_declspec ( align() )的一个特点是,它仅仅规定了数据对齐的位置,而没有规定数据实际占用的内存长度,

当指定的数据被放置在确定的位置之后,其后的数据填充仍然是按照#pragma pack规定的方式填充的。

特别注意:不要忘记__declspec ( align(n) )会让结构体的首地址n字节对齐。

时间: 2024-12-24 22:18:58

数据对齐总结的相关文章

gpu显存(全局内存)在使用时数据对齐的问题

全局存储器,即普通的显存,整个网格中的任意线程都能读写全局存储器的任意位置. 存取延时为400-600 clock cycles  非常容易成为性能瓶颈. 访问显存时,读取和存储必须对齐,宽度为4Byte.如果没有正确的对齐,读写将被编译器拆分为多次操作,降低访存性能. 多个warp的读写操作如果能够满足合并访问,则多次访存操作会被合并成一次完成.合并访问的条件,1.0和1.1的设备要求较严格,1.2及更高能力的设备上放宽了合并访问的条件. 1.2及其更高能力的设备支持对8 bit.16 bit

erlang shell表格数据对齐

最近在erlang shell做一些测试,为了让测试结果数据显得更直观,想对齐需要打印的数据,做成像表格一样的效果. 开始的想法是在数据中插入tab.当然,erlang也有对tab的支持,但实际效果不理想. 对tab的支持如下: 1> io:format("No1~sNo2~n1~s2~n", ["\t", "\t"]). No1        No2 1             2 ok 但数据的长度超过8个字符就有问题,那有没有一种可以

sizeof数据对齐问题

#include <iostream> using namespace std; struct A1 { int a; static int b; }; struct A2 { int a; char c; }; struct A3 { float a; char c; }; struct A4 { float a; int b; char c; }; struct A5 { double d; float a; int b; char c; }; void main() { cout<

Ultra Edit中的数据对齐

有时会用到Ultra Edit的数据对齐功能.比如,要求64个符号一组,从低位开始对齐.这时,如果数据长度不是一行长度的整数, 就会产生高位对齐.低位不足的问题.为了调整,往往需要逐行调整,很不方便. 有一个优化的方法,先计算数据长度除以"行长度"的余数,然后在数据首位前补足零,以保证整除.之后自动对齐,没有低位不足 的问题.

gcc 数据对齐之:总结篇.

通过上面的分析,总结结构体对齐规则如下: 1.数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行.2.结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行.3.结合1.2颗推断:当#pragma p

内存中的数据对齐

数据对齐,是指数据所在的内存地址必须是该数据长度的整数倍.DWORD数据的内存起始地址能被4除尽,WORD数据的内存起始地址能被2除尽.x86 CPU能直接访问对齐的数据,当它试图访问一个未对齐的数据时,会在内部进行一系列的调整.这些调整对于程序来说是透明的,但是会降低运行速度,所以编译器在编译程序时会尽量保证数据对齐.同样一段代码,我们来看看用VC.Dev C++和LCC这3个不同的编译器编译出来的程序的执行结果: 这是用VC编译后的执行结果: 变量在内存中的顺序:b(1字节)-a(4字节)-

C/C++数据对齐汇总

C/C++数据对齐汇总 这里用两句话总结数据对齐的原则: (1)对于n字节的元素(n=2,4,8,...),它的首地址能被n整除,才干获得最好的性能: (2)如果len为结构体中长度最长的变量,size为CPU(处理器)的位数,对齐规则: 若len < size,则以len为单位对齐 若len >= size,则以size为单位对齐 这里不考虑指定对齐方式的情况. 測试 struct B{ bool i; int j; bool k; }; struct A{ int j; bool i; b

数据对齐与代码优化笔记

1. 一直以来对数据对齐都不明白,直到看了这篇文章后才能说是有点感觉,结合代码将理解写下来,以备将来回头加深理解或者是修正错误的认知. http://www.searchtb.com/2013/04/performance_optimization_tips.html 代码如下,运行的环境是64位的linux机器,内核3.10,gcc 4.8.2 /*************************************************************************

gcc数据对齐之: howto 2.

原文链接:http://www.catb.org/esr/structure-packing/ 谁应阅读本文 本文探讨如何通过手工重新打包C结构体声明,来减小内存空间占用.你需要掌握基本的C语言知识,以理解本文所讲述的内容. 如果你在内存容量受限的嵌入式系统中写程序,或者编写操作系统内核代码,就有必要了解这项技术.如果数据集巨大,应用时常逼近内存极限,这项技术会有所帮助.倘若你非常非常关心如何最大限度地减少处理器缓存段(cache-line)未命中情况的发生,这项技术也有所裨益. 最后,理解这项