相信绝大多数的人都了解内存对齐,对齐后性能高。但是其最最底层的原理是啥呢? 有的人可能会说,因为高速缓存的工作机制。读者你很聪明,这是原因之一。但我今天想挖的是更底层一点的原理,让我们去内存的物理构成里找找答案!

内存物理结构

我们来了解一下内存的物理构造,一般内存的外形图片如下图:

图1 内存外形图

一个内存是由若干个黑色的内存颗粒构成的。每一个内存颗粒叫做一个chip。每个chip内部,是由8个bank组成的。其构造如下图:

图2 chip内部构成

而每一个bank是一个二维平面上的矩阵,前面文章中我们说到过。矩阵中每一个元素中都是保存了1个字节,也就是8个bit。

图3 bank内部构成

内存编址方式

那么对于我们在应用程序中内存中地址连续的8个字节,例如0x0000-0x0007,是从位于bank上的呢?直观感觉,应该是在第一个bank上吗?其实不是的,程序员视角看起来连续的地址0x0000-0x0007,实际上是位于8个bank中的,每一个bank只保存了一个字节。在物理上,他们并不连续。下图很好地阐述了实际情况。

图4 连续8字节在内存中实际分布

你可能想知道这是为什么,原因是电路工作效率。内存中的8个bank是可以并行工作的。如果你想读取地址0x0000-0x0007,每个bank工作一次,拼起来就是你要的数据,IO效率会比较高。但要存在一个bank里,那这个bank只能自己干活。只能串行进行读取,需要读8次,这样速度会慢很多。

结论

所以,内存对齐最最底层的原因是内存的IO是以8个字节64bit为单位进行的。 对于64位数据宽度的内存,假如cpu也是64位的cpu(现在的计算机基本都是这样的),每次内存IO获取数据都是从同行同列的8个bank中各自读取一个字节拼起来的。从内存的0地址开始,0-7字节的数据可以一次IO读取出来,8-15字节的数据也可以一次读取出来。

换个例子,假如你指定要获取的是0x0001-0x0008,也是8字节,但是不是0开头的,内存需要怎么工作呢?没有好办法,内存只好先工作一次把0x0000-0x0007取出来,然后再把0x0008-0x0015取出来,把两次的结果都返回给你。CPU和内存IO的硬件限制导致没办法一次跨在两个数据宽度中间进行IO。这样你的应用程序就会变慢,算是计算机因为你不懂内存对齐而给你的一点点惩罚。

扩展1:事实上,编译和链接器会自动替开发者对齐内存的,尽量帮你保证一个变量不跨列寻址。但是他不能做到十分完美。

扩展2:其实在内存硬件层上,还有操作系统层。操作系统还管理了CPU的一级、二级、三级缓存。不知道你有没有印象,我们前面的文章说过高速缓存里的Cache Line是64字节,它是内存IO单位的8倍,不会让内存IO浪费。


©著作权归作者所有:来自51CTO博客作者mb5fd86a050ef28的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 一次内存性能提升的项目实践
  2. 开发内功修炼内存篇汇总
  3. 挑战Redis单实例内存最大极限,“遭遇”NUMA陷阱!
  4. C语言中的指针
  5. 败家玩意儿!Redis 竟然浪费了这么多内存!
  6. 1Mbps能做什么?
  7. Flink State 最佳实践
  8. Java堆外内存排查小结
  9. Linux压力测试软件Stress使用指南

随机推荐

  1. PHP 跨域之header函数(代码示例)
  2. 如何保证消息队列的高可用?
  3. PHP识别文件伪装(文件上传)
  4. PHP中Trait的用法及示例
  5. php基础知识考察点之正则表达式
  6. PHP+Ajax实现文章心情投票功能(代码实例)
  7. php实现微信企业付款到零钱
  8. 关于CentOS6.x/6.5/6.4/6.3/6.2/7.x 64位
  9. 正则表达式中模式修正符作用详解
  10. PHP中的global关键字用法