【问题标题】:Performance Impact of Nested Vectors vs. Contiguous Arrays嵌套向量与连续数组的性能影响
【发布时间】:2018-01-26 14:53:53
【问题描述】:

是否有任何可靠的测试可以清楚地显示访问和写入嵌套向量与 C++ 的内置数组之间的性能差异?我听说与访问单个数组中的元素(所有元素都存储在连续内存中)相比,使用嵌套(多维)向量通常会产生一些性能开销,但这对我来说似乎都是假设的。我还没有看到任何实际显示这些差异的测试。它们很重要吗?我确信这取决于场景,但作为一个没有经验的程序员,我不太确定这些差异在什么程度上会变得显着。

【问题讨论】:

  • 嵌套vector 的空间局部性差对缓存的性能影响绝对令人震惊。这里有多么痛苦的好例子:blog.codinghorror.com/the-infinite-space-between-words 留意它,但不要过早恐慌。
  • 另一个性能影响是创建:一个大的分配与几个较小的分配。

标签: c++ arrays multidimensional-array vector


【解决方案1】:

嵌套数组和展平数组之间的运行时差异有两个原因:
缓存行为和间接

  • CPU 使用缓存层次结构来避免过于频繁地直接访问 RAM。这利用了大多数内存访问是连续或具有特定时间局部性的事实,即最近访问的内容将很快再次访问。
    这意味着,如果嵌套数组的最里面的数组相当大,如果你以连续的方式迭代值,你会注意到与平面数组的差异很小甚至没有差异。这意味着当迭代一系列值时,对于平面数组,最里面的循环应该遍历连续的元素,对于嵌套数组,最里面的循环应该遍历最里面的数组。
  • 但是,如果您的访问模式是随机的,那么时间上最重要的区别就是间接访问:
    对于平面数组,您可以使用 A[(Z * M + Y) * N + X] 之类的东西,因此您需要执行 4 次算术运算,然后执行一次内存访问。
    对于嵌套数组,你使用A[Z][Y][X],所以实际上有三种相互依赖的内存访问:你需要知道A[Z]才能访问A[Z][Y]等等。由于现代 CPU 的超标量体系结构,可以并行执行的操作特别高效,相互依赖的操作没有那么多。 因此,一方面有一些算术运算和内存负载,另一方面有三个相互依赖的负载,这要慢得多。对于嵌套数组,A 和 A[Z] 的某些值的 Z 的内容可能会在缓存层次结构中找到,但如果嵌套数组足够大,它永远不会完全适合缓存,从而导致多次缓存未命中和内存加载(嵌套),而不是单个缓存未命中和加载(平面)以实现对数组的单次随机访问。

另请参阅his question,尤其是下面的简短答案,以更详细地讨论缓存(我的答案)和间接(彼得的答案),它还提供了一个示例,其中嵌套数组和平面数组之间没有明显差异(之后当然是修复索引错误;))

因此,如果您想知道它们之间是否存在显着的运行时差异,我的答案是:

  • 如果你做随机访问,你肯定会注意到多重间接,从而导致嵌套数组的运行时间更长。

  • 如果您进行连续访问和,请使用正确的循环顺序(最内层循环 = 最内层数组/平面数组的最内层索引)和您的最内层维度多维数组足够大,那么差异可以忽略不计,因为编译器将能够将所有间接移出最内层循环。

【讨论】:

  • 注意N * M * Z + N * Y + X可以重写为(M * Z + Y) * N + X,减少到4个算术运算。
【解决方案2】:

这绝对取决于场景,在某种程度上,我认为不可能以一般方式回答哪种方法最快。最快的方法将是访问模式具有最佳数据局部性的方法——这在很大程度上取决于访问模式以及结构在内存中的布局方式,在嵌套向量的情况下,这取决于分配器并且可能在编译器之间存在很大差异。

我会遵循优化的一般规则,即首先以最直接的方式编写内容,然后在您证明存在瓶颈时尝试优化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-29
    • 1970-01-01
    • 2020-03-15
    • 1970-01-01
    • 2013-07-26
    • 2023-03-30
    • 1970-01-01
    • 2013-04-01
    相关资源
    最近更新 更多