为什么要内存对齐?
直观地看:CPU一次只能读取出4n,4n+1,4n+2,4n+3的信息
如果把一个short类型变量(占两个字节)存在如图红色位置,那么我就要读两次
“如果变量在0x00000003地址上的话则要访问三次内存,第一次为char,第二次为short,第三次为char”我不明白,这三次的地址分别是多少,如何计算确定的?
我的理解如上图所示。(CPU读取内存是按字节、字、双字来的)
#include <stdio.h>
main()
{
struct A {
int a;
char b;
short c;
};
struct B {
char b;
int a;
short c;
};
#pragma pack (2) /*指定按2字节对齐*/
struct C {
char b;
int a;
short c;
};
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
#pragma pack (1) /*指定按1字节对齐*/
struct D {
char b;
int a;
short c;
};
#pragma pack ()/*取消指定对齐,恢复缺省对齐*/
int s1=sizeof(struct A);
int s2=sizeof(struct B);
int s3=sizeof(struct C);
int s4=sizeof(struct D);
printf("%d\n",s1); //8
printf("%d\n",s2); //12
printf("%d\n",s3); //8
printf("%d\n",s4); //7
}
有三个概念:自然对齐值 指定对齐值 有效对齐值
自然对齐值:
char int short的自然对齐值是1,4,2
结构体的自然对齐值是成员中最大的对齐方式
要求:地址%自然对齐=0
指定对齐值:
可以用__attribute__
修改或者pragma pack (n)
有效对齐值:指定对齐值与自身对齐值中较小者,实际中用的
假设结构体从地址空间0x0000开始放,每个变量都要满足有效对齐值
无指定情况下:char int short的有效对齐值是1,4,2,结构体是4
struct A {
int a;
char b;
short c;
}
a:0-4 0%4=0
b:4-5 4%1=0
c:6-8 5%2!=0 因此填充一格,跳到6
最后发现消耗4+1+1+2=8,满足结构体的自然对齐值4,最终是8
无指定情况下:char int short的有效对齐值是1,4,2,结构体是4
struct B {
char b;
int a;
short c;
};
b: 0-1 0%1=0
a: 4-8 1%4!=0 填充三格
c: 8-10 8%2=0
最后发现消耗10,不满足结构体的自然对齐值4,填补两个,最终是12
有效对齐值是2
#pragma pack (2) /*指定按2字节对齐*/
struct C {
char b;
int a;
short c;
};
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
b: 0-1 0%2=0
a: 2-6 1%2!=0,填充1个
c: 6-8 6%2=0
最后消耗8个,满足结构体的有效对齐值2,最终是8
有效对齐值是1
#pragma pack (1) /*指定按1字节对齐*/
struct D {
char b;
int a;
short c;
};
#pragma pack ()/*取消指定对齐,恢复缺省对齐*/
b: 0-1 0%1=0
a: 1-5 1%1=0
c: 5-7 5%1=0
最后消耗7个,满足结构体的有效对齐值1,最终是7
最后从评论区补充:
32bit机子这是按照4字节对齐的,64bit机子是按照8字节对齐的,最终对齐字节是CPU、编译器和有效对齐字节中最小的值
#pragma pack(n)和__attribute__((aligned(m)))的区别
应该如xiaoxiaosunzhao所说
我们避免数据不对齐主要为了避免cross cache line,page boundary 和 cache bank conflic,不同的cpu有着不同的着重点。
VC调试(TC或BC用TD调试)时按Alt+8、Alt+6和Alt+5,打开汇编窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应内存和寄存器变化,这样过一遍不就啥都明白了吗。 (Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。) 不要迷信书、考题、老师、回帖; 要迷信CPU、编译器、调试器、运行结果
SSE指令集入门
在C/C++代码中使用SSE等指令集的指令(3)SSE指令集基础
在C/C++代码中使用SSE等指令集的指令(4)SSE指令集Intrinsic函数使用
Streaming SIMD Extensions
总的来说,当循环没有if else分支的时候,可以使用该方法,批量计算。
源码不太了解的地方:
uint8_t * bf_base; //bloom filter base, 32 bytes aligned
uint8_t * bf_base_rec; // the real memory space of bloom filter
memset(bf_base_rec, 0, m/8 + 32); ? +32是干嘛的?