内存对齐(memory align)

news2025/1/15 16:56:25

0. 内存结构

我们平时所称的内存也叫随机访问存储器(random-access memory)也叫RAM。而RAM分为两类:

  • 一类是静态RAM(SRAM),这类SRAM用于前边介绍的CPU高速缓存L1Cache,L2Cache,L3Cache。其特点是访问速度快,访问速度为1 - 30个时钟周期,但是容量小,造价高。
  • 另一类则是动态RAM(DRAM),这类DRAM用于我们常说的主存上,其特点的是访问速度慢(相对高速缓存),访问速度为50 - 200个时钟周期,但是容量大,造价便宜些(相对高速缓存)。

内存由一个一个的存储器模块(memory module)组成,它们插在主板的扩展槽上。常见的存储器模块通常以64位为单位(8个字节)传输数据到存储控制器上或者从存储控制器传出数据。
在这里插入图片描述
如图所示内存条上黑色的元器件就是存储器模块(memory module)。多个存储器模块连接到存储控制器上,就聚合成了主存。
在这里插入图片描述
而前边介绍到的DRAM芯片就包装在存储器模块中,每个存储器模块中包含8个DRAM芯片,依次编号为0 - 7。
在这里插入图片描述
而每一个DRAM芯片的存储结构是一个二维矩阵,二维矩阵中存储的元素我们称为超单元(supercell),每个supercell大小为一个字节(8 bit)。每个supercell都由一个坐标地址(i,j)。

i表示二维矩阵中的行地址,在计算机中行地址称为RAS(row access strobe,行访问选通脉冲)。
j表示二维矩阵中的列地址,在计算机中列地址称为CAS(column access strobe,列访问选通脉冲)。

下图中的supercell的RAS = 2,CAS = 2。
在这里插入图片描述
DRAM芯片中的信息通过引脚流入流出DRAM芯片。每个引脚携带1 bit的信号。
图中DRAM芯片包含了两个地址引脚(addr),因为我们要通过RAS,CAS来定位要获取的supercell。还有8个数据引脚(data),因为DRAM芯片的IO单位为一个字节(8 bit),所以需要8个data引脚从DRAM芯片传入传出数据。

注意这里只是为了解释地址引脚和数据引脚的概念,实际硬件中的引脚数量是不一定的。

1. DRAM芯片的访问

我们现在就以读取上图中坐标地址为(2,2)的supercell为例,来说明访问DRAM芯片的过程。
在这里插入图片描述

  1. 首先存储控制器将行地址RAS = 2通过地址引脚发送给DRAM芯片。
  2. DRAM芯片根据RAS = 2将二维矩阵中的第二行的全部内容拷贝到内部行缓冲区中。
  3. 接下来存储控制器会通过地址引脚发送CAS = 2到DRAM芯片中。
  4. DRAM芯片从内部行缓冲区中根据CAS = 2拷贝出第二列的supercell并通过数据引脚发送给存储控制器。

DRAM芯片的IO单位为一个supercell,也就是一个字节(8 bit)。

2. CPU如何读写主存

前边我们介绍了内存的物理结构,以及如何访问内存中的DRAM芯片获取supercell中存储的数据(一个字节)。本小节我们来介绍下CPU是如何访问内存的。
在这里插入图片描述

2.1 总线结构

CPU与内存之间的数据交互是通过总线(bus)完成的,而数据在总线上的传送是通过一系列的步骤完成的,这些步骤称为总线事务(bus transaction)。

其中数据从内存传送到CPU称之为读事务(read transaction),数据从CPU传送到内存称之为写事务(write transaction)。

总线上传输的信号包括:地址信号,数据信号,控制信号。其中控制总线上传输的控制信号可以同步事务,并能够标识出当前正在被执行的事务信息:
当前这个事务是到内存的?还是到磁盘的?或者是到其他IO设备的?
这个事务是读还是写?
总线上传输的地址信号(内存地址),还是数据信号(数据)?

若使用MESI缓存一致性协议,当core0修改字段a的值时,其他CPU核心会在总线上嗅探字段a的内存地址,如果嗅探到总线上出现字段a的内存地址,说明有人在修改字段a,这样其他CPU核心就会失效自己缓存字段a所在的cache line。

如上图所示,其中系统总线是连接CPU与IO bridge的,存储总线是来连接IO bridge和主存的。
IO bridge负责将系统总线上的电子信号转换成存储总线上的电子信号。IO bridge也会将系统总线和存储总线连接到IO总线(磁盘等IO设备)上。这里我们看到IO bridge其实起的作用就是转换不同总线上的电子信号。

