2

假设我在两个相隔 1024000+1 字节的数组之间复制数据。由于偏移量不是字长的倍数,因此我需要进行一些未对齐的访问 - 加载或存储(目前,让我们忘记可以通过一些 ORing 和位移完全避免未对齐的访问)。哪个未对齐的负载或未对齐的存储会更昂贵?

这是一种假设情况,所以我不能只是对其进行基准测试:-) 我更感兴趣的是哪些因素会导致性能差异(如果有的话)。指向一些进一步阅读的指针会很棒。

谢谢!

4

3 回答 3

2

Actually that depends greatly on the CPU you are using. On newer Intel CPUs there is no penalty for loading and storing unaligned words (at least none that you can notice). Only if you load and store 16byte or 32byte unaligned chunks you may see small performance degradation.

于 2013-06-11T07:08:55.680 回答
2

未对齐的写入将需要读取两个目标字,合并新数据并写入两个字。这将与对齐读取相结合。所以,3R + 2W。

未对齐的读取将需要读取两个源字,并合并数据(移位和位)。这将与对齐的写入相结合。所以,2R + 1W。

因此,未对齐的读取显然是赢家。

当然,正如您所说,有更有效的方法可以避免除数组末端之外的任何未对齐操作。

于 2013-06-10T22:20:07.040 回答
1

多少数据?我们是在谈论大块数据(在噪声中)末端未对齐的两件事还是未对齐的一项(单词等)(100%的数据)?

您是否使用 memcpy() 来移动这些数据等?

我更感兴趣的是哪些因素会导致性能差异(如果有的话)。

存储器、模块、芯片、模块等通常以固定的访问大小进行组织,至少在沿途的某个地方存在固定的访问大小。让我们只说 64 位宽,这些天并不少见。因此,在该层,无论它在哪里,您都只能以对齐的 64 位单元写入或读取。

如果您考虑写入与读取,通过读取发送地址并且必须进入内存并且数据返回,则必须进行完整的往返。通过写入,执行写入所需知道的一切都在出站路径上进行,因此发生火灾并忘记类型交易的情况并不少见,其中内存控制器获取地址和数据并告诉处理器写入已完成,即使信息还没有到达内存。它确实需要时间,但不像读取那么长(这里不谈论闪存/proms 只是 ram),因为读取需要两条路径。因此,对于对齐的全宽内容,写入可以更快,一些系统可能会等待数据一直到内存,然后返回一个完成,这可能与读取的时间大致相同。不过,这取决于您的系统,内存技术可以使内存本身更快或更慢。现在,在没有发生任何事情之后的第一次写入可以做到这一点并忘记事情,但是连续的第二次或第三次或第四次或第 16 次最终会填满路径上某处的缓冲区,处理器必须等待最旧的一次写入在最近的一个在队列中占有一席之地之前,它一直到内存。因此,对于突发性数据,写入可能比读取快,但对于大量数据移动,它们会相互接近。但是连续的第二个或第三个或第四个或第 16 个最终会填满路径沿线某处的缓冲区,并且处理器必须等待最旧的缓冲区一直到达内存,然后最新的缓冲区才能在内存中占有一席之地队列。因此,对于突发性数据,写入可能比读取快,但对于大量数据移动,它们会相互接近。但是连续的第二个或第三个或第四个或第 16 个最终会填满路径沿线某处的缓冲区,并且处理器必须等待最旧的缓冲区一直到达内存,然后最新的缓冲区才能在内存中占有一席之地队列。因此,对于突发性数据,写入可能比读取快,但对于大量数据移动,它们会相互接近。

现在对齐。整个内存宽度将在读取时读取,在这种情况下,假设为 64 位,如果您只对其中 8 位真正感兴趣,那么在内存和处理器之间的某个位置,其他 24 位将被丢弃,这取决于系统。写入不是一个完整的、对齐的内存大小意味着您必须读取内存的宽度,比如 64 位,修改新的位,比如 8 位,然后将整个 64 位写回。一个读-修改-写。读只需要读,写需要读-修改-写,离需要读-修改-写的内存越远,时间越长,速度越慢,不管读-修改-写是什么,都不能比单独读取,因此读取速度会更快,

未对齐只会增加问题。说最坏的情况,如果您想读取 16 位,其中 8 位位于一个 64 位位置,而另一个 8 位位于下一个 64 位位置,则需要读取 128 位以满足该 16 位读取。这究竟是如何发生的以及多少惩罚取决于您的系统。一些总线设置传输 X 时钟数,但之后数据是每个总线宽度一个时钟,因此 128 位读取可能仅比读取 64 或最坏情况所需的时钟长一个时钟(比几十到几百个)为了获得这个 16 位读取所需的 128 位,可能需要两倍的时间。写入是读取-修改-写入,因此需要读取时间,然后修改两个 64 位项目,然后将它们写回,相同的处理可能是每个方向上 X+1 个时钟,或者可能与 2X 个时钟一样糟糕在每个方向。

