在特定大小的阵列上运行时,计算时间达到峰值 我做了一个程序,分配了两组C++双打。第一个包含从0到pi/4的x的sin(x)值。对于第二个,我写下sin(x)的二阶导数,使用三点公式计算: (vals[i-1]-2*vals[i]+vals[i+1])/(dx*dx)
我对不同大小的数组执行这些计算,每次重复几次,然后输出计算的平均时间。这样我就得到了一个很好的图表,显示了计算特定大小数组的导数所需的时间 这听起来很简单,但我遇到了一个奇怪的问题。请看图表: 当数组很小时,不会发生奇怪的事情。然而,当它们大于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的尺寸运行时的第一个输出:在特定大小的阵列上运行时,计算时间达到峰值 我做了一个程序,分配了两组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
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