处理器如何读取内存?

2023-12-04

我正在尝试重新实现 malloc,我需要了解对齐的目的。据我了解,如果内存对齐,代码将执行得更快,因为处理器不必采取额外的步骤来恢复被剪切的内存位。我想我明白 64 位处理器读取 64 位乘 64 位内存。现在,让我们想象一下我有一个按顺序排列的结构(没有填充):一个 char、一个 Short、一个 char 和一个 int。为什么短路会错位?我们拥有该块中的所有数据!为什么它必须位于 2 的倍数的地址上。对于整数和其他类型有同样的问题吗?

我还有第二个问题:根据我之前提到的结构,处理器在读取 64 位时如何知道前 8 位对应于 char,然后接下来的 16 位对应于 Short 等...?


影响甚至可以包括正确性,而不仅仅是性能:C 未定义行为 (UB) 导致可能的段错误或其他不当行为,例如,如果您有short不满足的对象alignof(short)。 (加载/存储指令默认需要对齐的 ISA 上预计会出现故障,例如 SPARC 和 MIPS64r6 之前的 MIPS。并且即使在 x86 上也可能在编译器优化循环之后,尽管 x86 asm 允许不对齐加载/存储,但某些 16 字节或更宽的 SIMD 除外。)

或撕裂原子操作,如果_Atomic int没有alignof(_Atomic int).

(通常alignof(T) = sizeof(T)在任何给定的 ABI 中,达到一定的尺寸,通常是寄存器宽度或更宽)。


malloc应该返回内存alignof(max_align_t)因为您没有任何有关如何使用分配的类型信息。

对于小于的分配sizeof(max_align_t), you can如果您愿意,可以返回仅自然对齐的内存(例如,按 4 字节对齐的 4 字节分配),因为您知道存储不能用于具有更高对齐要求的任何内容。

过度对齐的东西,例如动态分配的等价物alignas (16) int32_t foo需要使用像C11这样的特殊分配器aligned_alloc。如果您正在实现自己的分配器库,您可能希望支持aligned_realloc 和aligned_calloc,以填补ISO C 无缘无故留下的空白。

并确保你don't实现 Braindead ISO C++17 要求aligned_alloc如果分配大小不是对齐的倍数,则会失败。没有人想要一个分配器拒绝从 16 字节边界开始的 101 个浮点数的分配,或者更大的浮点数以获得更好的透明大页。aligned_alloc函数要求 and 如何解决 AVX 加载/存储操作的 32 字节对齐问题?


我想我明白 64 位处理器读取 64 位乘 64 位内存

没有。数据总线宽度和突发大小以及加载/存储执行单元最大宽度或实际使用的宽度不必与整数寄存器的宽度相同,也不必与 CPU 定义其位数相同。 (现代高性能 CPU 通常不是这样。例如,32 位 P5 Pentium 有 64 位总线;现代 32 位 ARM 具有执行原子 64 位访问的加载/存储对指令。)

处理器将整个缓存行从 DRAM / L3 / L2 缓存读取到 L1d 缓存;现代 x86 上为 64 字节;在某些其他系统上为 32 字节。

当读取单个对象或数组元素时,它们从 L1d 缓存中读取元素宽度。例如Auint16_t数组可能仅受益于 2 字节加载/存储的 2 字节边界对齐。

或者,如果编译器使用 SIMD 对循环进行矢量化,则uint16_t数组可读取 16 或 32bytes一次,即 8 或 16 个元素的 SIMD 向量。 (甚至 AVX512 为 64)。将数组与预期的向量宽度对齐会很有帮助;当不跨越缓存线边界时,未对齐的 SIMD 加载/存储在现代 x86 上运行得很快。


缓存行分割,尤其是页面分割是现代 x86 因未对齐而减慢速度的地方;高速缓存行内未对齐通常不是因为它们将晶体管用于快速未对齐加载/存储。其他一些 ISA 会因任何未对齐而变慢,有些甚至会出现故障,即使在高速缓存行内也是如此。解决方案是相同的:给类型自然对齐:alignof(T) = sizeof(T)。

在您的结构示例中,现代 x86 CPU 不会受到任何惩罚,即使short未对准。alignof(int) = 4在任何正常的 ABI 中,所以整个结构有alignof(struct) = 4, 所以char;short;char块从 4 字节边界开始。就这样short包含在单个 4 字节双字内,不跨越任何更宽的边界。 AMD 和英特尔都能够高效地处理这个问题。 (x86 ISA 保证在与 P5 Pentium 或更高版本兼容的 CPU 上对它的访问是原子的,甚至是未缓存的:为什么自然对齐变量的整数赋值在 x86 上是原子的?)

