数组访问/写入性能差异?

时间:2011-07-17 18:20:03

标签: arrays performance algorithm

这可能与语言有关,但一般来说,访问和写入数组之间的性能差异是什么?

例如,如果我正在尝试编写prime sieve并将该素数表示为布尔数组。

找到素数后,我可以说

for(int i = 2; n * i < end; i++)
{
    prime[n * i] = false;
}

for(int i = 2; n * i < end; i++)
{
    if(prime[n * i])
    {
        prime[n * i] = false;
    }
}

后一种情况的意图是在写入之前检查该值,以避免重写已经检查过的许多值。这里的性能是否有任何实际的好处,或者访问和写入的速度大致相当?

3 个答案:

答案 0 :(得分:3)

在没有运行的机器/操作系统的细节的情况下无法回答这样的通用问题,但一般来说后者会变慢,因为:

  1. 第二个例子,您必须从RAM获取值到L2 / L1缓存并将其读取到寄存器,使值有机会并将其写回。在第一种情况下,您可以简单地将值写入L1 / L2缓存。它可以在程序执行其他操作时从缓存写入RAM。

  2. 第二种形式每次迭代执行的代码要多得多。对于足够多的迭代次数,差异变得非常快。

答案 1 :(得分:2)

一般来说,这更多地取决于机器而不是编程语言。写入通常会花费更多的时钟周期,因为根据机器的不同,需要在内存中更新更多的缓存值。

然而,你的第二段代码将变得更慢,这不仅仅是因为“更多代码”。最大的原因是,无论何时在大多数机器上使用if语句,CPU都会使用分支预测器。 CPU确实预测if语句将提前运行的方式,如果它是错误的,它必须回溯。请参阅http://en.wikipedia.org/wiki/Pipeline_%28computing%29http://en.wikipedia.org/wiki/Branch_predictor以了解原因。

如果您想进行一些优化,我建议您使用以下内容:

  • 资料!看看真正占用时间的是什么。
  • 乘法比添加困难得多。尝试重写循环,使i + = n,并将其用于数组索引。
  • 循环条件“应该”在每次迭代时完全重新评估,除非编译器将其优化掉。所以尽量避免乘法。
  • 使用-O2或-O3作为编译器选项
  • 由于缓存局部性,您可能会发现某些n值比其他值快。您可能会想到一些巧妙的方法来重写代码以利用它。
  • 反汇编代码并查看它在处理器上的实际操作

答案 2 :(得分:0)

这是一个难题,它在很大程度上取决于您的硬件,操作系统和编译器。但是为了理论,你应该考虑两件事:分支和内存访问。由于分支通常是邪恶的,你想要避免它。如果发生了一些编译器优化并且你的第二个片段将被缩减为第一个片段(编译器喜欢避免分支,他们可能认为它是一个爱好,但他们有一个原因)我甚至不会感到惊讶。所以在这些术语中,第一个例子更清晰,更容易处理。

还有CPU缓存和其他与内存相关的问题。我相信在两个示例中,您必须实际将内存加载到CPU缓存中,因此您可以读取它或更新。虽然阅读不是问题,但写作必须传播变化。如果你在单个线程中使用该函数,我不会担心(正如@gby指出的那样,OS可以稍后推动更改)。

我只能提出一个方案,这会让我从第二个例子中考虑解决方案。如果我在线程之间共享表以并行处理它(没有锁定)并且为不同的CPU分配了单独的缓存。然后,每次从一个线程修改缓存行时,另一个线程必须在读取或写入同一个内存块之前更新它的副本。它被称为缓存一致性,它实际上可能会严重损害您的性能;在这种情况下,我可以考虑条件写入。但是等等,这可能远离你的问题......