我知道这是一个带有选定答案的老问题,但没有看到有人解释对齐和未对齐内存访问之间有什么区别的答案...
无论是 dram 还是 sram 或 flash 或其他。以 sram 为例,它是由位构成的,特定的 sram 将由固定数量的位宽和固定数量的行深构成。让我们说 32 位宽和几/多行深。
如果我对这个 sram 中的地址 0x0000 进行 32 位写入,那么围绕这个 sram 的内存控制器可以简单地对第 0 行执行一个写入周期。
如果我对这个 sram 中的地址 0x0001 进行 32 位写入,假设允许,控制器将需要读取第 0 行,修改三个字节,保留一个,然后将其写入第 0 行,然后读取第 1 行修改一个字节,将其他三个保留为找到并写回。哪些字节被修改或与系统的字节序无关。
前者对齐,后者未对齐,显然是性能差异加上需要额外的逻辑才能完成四个内存周期并合并字节通道。
如果我要从地址 0x0000 读取 32 位,然后单次读取第 0 行,就完成了。但是从 0x0001 读取,我必须执行两次读取 row0 和 row1 并且根据系统设计,只需将这些 64 位发送回处理器,可能是两个总线时钟而不是一个。或者内存控制器具有额外的逻辑,以便在一个总线周期内将 32 位在数据总线上对齐。
16 位读取要好一些,从 0x0000、0x0001 和 0x0002 读取只会从 row0 读取,并且可以基于系统/处理器设计将这些 32 位发送回,处理器提取它们或将它们移入内存控制器,以便它们降落在特定的字节通道上,因此处理器不必旋转。一个或另一个必须如果不是两者兼而有之。从 0x0003 读取虽然就像上面一样,但您必须读取第 0 行和第 1 行,因为每个字节中都有一个字节,然后将 64 位发送回处理器以提取或内存控制器将这些位组合成一个 32 位总线响应(对于这些示例,假设处理器和内存控制器之间的总线为 32 位宽)。
虽然在这个示例 sram 中,16 位写入总是以至少一个 read-modify-write 结束,地址 0x0000、0x0001 和 0x0002 读取 row0 修改两个字节并写回。地址 0x0003 读取两行,每行修改一个字节并写回。
8 位你只需要读取包含该字节的一行,写入虽然是一行的读取-修改-写入。
armv4 不喜欢未对齐,尽管您可以禁用陷阱,但结果与您在上面所期望的不同,这并不重要,当前的 arm 允许未对齐并为您提供上述行为,您可以在控制寄存器中进行一些更改,然后它将中止未对齐的传输。 mips 以前不允许,现在不知道他们在做什么。 x86、68K 等是允许的,内存控制器可能不得不做最多的工作。
显然不允许它的设计是为了性能和更少的逻辑,有些人会说这是程序员的负担,其他人可能会说这对程序员来说没有额外的工作或更容易。对齐与否,您还可以看到为什么最好不要尝试通过创建 8 位变量来节省任何内存,而是继续烧写 32 位字或寄存器或总线的自然大小。它可以以一些字节的小成本帮助您的性能。更不用说编译器需要添加的额外代码,以使 32 位寄存器模仿 8 位变量、屏蔽和有时符号扩展。在使用寄存器本机大小的情况下,不需要这些额外的指令。您还可以将多个东西打包到一个总线/内存范围的位置,并执行一个内存周期来收集或写入它们,然后使用一些额外的指令在不消耗内存的寄存器之间进行操作,并且可能会清洗指令数量。
我不同意编译器总是为目标对齐数据,有办法打破这一点。如果目标不支持未对齐,您将遇到错误。如果编译器总是根据你能想到的任何合法代码正确地做到这一点,程序员就不需要谈论这个问题,除非是为了性能,否则没有理由提出这个问题。如果您不控制 void ptr 地址是否对齐,那么您必须始终使用 mem2() 未对齐访问,或者您必须根据 ptr 作为 nik 的值在代码中执行 if-then-else指出。通过声明为 void,C 编译器现在无法正确处理您的对齐方式,并且无法保证。如果您采用 char *prt 并将其提供给这些函数,那么所有的赌注都在于编译器是否正确,而无需添加额外的代码,无论是埋在 mem2() 函数中还是在这两个函数之外。所以写在你的问题 mem2() 是唯一正确的答案。
说在您的台式机/笔记本电脑中使用的 DRAM 往往是 64 或 72(带 ecc)位宽,并且对它们的每次访问都是对齐的。即使记忆棒实际上是由 8 位宽或 16 或 32 位宽的芯片组成的。 (由于各种原因,这可能会随着手机/平板电脑而改变)内存控制器和理想情况下至少有一个缓存位于该 DRAM 前面,以便处理小于总线宽度的未对齐甚至对齐访问读取-修改-写入在缓存 sram 中速度更快,并且 DRAM 访问都是对齐的全总线宽度访问。如果您在 DRAM 前面没有缓存并且控制器设计用于全宽度访问,那么这是最差的性能,如果设计用于单独点亮字节通道(假设 8 位宽芯片),那么您没有读取修改-writes 但更复杂的控制器。如果典型用例是缓存(如果设计中有缓存),那么在控制器中为每个字节通道进行额外的工作可能没有意义,但让它知道如何进行全总线宽度大小的传输或倍数。