本文共 1414 字,大约阅读时间需要 4 分钟。
无论是自己写代码,还是阅读一些开源的代码,总会遇到一些对齐问题,经过查阅资料和自己编程做的一些小实验,总结下关于对齐的那些事。
我个人把对齐分为字节对齐和页对齐,下面依次讨论
首先我们来思考一下CPU是如何读取数据的。
先明确一个问题,CPU不是按照字节来处理数据的,而是按照字长来处理数据的。所谓字长,就是CPU可以一次性读取的数据长度。假设CPU是32位的,那么CPU一次性就可以读取32位的数据。按照CPU的设计,读取的地址总是32的倍数开始。
字节对齐问题和CPU架构以及编译器有极大的关系。比如在x86架构中,即便是字节没有对齐,也不会出现问题,原因是x86架构的CPU支持读取奇数位地址,但是会降低程序运行的效率。用gcc编译的程序,编译器会默认4字节对齐。如果定义了一个自己没有对齐的结构体,编译器会自动帮你对齐。
但是如果是某些不支持读取奇数地址的CPU架构中,就会产生意想不到的问题。
让我们来考察这么以下代码。
struct A{ short i; short j:1;};struct B{ short i;};struct A a;struct B b;
如果是某些架构的CPU中,b的起始地址就会是奇数,当读取b的内容就有可能会从a.j开始读取。
当然,现在这种架构的CPU往往只会在嵌入式行业中有所应用,但是考虑到代码的可移植性,字节对齐还是很有必要做的。
考察一下结构体
struct A{ short i; int j; short k;};struct B{ short i; short k; int j;};struct A a;struct B b;
读取a和b所用的时间是相同的吗?
答案是不相同。读取a需要CPU读取三次内存,b只需要读取两次内存就可以。如果程序中存在着大量的字节不对齐的结构体,会造成程序运行效率的下降。
页面对齐对做普通应用开发的程序员来说,是不需要操心的,但是如果涉及到高性能程序开发,比如数据转发,就需要对页面对齐有所了解。
关于这个问题,需要去参考计算机组成原理相关的书籍或者网上查找一些资料,这里需要知道,当CPU触发了缺页流程,是很浪费CPU性能的。这也是为什么dpdk等高性能IO开发组件需要用到巨页的原因。如果页面没有对齐,容易造成缺页。比如以下场景:
一段小于一个页面大小的连续内存,当我们需要访问这段内存的时候,CPU就需要读取两个页面,无疑增加了缺页的概率。当然,如果我们用内核的malloc函数,如果申请的内存小于一个页面大小,系统是不会给我们分配跨页的内存,但是如果我们需要开发一套内存管理程序,就需要考虑这个问题。当上层业务申请内存的时候,不能简单的返回申请内存的起始地址,而应该先判断是否跨页。
参考内核代码
向下对齐:#define alignment_down(a, size) (a & (~ (size-1)) )
向上对齐:#define alignment_up(a, size) ((a+size-1) & (~ (size-1)))size为页大小,这个宏要求size为2的n次幂大小,否则计算会出错
转载地址:http://dyali.baihongyu.com/