首页 > 解决方案 > 为什么这个间接调用(调用 rax)平均需要超过 100 个周期?

问题描述

编辑:这个问题是一个错误。我误读了在子例程中花费的时间,并认为该例程中的语句占用了总时间的 15%,而不是实际的 3%。

由于答案的高质量,我将问题留在这里。令我惊讶的是,即使目的地始终是相同的地址,计算的分支似乎也丢失了,我将使用那里的建议进行调查。

原来的:

这是我的 C 代码中的关键点,带有来自分析器的注释。间接调用占用了几百行循环总时间的三分之一。根据总运行时间,此调用语句必须至少占用 100 个时钟周期。

分析运行

以下是最后 3 个 C 语句的生成代码:

00007FFE066FB5EA  mov         r12,qword ptr [r8+8]  
00007FFE066FB5EE  and         r12d,30000h  
00007FFE066FB5F5  dec         r12  
00007FFE066FB5F8  and         r12,qword ptr [r8+20h]  
00007FFE066FB5FC  sar         r12,3Fh  
00007FFE066FB600  and         r12,qword ptr [r8+10h]  
00007FFE066FB604  lea         rbx,[validitymask+60h (07FFE06B058E0h)]  
00007FFE066FB60B  cmove       r12,rbx  
       A *tpopa=AZAPLOC(arg1); tpopa=(A*)((I)tpopa&REPSGN(AC(arg1)&((AFLAG(arg1)&(AFVIRTUAL|AFUNINCORPABLE))-1))); tpopa=tpopa?tpopa:ZAPLOC0; tpopw=(pline&2)?tpopw:tpopa; // monad: w fs  dyad: a w   if monad, change to w w  
00007FFE066FB60F  mov         rsi,qword ptr [rdx+8]  
00007FFE066FB613  and         esi,30000h  
00007FFE066FB619  dec         rsi  
00007FFE066FB61C  and         rsi,qword ptr [rdx+20h]  
00007FFE066FB620  sar         rsi,3Fh  
00007FFE066FB624  and         rsi,qword ptr [rdx+10h]  
00007FFE066FB628  cmove       rsi,rbx  
00007FFE066FB62C  test        r15b,2  
00007FFE066FB630  cmove       r12,rsi  
       y=(*actionfn)((J)((I)jt+(REPSGN(SGNIF(pt0ecam,VJTFLGOK1X+(pline>>1)))&(pline|1))),arg1,arg2,fs);   // set bit 0, and bit 1 if dyadic, if inplacing allowed by the verb
00007FFE066FB634  mov         bl,28h  
00007FFE066FB636  sub         bl,cl  
00007FFE066FB638  shlx        rcx,rdi,rbx  
00007FFE066FB63D  sar         rcx,3Fh  
00007FFE066FB641  or          r15d,1  
00007FFE066FB645  and         ecx,r15d  
00007FFE066FB648  add         rcx,r10  
00007FFE066FB64B  vzeroupper  
00007FFE066FB64E  call        rax  

正如你所看到的,除了调用 rax之外,有问题的语句并没有什么奇异之处。我希望该调用会创建可能 15 个时钟的管道中断,但与我所看到的完全不同。事实上,这段代码是循环执行的,而且大多数时候函数地址是相同的,所以我希望大多数间接调用都能正确预测。但这肯定不是我所看到的。

加载分支地址的指令(在所示的第一个 C 语句中)在使用分支地址之前很久就执行了;即,地址已确定。

为什么这个语句/指令这么慢?

第二个问题:出于分析目的,该语句何时结束?如果调用 rax需要管道中断,那么在中断期间发生的所有中断是否都计入调用 rax?这是有道理的,但其他指令仍在执行,对吧?

标签: performanceassemblyprofilingx86-64

解决方案


推荐阅读