【问题标题】:What is the most efficient way to loop over an array? (c++)遍历数组的最有效方法是什么? (c++)
【发布时间】:2019-07-26 03:30:21
【问题描述】:

这是一个愚蠢的问题,但它一直困扰着我,我无法通过谷歌解决它。

考虑以下数组:

struct SomeDataStruct
{
    uint64_t ValueOne;
    uint64_t ValueTwo;
    uint64_t ValueThree;
};

SomeDataStruct _veryLargeArray[1024];

现在,哪些方法可以更快地遍历每个元素并对每个元素进行处理?

方法一:

for (int i = 0; i < 1024; ++i)
{
    _veryLargeArray[i].ValueOne += 1;
    _veryLargeArray[i].ValueTwo += 1;
    _veryLargeArray[i].ValueThree = _veryLargeArray[i].ValueOne + _veryLargeArray[i].ValueTwo;
}

方法二:

SomeDataStruct * pEndOfStruct = &(_veryLargeArray[1024]);

for (SomeDataStruct * ptr = _veryLargeArray; ptr != pEndOfStruct; ptr += 1)
{
    ptr->ValueOne += 1;
    ptr->ValueTwo += 1;
    ptr->ValueThree = ptr->ValueOne + ptr->ValueTwo;
}

我知道这个问题表面上看起来真的很愚蠢,但我想知道的是编译器是否对每种给定的实现 for 循环的方式做了一些聪明/特殊的事情?在第一种情况下,如果编译器实际上每次都查找 BaseArrayPointer + Offset 可能会占用大量内存,但如果编译器足够聪明,如果会用整个数组填充 L2 缓存并处理 {} 之间的代码正确。

如果编译器每次都解析指针,则第二种方法可以解决,但可能会使编译器很难确定是否可以将整个数组复制到 L2 缓存中并在那里遍历。

很抱歉提出这么愚蠢的问题,我在学习 c++ 时获得了很多乐趣,并且已经开始做你想太多的事情。只是好奇是否有人知道是否有“明确”的答案。

【问题讨论】:

  • 你问的是什么平台?在主要的桌面平台上,“将数组复制到 L2”不是编译器的工作,而是有 MMU 硬件。
  • 在某些情况下,编译器甚至可能为两个循环生成相同的代码。您不应该注意到任何性能差异。此外,为了可读性,我建议您对这些选项中的任何一个使用范围 for 循环。
  • 每个编译器都有自己的一组优化。在架构上为编译器获得明确答案的唯一方法是分析代码。 (好吧,如果循环恰好编译成相同的机器代码,我猜你不必实际运行它就知道它们具有相同的性能。)
  • 在这种情况下使用结构或数组将更容易对代码进行矢量化

标签: c++ arrays for-loop optimization


【解决方案1】:

除非您想查看中间汇编语言输出并分析 CPU 的缓存行为,否则您能够回答此问题的唯一方法是分析代码。运行它,数百或数千次,看看需要多长时间。

如果您想要最快的代码,请编写最简单、最明显的版本,然后将其留给优化编译器。如果您尝试花哨,使用这样的循环,您可能会混淆编译器并且无法优化。

我看到一个简单的 C 循环编译比手动编码的程序集更快,而一个手动优化的 C 版本最终比手动编码的程序集慢。

另一方面,了解一些关于缓存的知识以及幕后发生的事情是值得的。但通常,这发生在您发现您的代码不够快之后。否则会导致过早优化,即root of all evil

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-07
  • 2021-07-16
相关资源
最近更新 更多