在特定大小的阵列上运行时,计算时间达到峰值 我做了一个程序,分配了两组C++双打。第一个包含从0到pi/4的x的sin(x)值。对于第二个,我写下sin(x)的二阶导数,使用三点公式计算: (vals[i-1]-2*vals[i]+vals[i+1])/(dx*dx)

在特定大小的阵列上运行时,计算时间达到峰值 我做了一个程序,分配了两组C++双打。第一个包含从0到pi/4的x的sin(x)值。对于第二个,我写下sin(x)的二阶导数,使用三点公式计算: (vals[i-1]-2*vals[i]+vals[i+1])/(dx*dx),c++,arrays,C++,Arrays,我对不同大小的数组执行这些计算,每次重复几次,然后输出计算的平均时间。这样我就得到了一个很好的图表,显示了计算特定大小数组的导数所需的时间 这听起来很简单,但我遇到了一个奇怪的问题。请看图表: 当数组很小时,不会发生奇怪的事情。然而,当它们大于10000个元素时(两个数组意味着大约16kB和160kB的内存),我会得到周期性的峰值,每个峰值大约在前一个数组之后512个元素。这种情况会发生在多个CPU上(AMD Sempron 3850、英特尔Q9400和英特尔至强5310),而不会发生在其他CP

我对不同大小的数组执行这些计算,每次重复几次,然后输出计算的平均时间。这样我就得到了一个很好的图表,显示了计算特定大小数组的导数所需的时间

这听起来很简单,但我遇到了一个奇怪的问题。请看图表: 当数组很小时,不会发生奇怪的事情。然而,当它们大于10000个元素时(两个数组意味着大约16kB和160kB的内存),我会得到周期性的峰值,每个峰值大约在前一个数组之后512个元素。这种情况会发生在多个CPU上(AMD Sempron 3850、英特尔Q9400和英特尔至强5310),而不会发生在其他CPU上(英特尔i5 5200U、英特尔Nehalem-C)

如果这还不够的话,当阵列达到大约65000个元素时,Windows7上的AMD3850的时间会突然增加。Debian上不会发生这种情况

我认为这可能与CPU缓存(考虑到“神奇”的元素数量)以及操作系统的调度有关,但我想不出任何具体的解释和方法来证实这一点(除了分析CPU缓存命中率)

代码:

附加信息:对于小于10000个元素的数组,两个数组始终位于同一地址。对于较大的阵列,第二个阵列不断移动。这可能是我没有得到小尺寸峰值的原因

此外,正如@geza所建议的,我还玩过“perf stat”。对于5000到12930的尺寸运行时的第一个输出:

 Performance counter stats for './a.out':

      70733.635707      task-clock (msec)         #    0.999 CPUs utilized          
             6,008      context-switches          #    0.085 K/sec                  
                 3      cpu-migrations            #    0.000 K/sec                  
             3,866      page-faults               #    0.055 K/sec                  
    90,800,323,159      cycles                    #    1.284 GHz                      (50.00%)
                 0      stalled-cycles-frontend                                       (50.01%)
                 0      stalled-cycles-backend    #    0.00% backend cycles idle      (50.01%)
   160,806,960,842      instructions              #    1.77  insn per cycle                                              (50.00%)
    16,118,385,897      branches                  #  227.874 M/sec                    (50.00%)
         5,818,878      branch-misses             #    0.04% of all branches          (49.99%)

      70.839631335 seconds time elapsed
…对于12940到30000的尺寸:

 Performance counter stats for './a.out':

     157468.056544      task-clock (msec)         #    0.999 CPUs utilized          
            13,420      context-switches          #    0.085 K/sec                  
                10      cpu-migrations            #    0.000 K/sec                  
            32,271      page-faults               #    0.205 K/sec                  
   201,597,569,555      cycles                    #    1.280 GHz                      (50.00%)
                 0      stalled-cycles-frontend                                       (50.00%)
                 0      stalled-cycles-backend    #    0.00% backend cycles idle      (50.00%)
   351,405,781,351      instructions              #    1.74  insn per cycle                                              (50.00%)
    35,289,858,166      branches                  #  224.108 M/sec                    (50.00%)
        10,494,566      branch-misses             #    0.03% of all branches          (50.00%)

     157.692170504 seconds time elapsed
程序运行的时间是原来的两倍,所以上下文切换的次数是原来的两倍也就不足为奇了。然而,在这个范围内,我得到了更多的页面错误

另一件有趣的事情是,当我重新启动程序进行第二次运行时,第二个数组并没有在内存中移动太多。似乎当程序运行一段时间后,它就开始这样做了


我测试的时间越长,我知道的就越少

考虑到
double
的通常大小是8字节,那么10000个
double
元素将是80000字节,而不是16KiB。你差了五倍。但这并不重要,对任何远程现代和非嵌入式的东西都不重要,因为它仍然是沧海一粟。FWIW,10752个元素==86016 B==21页。我不确定具体数字的相关性,但我想这是一个线索。鉴于峰值都是页面大小的精确倍数,我怀疑这两个数组的病理性别名是原因。唯一真正的谜团是为什么这种行为在10752以下被掩盖。它与地址对齐有关。若我将地址和页面大小对齐,那个么我会产生一些奇怪的行为。不完全是你的,但有些不规则。它线性增长到8692(时间:4.4微秒),然后跳到6微秒直到8830,然后回落到4.4微秒。其他尺寸也有不规则。(也许这两个数组不需要页面对齐,但在一个页面中具有相同的对齐方式?@cruetrain-好的,这就解释了这一点。总的来说,这种行为是由内存分配算法的突发奇想控制的(大概与硬件无关)。超过10k时,每当数组与页面完全对齐时,就会看到病理性别名。
 Performance counter stats for './a.out':

      70733.635707      task-clock (msec)         #    0.999 CPUs utilized          
             6,008      context-switches          #    0.085 K/sec                  
                 3      cpu-migrations            #    0.000 K/sec                  
             3,866      page-faults               #    0.055 K/sec                  
    90,800,323,159      cycles                    #    1.284 GHz                      (50.00%)
                 0      stalled-cycles-frontend                                       (50.01%)
                 0      stalled-cycles-backend    #    0.00% backend cycles idle      (50.01%)
   160,806,960,842      instructions              #    1.77  insn per cycle                                              (50.00%)
    16,118,385,897      branches                  #  227.874 M/sec                    (50.00%)
         5,818,878      branch-misses             #    0.04% of all branches          (49.99%)

      70.839631335 seconds time elapsed
 Performance counter stats for './a.out':

     157468.056544      task-clock (msec)         #    0.999 CPUs utilized          
            13,420      context-switches          #    0.085 K/sec                  
                10      cpu-migrations            #    0.000 K/sec                  
            32,271      page-faults               #    0.205 K/sec                  
   201,597,569,555      cycles                    #    1.280 GHz                      (50.00%)
                 0      stalled-cycles-frontend                                       (50.00%)
                 0      stalled-cycles-backend    #    0.00% backend cycles idle      (50.00%)
   351,405,781,351      instructions              #    1.74  insn per cycle                                              (50.00%)
    35,289,858,166      branches                  #  224.108 M/sec                    (50.00%)
        10,494,566      branch-misses             #    0.03% of all branches          (50.00%)

     157.692170504 seconds time elapsed