缓存帮助和伤害。使用缓存的一个好处是您可以平滑传输到慢速内存,您可以让缓存担心确保所有内存访问对齐并且所有写入都是完整的 64 位写入,等等。缓存将执行相同或更大大小的读取。因此,对于第一个字节,读取 8 位可能会导致对慢速内存进行一次或多次 64 位读取,如果您在下一个字节位置之后立即执行第二次读取并且如果该位置位于同一缓存行中,那么它不会出去慢内存,它从缓存中读取,要快得多。依此类推,直到您越过另一个缓存边界或其他读取导致该缓存行被驱逐。如果正在写入的位置在缓存中,则读取-修改-写入发生在缓存中,如果不在缓存中,则取决于系统,写入并不一定意味着读取修改写入会导致缓存行填充,它可能发生在缓存不存在的背面。现在,如果您修改了高速缓存行中的一个字节,那么现在必须写回该行,它根本不能被丢弃,因此您有一个到几个宽度的内存要写回。您的修改速度很快,但最终写入发生在慢速内存上,这会影响整体性能。

您可能会遇到进行(字节)读取的情况,如果缓存线大于外部存储器宽度,则可以使读取速度比缓存不存在时更慢,但是随后您对该缓存线中的某些项目进行字节写入并且这很快,因为它在缓存中。因此,您可能会进行实验以显示写入速度更快。

一个痛苦的情况是读取 16 位未对齐,这样它们不仅跨越了 64 位内存宽度边界,而且跨越了缓存线边界,因此必须读取两条缓存线,而不是读取可能的 128 位意味着必须读取 256 或 512 或 1024 位才能获得 16 位。

例如,您计算机上的记忆棒实际上是多个存储器,例如可能是 8 个 8 位宽以形成 64 位整体宽度或 16 个 4 位宽以形成整体 64 位宽度等。这并不意味着您可以将写入隔离在一个车道,但也许,我不太了解这些模块,但有些系统可以/可以这样做,但我认为这些系统是 8 位或 4 位宽,就最小可寻址大小而言,而不是 64 位这个讨论进行。然而,ECC 让事情变得更糟。首先,您需要一个额外的内存芯片或更多,例如基本上更多宽度的 72 位以支持 64。您必须使用 ECC 进行完整写入,因为整个 72 位可以说必须进行自我检查,因此您不能进行分数。如果存在可纠正的(一位)错误,则读取不会受到真正的惩罚,它会获得纠正后的 64 位(发生此检查的路径中的某个位置)。理想情况下,您希望系统写回正确的值,但这并不是所有系统的工作方式,因此读取可能会变成读取修改写入,无论是否对齐。主要的惩罚是,如果您能够进行小数写入,那么您现在无法使用 ECC 进行全宽写入。

现在我的问题是,假设您使用 memcpy 来移动这些数据,许多 C 库都经过调整以进行对齐传输,至少在可能的情况下,如果源和目标以不同的方式未对齐,这可能是不好的,您可能想要自己管理部分副本。假设它们以相同的方式未对齐,memcpy 将首先尝试复制未对齐的字节,直到它到达对齐的边界,然后它切换到高速档,复制对齐的块直到它接近末尾,它降档并复制最后一个几个字节,如果有的话,以未对齐的方式。因此,如果您正在谈论的这个内存副本是数千字节,并且唯一未对齐的东西靠近末端,那么是的,它会花费您一些额外的读取,多达两个额外的缓存行填充,但这可能是噪音。

纯粹的传统的、非缓存的内存视图,所有其他的东西都保持不变,就像 Doug 写的那样。跨这些边界之一的未对齐读取,例如跨两个 64 位字的 16 位,会花费您额外的读取 2R 与 1R。类似的写入花费您 2R+2W 与 1W 相比,要贵得多。缓存和其他事情只会使问题变得非常复杂,从而使答案“视情况而定”......您需要非常了解您的系统以及周围发生的其他事情(如果有的话)。缓存有帮助,也有伤害,对于任何缓存,都可以设计一个测试来显示缓存使事情变慢,而对于相同的系统,可以编写一个测试来显示缓存使事情变得更快。

进一步阅读将是查看数据手册/表格技术参考手册或供应商为各种事情调用他们的文档的任何内容。对于 ARM,获取有关其总线的 AXI/AMBA 文档,获取其缓存的缓存文档(例如 PL310)。关于 ddr 内存的信息,您插入计算机的模块中使用的各个芯片都在那里,很多时序图等(注意只是因为您认为您正在购买千兆赫内存,您不是,dram 并没有变得更快像 10 年或更长时间,它在 133Mhz 左右相当慢,只是总线更快并且可以排队更多传输,一个 ddr 内存周期仍然需要数百到数千个处理器周期,读取一个错过所有缓存的字节你的处理器等待永恒)。

于 2013-06-11T00:22:18.800 回答