一些非 x86 CPU 会因未对齐短路而受到惩罚,或者必须使用其他指令。 (由于您知道相对于对齐的 32 位块的对齐方式,因此对于加载,您可能会执行 32 位加载和移位。)

所以是的,访问包含以下内容的单个单词是没有问题的short, but 问题是加载端口硬件提取和零扩展(或符号扩展)short进入完整的寄存器。这就是 x86 花费晶体管来提高速度的地方。 (@Eric的回答在这个问题的先前版本中,详细介绍了所需的转换。)

将未对齐的存储提交回缓存也很重要。例如,L1d 缓存可能在 32 位或 64 位块(我将其称为“缓存字”)中具有 ECC(针对位翻转的纠错)。因此,仅写入缓存字的一部分以及将其转移到要访问的缓存字内的任意字节边界都是一个问题。 (存储缓冲区中相邻窄存储的合并可以产生全角提交,从而避免在以这种方式处理窄存储的缓存中更新部分字的 RMW 周期)。请注意,我现在说“单词”是因为我谈论的是更面向单词的硬件,而不是像现代 x86 那样围绕未对齐的加载/存储进行设计。See 是否有任何现代 CPU 的缓存字节存储实际上比字存储慢?(存储单个字节仅比未对齐的字节简单一点short)

(If the short跨越两个缓存字,当然需要单独的 RMW 周期,每个字节一个。)

当然还有short错位的原因很简单alignof(short) = 2并且它违反了此 ABI 规则(假设 ABI 确实具有该规则)。因此,如果您将指向它的指针传递给其他函数,您可能会遇到麻烦。特别是在负载未对齐时出现故障的 CPU 上,而不是在运行时发现负载未对齐时由硬件处理这种情况。然后你可以得到像这样的案例为什么对 mmap 内存的未对齐访问有时会在 AMD64 上出现段错误?其中 GCC 自动向量化预计通过执行 2 字节元素标量的某些倍数来达到 16 字节边界,因此违反 ABI 会导致 x86 上的段错误(通常可以容忍未对齐)。


有关内存访问的完整详细信息,从 DRAM RAS / CAS 延迟到缓存带宽和对齐,请参阅每个程序员都应该了解哪些关于内存的知识?它几乎仍然相关/适用

Also 内存对齐的目的有一个很好的答案。 SO 中还有很多其他很好的答案内存对齐 tag.

有关(某种程度上)现代英特尔加载/存储执行单元的更详细信息,请参阅:https:// electronics.stackexchange.com/questions/329789/how-can-cache-be-that-fast/329955#329955


处理器在读取 64 位时如何知道前 8 位对应于 char,然后接下来的 16 位对应于短整型等等...?

事实并非如此,除了它正在运行以这种方式处理数据的指令之外。

在 asm / 机器代码中,一切都只是字节。每一条指令指定确切地说如何处理哪些数据。由编译器(或人类程序员)在原始字节数组(主内存)之上实现具有类型的变量和 C 程序的逻辑。

我的意思是,在 asm 中,您可以运行任何您想要的加载或存储指令,并且您可以在正确的地址上使用正确的指令。你could加载与两个相邻重叠的 4 个字节int变量存入浮点寄存器,然后运行addss(单精度 FP 添加)就可以了,CPU 不会抱怨。但您可能不想这样做,因为让 CPU 将这 4 个字节解释为 IEEE754 二进制 32 浮点不太可能有意义。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

处理器如何读取内存? 的相关文章

