【问题标题】:Does random indexing an array have any performance implications over sequential indexing?随机索引数组是否对顺序索引有任何性能影响?
【发布时间】:2021-01-16 13:11:07
【问题描述】:

假设我们有一个名为DataList 的长数组。我们还有另外两个包含索引的数组,一个包含以升序排列的索引(即:0, 1, 2, 3, 4, ...),命名为sIndexes,另一个数组由随机的索引组成打包(即:6、5、1、9、7,...)命名为 rIndexes
这些索引数组(sIndexesrIndexes)用于索引DataList 数组。当我们使用sIndex 数组来索引DataList 时,它会按顺序索引元素。当我们使用rIndexes 来索引DataList 时,它会在数组中的随机位置进行索引。

所以我的问题是,
使用随机索引而不是顺序索引时是否存在任何性能差异?缓存未命中是否会导致性能损失(例如,如果索引指向缓存行中不可用的位置)?

【问题讨论】:

  • “缓存未命中是否会导致性能损失” - 缓存未命中会减慢速度,因此理论上,如果证明缓存未命中肯定会发生,“随机索引”应该更慢。然而,真的有那么重要吗?如果有的话,您可能会受到 2 微秒的性能损失。
  • 嗯,是的,如果数据不在缓存中,加载它需要更多时间。所以顺序排序应该更有效。但这也取决于DataList 中元素的大小 - 如果它们大约是缓存线(64 字节)或更多的大小,或者如果索引足够稀疏因此没有交集,那么不应该有有很大的不同。然后再次测量它以查看性能差异。

标签: c++ arrays performance


【解决方案1】:

线性访问要快得多,因为:

  • 从 RAM 中获取的最小大小是一个高速缓存行(通常为 64 字节)。如果您只使用其中的一个字节,那么您就是在浪费宝贵的内存带宽。
  • 现代 CPU 可以检测常规访问模式,并会从 RAM 中预取数据,因此在您访问它之前它就会被缓存。 (或者至少它已经以最大内存带宽进行流式传输。)
  • 如果在编译时检测到线性访问(您的代码可能不是这种情况),则可以将其向量化为 SIMD 指令,同时处理许多项。

随机访问会慢得多,除非您的整个数组适合 L2 缓存,或者您为每个项目做大量工作。 L2 缓存未命中通常非常昂贵。

有关详细信息,我推荐经典的每个程序员都应该知道的关于内存的知识[1],这是一本冗长而引人入胜的读物。从 2007 年开始,但 CPU 架构并没有根本性改变;如果有什么这变得更相关的话。

上述情况适用于现代大型 CPU。存在没有缓存但可访问 SRAM 的小型嵌入式 CPU。在这种情况下,随机索引将执行相同的操作。

[1]https://www.gwern.net/docs/cs/2007-drepper.pdf

【讨论】:

    【解决方案2】:

    “幕后”发生的事情很可能是乘法 (index * sizeof(data)),无论值是什么,所涉及的类型都需要相同的时间。

    正如您自己指出的那样,如果访问的数组部分不在当前缓存行中,实际从数组中获取值可能实际上需要更长的时间。

    对于单线程应用程序,通常希望将数据紧密打包以提高缓存命中率(请参阅std::hardware_constructive_interference_size 背后的想法),但对于多线程应用程序,通常会尝试将元素分开(std::hardware_destructive_interference_size)以避免错误共享并希望让每个线程的缓存线不受其他线程的干扰。

    【讨论】:

    • 不会影响 CPU 缓存吗?就像索引指向的数组位置比缓存线大?
    • @D-RAJ 这是一个公平的问题,是的,这实际上可能会产生影响。我正在删除这个答案。你好像有这个。 :-)
    • 我可能应该将它添加到问题中:3
    • @D-RAJ :-) 这确实使它成为一个更有趣的问题。更新了答案。
    • CPU 具有用于数组索引的专用硬件;这种乘法很可能会与其他一切“免费”同时发生。
    猜你喜欢
    • 2014-12-05
    • 2012-11-16
    • 2021-12-08
    • 2016-02-06
    • 2010-11-30
    • 1970-01-01
    • 2011-02-19
    • 1970-01-01
    • 2019-04-12
    相关资源
    最近更新 更多