2.2 CPU从内存读取数据过程

假设CPU现在要将内存地址为A的内容加载到寄存器中进行运算。
在这里插入图片描述
首先CPU芯片中的总线接口会在总线上发起读事务(read transaction)。该读事务分为以下步骤进行:

  1. CPU将内存地址A放到系统总线上。随后IO bridge将信号传递到存储总线上。
  2. 主存感受到存储总线上的地址信号并通过存储控制器将存储总线上的内存地址A读取出来。
  3. 存储控制器通过内存地址A定位到具体的存储器模块,从DRAM芯片中取出内存地址A对应的数据X。
  4. 存储控制器将读取到的数据X放到存储总线上,随后IO bridge将存储总线上的数据信号转换为系统总线上的数据信号,然后继续沿着系统总线传递。
  5. CPU芯片感受到系统总线上的数据信号,将数据从系统总线上读取出来并拷贝到寄存器中。

以上就是CPU读取内存数据到寄存器中的完整过程。
但是其中还涉及到一个重要的过程,这里我们还是需要摊开来介绍一下,那就是存储控制器如何通过内存地址A从主存中读取出对应的数据X的?

接下来我们结合前边介绍的内存结构以及从DRAM芯片读取数据的过程,来总体介绍下如何从主存中读取数据。

2.3 如何根据内存地址从主存中读取数据

前边介绍到,当主存中的存储控制器感受到了存储总线上的地址信号时,会将内存地址从存储总线上读取出来。
随后会通过内存地址定位到具体的存储器模块。还记得内存结构中的存储器模块吗??
在这里插入图片描述
而每个存储器模块中包含了8个DRAM芯片,编号从0 - 7

存储控制器会将内存地址转换为DRAM芯片中supercell在二维矩阵中的坐标地址(RAS,CAS)。并将这个坐标地址发送给对应的存储器模块。随后存储器模块会将RAS和CAS广播到存储器模块中的所有DRAM芯片。依次通过(RAS,CAS)从DRAM0到DRAM7读取到相应的supercell
在这里插入图片描述
我们知道一个supercell存储了8 bit数据,这里我们从DRAM0到DRAM7 依次读取到了8个supercell也就是8个字节,然后将这8个字节返回给存储控制器,由存储控制器将数据放到存储总线上。

CPU总是以word size为单位从内存中读取数据,在64位处理器中的word size为8个字节。64位的内存也只能每次吞吐8个字节。

CPU每次会向内存读写一个cache line大小的数据(64个字节),但是内存一次只能吞吐8个字节。

所以在内存地址对应的存储器模块中,DRAM0芯片存储第一个低位字节(supercell),DRAM1芯片存储第二个字节,…依次类推DRAM7芯片存储最后一个高位字节。

内存一次读取和写入的单位是8个字节。而且在程序员眼里连续的内存地址实际上在物理上是不连续的。因为这连续的8个字节其实是存储于不同的DRAM芯片上的。每个DRAM芯片存储一个字节(supercell)。

在这里插入图片描述

3. 为什么要内存对齐

我们在了解了内存结构以及CPU读写内存的过程之后,现在我们回过头来讨论下本小节开头的问题:为什么要内存对齐?
下面笔者从三个方面来介绍下要进行内存对齐的原因:

  • 速度

CPU读取数据的单位是根据word size来的,在64位处理器中word size = 8字节,所以CPU向内存读写数据的单位为8字节。

在64位内存中,内存IO单位为8个字节,我们前边也提到内存结构中的存储器模块通常以64位为单位(8个字节)传输数据到存储控制器上或者从存储控制器传出数据。因为每次内存IO读取数据都是从数据所在具体的存储器模块中包含的这8个DRAM芯片中以相同的(RAM,CAS)依次读取一个字节,然后在存储控制器中聚合成8个字节返回给CPU。

