Arrays 阵列访问/写入性能差异?

Arrays 阵列访问/写入性能差异?,arrays,performance,algorithm,Arrays,Performance,Algorithm,这可能与语言有关,但一般来说,访问和写入数组之间的性能差异是什么 例如,如果我试图编写一个数组,并将素数表示为布尔数组 在找到素数后,我可以说 for(int i = 2; n * i < end; i++) { prime[n * i] = false; } for(int i=2;n*i

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

例如,如果我试图编写一个数组,并将素数表示为布尔数组

在找到素数后,我可以说

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

for(int i=2;n*i

后一种情况的目的是在写入之前检查值,以避免重写许多已经检查过的值。在性能方面是否有任何实际的提高,或者访问和写入的速度是否基本相同?

如果没有运行该操作系统的机器/操作系统的细节,就无法回答这样一个一般性的问题,但一般来说,后者会变慢,因为:

  • 第二个例子是,您必须将值从RAM获取到二级/一级缓存,并将其读取到寄存器中,对该值进行冒险,然后将其写回。在第一种情况下,只需向L1/L2缓存中写入一个值,就可以很好地解决问题。稍后,当您的程序执行其他操作时,它可以从缓存写入RAM

  • 第二种形式在每次迭代中要执行的代码要多得多。对于足够多的迭代,差异会很快变大


  • 如果没有运行此操作系统的机器/操作系统的详细信息,就无法回答此类一般性问题,但通常情况下,后者的速度会较慢,因为:

  • 第二个例子是,您必须将值从RAM获取到二级/一级缓存,并将其读取到寄存器中,对该值进行冒险,然后将其写回。在第一种情况下,只需向L1/L2缓存中写入一个值,就可以很好地解决问题。稍后,当您的程序执行其他操作时,它可以从缓存写入RAM

  • 第二种形式在每次迭代中要执行的代码要多得多。对于足够多的迭代,差异会很快变大


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

    然而,您的第二段代码将慢得多,这不仅仅是因为有“更多的代码”。最大的原因是,在大多数机器上使用if语句时,CPU都会使用分支预测器。CPU准确地预测if语句将以何种方式提前运行,如果错误,则必须回溯。看到并理解原因

    如果您想进行一些优化,我建议您:

    • 个人资料!看看到底是什么在占用时间
    • 乘法比加法难得多。尝试重写循环,使i+=n,并将其用于数组索引
    • 循环条件“应该”在每次迭代时完全重新评估,除非编译器将其优化掉。所以试着避免乘法运算
    • 使用-O2或-O3作为编译器选项
    • 您可能会发现,由于缓存位置的原因,n的某些值比其他值快。您可能会想到一些聪明的方法来重写代码以利用这一点
    • 分解代码并查看它在处理器上实际执行的操作

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

    然而,您的第二段代码将慢得多,这不仅仅是因为有“更多的代码”。最大的原因是,在大多数机器上使用if语句时,CPU都会使用分支预测器。CPU准确地预测if语句将以何种方式提前运行,如果错误,则必须回溯。看到并理解原因

    如果您想进行一些优化,我建议您:

    • 个人资料!看看到底是什么在占用时间
    • 乘法比加法难得多。尝试重写循环,使i+=n,并将其用于数组索引
    • 循环条件“应该”在每次迭代时完全重新评估,除非编译器将其优化掉。所以试着避免乘法运算
    • 使用-O2或-O3作为编译器选项
    • 您可能会发现,由于缓存位置的原因,n的某些值比其他值快。您可能会想到一些聪明的方法来重写代码以利用这一点
    • 分解代码并查看它在处理器上实际执行的操作

    这是一个很难回答的问题,很大程度上取决于您的硬件、操作系统和编译器。但是为了理论,你应该考虑两个问题:分支和内存访问。由于分支通常是邪恶的,所以您希望避免它。我甚至不会感到惊讶,如果一些编译器优化发生,你的第二个片段将被减少到第一个(编译器爱避免分支,他们可能认为它是一种爱好,但他们有理由)。因此,从这些角度来看,第一个例子更清晰,更容易处理

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

    只有一个场景我能想出,这会让我从第二个例子中考虑解决方案。如果我在线程之间共享表以并行处理它(不锁定),并为不同的CPU提供单独的缓存。然后,每次修改一个线程的缓存线时,另一个线程必须
    for(int i = 2; n * i < end; i++)
    {
        if(prime[n * i])
        {
            prime[n * i] = false;
        }
    }