X86 与CPU微体系结构相关的奇怪现象
我正在做一个测试,通过指针测量一个函数调用的开销,下面是我的。然而,我发现了一些非常奇怪的事情,并寻求你的帮助 代码在VS2017的发布模式下编译,使用默认配置 共有4个测试平台,所有操作系统均为Win10。以下是一些详细信息:X86 与CPU微体系结构相关的奇怪现象,x86,cpu,performance-testing,intel,cpu-architecture,X86,Cpu,Performance Testing,Intel,Cpu Architecture,我正在做一个测试,通过指针测量一个函数调用的开销,下面是我的。然而,我发现了一些非常奇怪的事情,并寻求你的帮助 代码在VS2017的发布模式下编译,使用默认配置 共有4个测试平台,所有操作系统均为Win10。以下是一些详细信息: M1:CPU:i7-7700,微体系结构:卡比湖 M2:CPU:i7-7700,微体系结构:卡比湖 M3:CPU:i7-4790,微体系结构:Haswell M4:CPU:E5-2698 v3,微体系结构:Haswell 在下图中,图例的形式为机器参数\u顺序别名机
- M1:CPU:i7-7700,微体系结构:卡比湖
- M2:CPU:i7-7700,微体系结构:卡比湖
- M3:CPU:i7-4790,微体系结构:Haswell
- M4:CPU:E5-2698 v3,微体系结构:Haswell
机器参数\u顺序别名
<上面列出了代码>机器<代码>参数\u顺序描述在一次运行期间传递给程序的循环
的顺序<代码>别名表示该时间属于哪一部分no exec
表示没有函数调用部分,也称为。第98-108行exec
表示调用函数部分,也称为。第115-125行per exec
是函数调用的成本。所有的时间单位都是毫秒<代码>每执行指的是左y轴,而其他代码则指的是右y轴
比较图1-4,您可以看到该图可能与CPU的微体系结构有关(M1和M2相似,M3和M4相似)
我的问题:
LOOP<25
和LOOP>100
)32时,所有的无执行时间都有一个奇怪的峰值
为什么所有机器都有两个阶段(循环<25和循环>100)
当最里面的循环(j=0;j
停止正确预测其退出时,会出现第一个不连续。在我的机器上,当循环
达到24次迭代时,就会发生这种情况
通过使用perf stat-I3000
将基准测试输出与perf stats交错,您可以非常清楚地看到这一点:
BenchWithFixture/RandomTarget/21 727779 ns 727224 ns 3851 78.6834M items/s
45.003283831 2998.636997 task-clock (msec)
45.003283831 118 context-switches # 0.039 K/sec
45.003283831 0 cpu-migrations # 0.000 K/sec
45.003283831 0 page-faults # 0.000 K/sec
45.003283831 7,777,209,518 cycles # 2.595 GHz
45.003283831 26,846,680,371 instructions # 3.45 insn per cycle
45.003283831 6,711,087,432 branches # 2238.882 M/sec
45.003283831 1,962,643 branch-misses # 0.03% of all branches
BenchWithFixture/RandomTarget/22 751421 ns 750758 ns 3731 76.2169M items/s
48.003487573 2998.943341 task-clock (msec)
48.003487573 111 context-switches # 0.037 K/sec
48.003487573 0 cpu-migrations # 0.000 K/sec
48.003487573 0 page-faults # 0.000 K/sec
48.003487573 7,778,285,186 cycles # 2.595 GHz
48.003487573 26,956,175,646 instructions # 3.47 insn per cycle
48.003487573 6,738,461,171 branches # 2247.947 M/sec
48.003487573 1,973,024 branch-misses # 0.03% of all branches
BenchWithFixture/RandomTarget/23 774490 ns 773955 ns 3620 73.9325M items/s
51.003697814 2999.024360 task-clock (msec)
51.003697814 105 context-switches # 0.035 K/sec
51.003697814 0 cpu-migrations # 0.000 K/sec
51.003697814 0 page-faults # 0.000 K/sec
51.003697814 7,778,570,598 cycles # 2.595 GHz
51.003697814 21,547,027,451 instructions # 2.77 insn per cycle
51.003697814 5,386,175,806 branches # 1796.776 M/sec
51.003697814 72,207,066 branch-misses # 1.12% of all branches
BenchWithFixture/RandomTarget/24 1138919 ns 1138088 ns 2461 50.2777M items/s
57.004129981 2999.003582 task-clock (msec)
57.004129981 108 context-switches # 0.036 K/sec
57.004129981 0 cpu-migrations # 0.000 K/sec
57.004129981 0 page-faults # 0.000 K/sec
57.004129981 7,778,509,575 cycles # 2.595 GHz
57.004129981 19,061,717,197 instructions # 2.45 insn per cycle
57.004129981 4,765,017,648 branches # 1589.492 M/sec
57.004129981 103,398,285 branch-misses # 1.65% of all branches
BenchWithFixture/RandomTarget/25 1171572 ns 1170748 ns 2391 48.8751M items/s
60.004325775 2998.547350 task-clock (msec)
60.004325775 111 context-switches # 0.037 K/sec
60.004325775 0 cpu-migrations # 0.000 K/sec
60.004325775 0 page-faults # 0.000 K/sec
60.004325775 7,777,298,382 cycles # 2.594 GHz
60.004325775 17,008,954,992 instructions # 2.19 insn per cycle
60.004325775 4,251,656,734 branches # 1418.230 M/sec
60.004325775 131,311,948 branch-misses # 2.13% of all branches
在过渡之前,分支预测失误率约为0.03%,然后在基准速度放缓或增加两个数量级时,分支预测失误率会跃升到2.13%左右。预测失误率实际上比您预期的要低一点:有25个分支(外加几个外部循环),您预期的预测失误率约为1/25==4%
mispredicts,但我们没有看到,不确定原因
在我的机器上,第一个循环(只有池[0](0,0,0,0,0)
调用)与您的一样,在~24次循环
迭代时没有转换,但我不清楚原因。我的经验是,TAGE计数器通常不能处理超过24ish周期的常量迭代循环,但是这里可能与间接分支预测器有一些交互作用。这很有趣
为什么所有的无执行时间都有一个奇怪的峰值,当你说你想通过指针测量一个函数调用的成本时,但我不明白你是怎么做到的。您所说的
无功能调用部分
和功能调用部分
是什么意思?两者似乎都包含函数调用。这部分(d2-d1)/(1.0*规则*ITER)
计算了什么?左y轴怎么可能是负的?循环的要点是什么?M1和M2之间的区别是什么?LOOP<25
是现代Intel CPU可以正确预测短循环的循环退出分支的地方。服用20次后不服用一次比服用24次后不服用一次更容易。在我对简单嵌套循环的测试中,我发现内部的22到23次迭代(外部有数百万次迭代)是完美分支预测的截止点。你的文章似乎与代码不一致。你说“no exec意味着没有函数调用部分,也就是第98-108行。exec意味着调用函数部分,也就是第115-125行。”但就我所见,这两个定时循环要么涉及到一个函数调用(第一个循环)到固定位置池[0]
,要么涉及到两个函数调用,一个调用池[0]
,另一个调用池[t]
对于t=1,2,3。。。等等。你说“perexec”是一个函数调用的值,所以它是另外两个测试中哪一个的循环规范化值?顺便说一句,我的印象是OP试图做的是测量一个(可能是随机的)间接调用的时间,该调用由一些其他工作参数化,可以与调用交错运行。我认为使用xor的关联性和交换性,编译器实际上可以将整个xor循环从所有循环中移出并计算一次,如果它知道循环没有逃逸函数的话。在我的测试中,它没有逃逸,但在OPs中,由于scanf,它逃逸了。不过编译器并没有那么聪明。@BeeOnRope:哦,对了,不是常数。但是对于加法来说,它并不像n*(n-1)/2那样简单,但只有9条指令长(),如果编译器花时间寻找这种非常罕见的模式,它将值得使用。(他们没有,也可能不应该!)
BenchWithFixture/RandomTarget/21 727779 ns 727224 ns 3851 78.6834M items/s
45.003283831 2998.636997 task-clock (msec)
45.003283831 118 context-switches # 0.039 K/sec
45.003283831 0 cpu-migrations # 0.000 K/sec
45.003283831 0 page-faults # 0.000 K/sec
45.003283831 7,777,209,518 cycles # 2.595 GHz
45.003283831 26,846,680,371 instructions # 3.45 insn per cycle
45.003283831 6,711,087,432 branches # 2238.882 M/sec
45.003283831 1,962,643 branch-misses # 0.03% of all branches
BenchWithFixture/RandomTarget/22 751421 ns 750758 ns 3731 76.2169M items/s
48.003487573 2998.943341 task-clock (msec)
48.003487573 111 context-switches # 0.037 K/sec
48.003487573 0 cpu-migrations # 0.000 K/sec
48.003487573 0 page-faults # 0.000 K/sec
48.003487573 7,778,285,186 cycles # 2.595 GHz
48.003487573 26,956,175,646 instructions # 3.47 insn per cycle
48.003487573 6,738,461,171 branches # 2247.947 M/sec
48.003487573 1,973,024 branch-misses # 0.03% of all branches
BenchWithFixture/RandomTarget/23 774490 ns 773955 ns 3620 73.9325M items/s
51.003697814 2999.024360 task-clock (msec)
51.003697814 105 context-switches # 0.035 K/sec
51.003697814 0 cpu-migrations # 0.000 K/sec
51.003697814 0 page-faults # 0.000 K/sec
51.003697814 7,778,570,598 cycles # 2.595 GHz
51.003697814 21,547,027,451 instructions # 2.77 insn per cycle
51.003697814 5,386,175,806 branches # 1796.776 M/sec
51.003697814 72,207,066 branch-misses # 1.12% of all branches
BenchWithFixture/RandomTarget/24 1138919 ns 1138088 ns 2461 50.2777M items/s
57.004129981 2999.003582 task-clock (msec)
57.004129981 108 context-switches # 0.036 K/sec
57.004129981 0 cpu-migrations # 0.000 K/sec
57.004129981 0 page-faults # 0.000 K/sec
57.004129981 7,778,509,575 cycles # 2.595 GHz
57.004129981 19,061,717,197 instructions # 2.45 insn per cycle
57.004129981 4,765,017,648 branches # 1589.492 M/sec
57.004129981 103,398,285 branch-misses # 1.65% of all branches
BenchWithFixture/RandomTarget/25 1171572 ns 1170748 ns 2391 48.8751M items/s
60.004325775 2998.547350 task-clock (msec)
60.004325775 111 context-switches # 0.037 K/sec
60.004325775 0 cpu-migrations # 0.000 K/sec
60.004325775 0 page-faults # 0.000 K/sec
60.004325775 7,777,298,382 cycles # 2.594 GHz
60.004325775 17,008,954,992 instructions # 2.19 insn per cycle
60.004325775 4,251,656,734 branches # 1418.230 M/sec
60.004325775 131,311,948 branch-misses # 2.13% of all branches