由于存储器模块中这种由8个DRAM芯片组成的物理存储结构的限制,内存读取数据只能是按照地址顺序8个字节的依次读取----8个字节8个字节地来读取数据
在这里插入图片描述

  • 假设我们现在读取0x0000 - 0x0007这段连续内存地址上的8个字节。由于内存读取是按照8个字节为单位依次顺序读取的,而我们要读取的这段内存地址的起始地址是0(8的倍数),所以0x0000 - 0x0007中每个地址的坐标都是相同的(RAS,CAS)。所以他可以在8个DRAM芯片中通过相同的(RAS,CAS)一次性读取出来。
  • 如果我们现在读取0x0008 - 0x0015这段连续内存上的8个字节也是一样的,因为内存段起始地址为8(8的倍数),所以这段内存上的每个内存地址在DREAM芯片中的坐标地址(RAS,CAS)也是相同的,我们也可以一次性读取出来。
    注意:0x0000 - 0x0007内存段中的坐标地址(RAS,CAS)与0x0008 - 0x0015内存段中的坐标地址(RAS,CAS)是不相同的。
  • 但如果我们现在读取0x0007 - 0x0014这段连续内存上的8个字节情况就不一样了,由于起始地址0x0007在DRAM芯片中的(RAS,CAS)与后边地址0x0008 - 0x0014的(RAS,CAS)不相同,所以CPU只能先从0x0000 - 0x0007读取8个字节出来先放入结果寄存器中并左移7个字节(目的是只获取0x0007),然后CPU在从0x0008 - 0x0015读取8个字节出来放入临时寄存器中并右移1个字节(目的是获取0x0008 - 0x0014)最后与结果寄存器或运算。最终得到0x0007 - 0x0014地址段上的8个字节。

从以上分析过程来看,当CPU访问内存对齐的地址时,比如0x0000和0x0008这两个起始地址都是对齐至8的倍数。CPU可以通过一次read transaction读取出来。
但是当CPU访问内存没有对齐的地址时,比如0x0007这个起始地址就没有对齐至8的倍数。CPU就需要两次read transaction才能将数据读取出来。

  • 原子性

如CPU可以原子地操作一个对齐的word size memory。64位处理器中word size = 8字节。

  • 尽量分配在一个缓存行中

前边在介绍false sharding的时候我们提到目前主流处理器中的cache line大小为64字节,堆中对象的起始地址通过内存对齐至8的倍数,可以让对象尽可能的分配到一个缓存行中。一个内存起始地址未对齐的对象可能会跨缓存行存储,这样会导致CPU的执行效率慢2倍。
其中对象中字段内存对齐的其中一个重要原因也是让字段只出现在同一 CPU 的缓存行中。如果字段不是对齐的,那么就有可能出现跨缓存行的字段。也就是说,该字段的读取可能需要替换两个缓存行,而该字段的存储也会同时污染两个缓存行。这两种情况对程序的执行效率而言都是不利的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/156175.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

不求星光灿烂,但愿岁月静好

作者:非妃是公主 专栏:《程序人生》 个性签:顺境不惰,逆境不馁,以心制境,万事可成。——曾国藩 文章目录不求星光灿烂,但愿岁月静好说一说这一年的自己的收获吧2022年的追求自我学会拒绝尝试表达…

Unreal单播委托

单播委托只能注册一个函数:无参无返回值给委托绑定函数:判断如果委托有绑定函数就发起广播:解绑:绑定方式除了BindUObject,还有BindUFunction,通过这种方式绑定需要给函数添加UFUNCTION标记:还有BindLambda匿名函数:BindRaw可以绑定原生C类中的函数:无参有返回值定义委托类型:声…

Linux进程状态与系统负载检测

1.基础知识-进程的5个状态进程可以分为五个状态,分别是:1)创建状态一个应用程序从系统上启动,首先就是进入创建状态,需要获取系统资源创建进程管理块(PCB)完成资源分配。2) 就绪状态在创建状态完…

Dextran-Azide,Dextran-N3结构式;叠氮修饰的葡聚糖 科研用试剂说明

Dextran-N3,叠氮基团葡聚糖 英文名称:Dextran-Azide,Dextran-N3 中文名:叠氮修饰的葡聚糖 存储条件:-20C,避光,避湿 外观: 固体或类白色絮状,取决于分子量 溶剂:溶于大部分有机溶剂&…

kafka单节点部署,手把手从零到一

kafka单节点部署 书接上回:zookeeper单节点部署,手把手从零到一 建议配套观看 2、kafka的单节点部署 2.1、下载 这里如果和zookeeper相似的就不再赘述,直接上命令 wget https://archive.apache.org/dist/kafka/2.8.2/kafka_2.12-2.8.2.tgz…

深入了解ArrayBlockingQueue 阻塞队列

1. 前言 开始正式了解阻塞队列之前,我们需要了解什么是队列。 队列有什么作用。其实队列的作用就是解耦,更加确切的说应该是生产者以及消费者 之间的解耦 今天就让我们来看下ArrayBlockingQueue 的实现。虽然通过名称就可以看到,无非是通过数…

Theory for the information-based decomposition of stock price