随机推荐

  • Rails form_for 从不调用创建控制器操作来使用redirect_to

    我正在使用 Rails 3 并且 StatusController 中有一个 form for 当我点击提交按钮时 我的创建方法永远不会被调用 我的创建方法有一个redirect to index 但是当我点击 提交 时 所有信息都保留在表
  • 如何使用列号从 MySQL 表中删除列

    我正在尝试从与 Handsontable 一起使用的 MySQL 表中删除一列 当我从 Handsontable 中删除一列时 我可以使用以下命令获取该列的索引afterRemoveCol 打回来 afterRemoveCol functi
  • MySQL 上的数据库事务内的 Codeception 验收测试

    使用 Codeception 为我们的 Custom 带有 Symfony 组件和 Doctrine 框架编写验收测试 我们有一组实用方法 由 Phpunit 测试单独使用 用于创建要测试的各种实体 这包括用户和其他相关数据等内容 在我们的
  • 重载方法值适用于替代方案:

    我是 Spark 新手 我试图为 json 数据定义一个模式 并在 spark shell
  • 如何修复此代码以创建字符串数组?

    我想创建一个字符串数组 这是代码 include
  • 如何在 eclipselink 中禁用缓存

    我尝试通过在 persistence xml 中使用以下属性 使用 Eclipse indigo 禁用 EclipseLink 中的 L2 缓存
  • 使用 OpenMP 并行化输出

    我编写了一个必须处理大量数据的 C 应用程序 使用 OpenMP 我很好地并行化了处理阶段 但令人尴尬的是 我发现输出写入现在是瓶颈 我决定使用一个parallel for那里也是如此 因为我输出项目的顺序是无关紧要的 它们只需要作为连贯的
  • OpenCV 创建后更改关键点或描述符参数

    在最新版本中 OpenCV 允许使用 create 函数轻松创建关键点检测器 描述符或匹配器 例如 cv Ptr
  • Unix和Linux中的“dot”和“dot dot”文件是真实文件吗?

    做特殊文件 and 实际上存在并作为普通文件存储 位于文件系统中 或者仅在文件系统处理程序访问时才解释 创建它们 当前目录 父目录 我的假设是它们不存在 否则 当您创建符号链接或绑定安装时 事情就会崩溃 我认为目前接受的答案并不正确 或者至
  • 以编程方式添加代码模板?

    我正在为我们的项目编写一个自定义 XML 编辑器 并希望添加对模板的支持 即当用户写
  • Spark异常:worker中的Python版本3.4与驱动程序3.5中的版本不同

    我正在使用 Amazon EC2 并且我将主服务器和开发服务器合二为一 我还有另一个针对单个工人的实例 我对此很陌生 但我已经设法使 Spark 在独立模式下工作 现在我正在尝试集群 master 和worker 处于活动状态 我可以看到它
  • fpdf“UnicodeEncodeError:'latin-1'编解码器无法对位置 88 中的字符 '\u2013' 进行编码:序数不在范围内(256)”

    我正在尝试在 Python 中将文本文件转换为 pdf 但出现错误 为什么会发生这种情况以及如何解决 这是我的代码 import fpdf from fpdf import FPDF pdf FPDF pdf add page pdf se
  • PHP:将本地时间转换为 UTC

    假设我得到一个像这样的字符串08 22 2015 10 56 PM并且该日期 时间字符串始终仅指一个特定时区 我需要能够将其转换为这种格式 Ymd THis Z 这是 iCal 格式 如何将该字符串转换为祖鲁时间并转换为 Ymd THis
  • 如何在 Xamarin iOS 上执行简单的后台任务

    在我们的应用程序中 用户可以跟踪并提交他们记录的旅程 我需要一种在 iOS 中创建任务的简单方法 我已经在 Android 上创建并测试了它 它的工作原理是 用户选择他们想要提交的旅程 点击同步并创建一个前台服务 将旅程同步到我们的 API
  • 用于仅插入/仅查询应用程序的 ORM 框架

    我已经使用 Hibernate 多年了 从来没有遇到过任何问题 但我刚刚意识到我的大部分工作都涉及 CRUD 方法 其中我需要数据保持持久化并随意修改 这样做的问题是 有人想要制作 2 个独立的应用程序 一个用于批量插入 另一个对插入的数据
  • 格式化斯坦福 Corenlp 的 NER 输出

    我正在与斯坦福 CoreNLP 合作并将其用于 NER 但是当我提取组织名称时 我看到每个单词都标有注释 因此 如果实体是 NEW YORK TIMES 那么它会被记录为三个不同的实体 NEW YORK 和 TIMES 我们是否可以在斯坦福
  • 重用PreparedStatement

    我在我们的代码库上运行了 findbugs 它指出还有两个语句仍然需要关闭 在这部分代码中 我们运行 preparedStatement connection prepareStatement query 对于3个不同的查询 重用prepa
  • 如何使用 Greasemonkey 脚本通过 XSLT 转换 XML 文件?

    我有一个搜索服务器 它提供一个测试页面 我可以在其中输入查询并以 XML 形式返回结果 我希望能够以更加用户友好的方式浏览结果 因此我开始使用 XSLT 现在我有了一个简单的样式表 可以将不知何故臃肿的 XML 转换为仅显示部分数据的简单表
  • 仅获取白色屏幕截图

    我可以读取条形码 但无法获取屏幕快照 getScreenImage 函数获取白屏 如何获取屏幕截图 包括我看到的相机视图的屏幕 谢谢 interface igViewController
  • 处理器如何读取内存?

    我正在尝试重新实现 malloc 我需要了解对齐的目的 据我了解 如果内存对齐 代码将执行得更快 因为处理器不必采取额外的步骤来恢复被剪切的内存位 我想我明白 64 位处理器读取 64 位乘 64 位内存 现在 让我们想象一下我有一个按顺序