文章目录MotivationThe potential of Brogaard DecompositionIntuitions for Brogaard decompositionTechnique details in Brogaard decompositionDefine the VAR systemIdentify the VAR systemVariance decompositionSummaryMain ReferencesMotivation Brogaard et al. (20…

1000字带您了解网络设备的接口分类和接口编号规则

通过本文,您可以了解到设备的接口分类和接口编号规则。 文章目录一、接口分类1.1 物理接口1.1.1 管理接口1.1.1 业务接口LAN侧接口WAN侧接口1.2 逻辑接口二、接口编号规则2.1 物理接口编号规则三、总结一、接口分类 接口是设备与网络中的其它设备交换数据并相互作用…

3.3 行列式的几何意义

文章目录二维面积三维体积多维体积行列式是线性代数一个非常重要的内容,也是非常难的领域.行列式在欧几里得空间里还有特殊的几何意义。二维面积 &esmp; 两个向量围成的平行四边形的面积就是这两个向量组成的矩阵的行列式的绝对值。以两个向量(3.−2)T(3.-2)^…

结构体 · 内存对齐

欢迎来到 Claffic 的博客 💞💞💞 前言: 在初识C语言中简单介绍了结构体,结构体可以理解为不同类型数据的集合体,但是你想过结构体的大小是如何计算的吗?看完这篇博客,你就能给自己答…

Linux 计算机网络 route 路由表、多网段与 bond 的故事

Linux 计算机网络 route 路由表、多网段与 bond 的故事 序 在之前的章节中,介绍了计算机网络的发展以及各种解析,在之中我们提到了每个主机设备都会维护一张自己的路由表,通过路由表来确定在不同网络之间,怎么将数据规划传输到各…

1988-2020年31省基尼系数数据

1、时间:1988-2020年 2、范围:31省 3、指标:包括省基尼系数年度数据,省城市和农村基尼系数年度 4、来源及计算方法说明附在文件内 5、指标说明: 基尼系数(英文:Gini index、Gini Coefficie…

LeetCode 94. 二叉树的中序遍历

🌈🌈😄😄 欢迎来到茶色岛独家岛屿,本期将为大家揭晓LeetCode 94. 二叉树的中序遍历,做好准备了么,那么开始吧。 🌲🌲🐴🐴 一、题目名称 LeetC…

Mybatis获取参数

Mybatis获取参数 配置模板 mybatis获取参数值的两种方式 1、&{}: 字符串拼接 2、#{}: 占位符赋值 MyBatis获取参数值的各种情况: MyBatis获取参数值的各种情况: 1、mapper接口方法的参数为单个的字面量类型 可以通过&#xf…

双系统下 linux挂载window磁盘

如果想让linux访问window分区磁盘,呈只读状态,解决办法是bios取消window快速开机。永久挂载windows磁盘 https://blog.csdn.net/yuehenmiss/article/details/124737456 # 创建挂载目录 sudo mkdir /window # 挂载分区 sudo mount /dev/sda1 /window # 查…

产品经理必懂知识之计算机基础知识

作为产品经理,非常有必要了解一下计算机的发展历史,今天带大家一起,大概地了解一下计算机的基础知识,希望能够帮助到大家,框架如下: 一、计算机发展史 1.1计算机的诞生 1946年第一台电子计算机问世美国宾…

YOLOv8训练自己的数据集(超详细)

一、准备深度学习环境 本人的笔记本电脑系统是:Windows10 YOLO系列最新版本的YOLOv8已经发布了,详细介绍可以参考我前面写的博客,目前ultralytics已经发布了部分代码以及说明,可以在github上下载YOLOv8代码,代码文件夹…

一种车辆纵向控制切换算法设计思路

传统及主流的纵向控制切换算法: 例如《某避障控制策略研究》硕士论文: 在CarSim中设定节气门开度及制动踏板力为0,测得不同车速工况下车辆自然滑行的减速度。为了避免在控制过程中车辆驱动与制动切换的过于频繁,在其两侧设置了宽…

VUE_vue-cli 卸载不掉的问题解决

nodejs版本最好在v17以下,推荐使用v16.19.0 问题 由于项目需要旧版的 vue-cli ,所以需要事先卸载新版本; 运行命令全局卸载: yarn global remove vue/cli// 查看当前版本确定是否卸载 vue --version结果还是旧版本,…

使用ResNet34实现CIFAR100数据集的训练

如果对你有用的话,希望能够点赞支持一下,这样我就能有更多的动力更新更多的学习笔记了。😄😄 使用ResNet进行CIFAR-10数据集进行测试,这里使用的是将CIFAR-10数据集的分辨率扩大到32X32,因为算力相关的…