两条相似行的CPU时间差异

utr*_*rbc 5 c c++ assembly profiling cpu-usage

有一个在我的节目,其中一个while循环IterZNext,IterZ是指向列表中的节点.列表中的节点是struct类型,带有一个名为"Index"的字段.

double xx = 20.0;
double yy = 10000.0;
double zz;      
while (IterZNext!=NULL && NextIndex<=NewIndex)
{
    IterZ=IterZNext;
    IterZNext = IterZ->Next;
    if (IterZNext!=NULL)
    {
        zz = xx + yy;
                NextIndex1 = IterZNext->Index; // line (*)
        NextIndex = IterZNext->Index;  // line (**)
        IterZNext->Index;
    }
}
Run Code Online (Sandbox Code Playgroud)

当我描述我的程序时,我找到了行(*)

NextIndex1 = IterZNext->Index;
Run Code Online (Sandbox Code Playgroud)

消耗大部分CPU时间(2.193s),而行(**)

NextIndex = IterZNext->Index;
Run Code Online (Sandbox Code Playgroud)

与行(*)最相似的只使用0.093s.我使用英特尔VTune放大器来查看这两条线的组装,如下所示:

Address Line    Assembly                   CPU Time Instructions Retired
Line (*):
0x1666  561 mov eax, dword ptr [ebp-0x44]   0.015s  50,000,000
0x1669  561 mov ecx, dword ptr [eax+0x8]        
0x166c  561 mov dword ptr [ebp-0x68], ecx   2.178s  1,614,000,000

Line (**):
0x166f  562 mov byte ptr [ebp-0x155], 0x1   0.039s  80,000,000
0x1676  562 mov eax, dword ptr [ebp-0x44]   0.027s  44,000,000
0x1679  562 mov ecx, dword ptr [eax+0x8]        
0x167c  562 mov dword ptr [ebp-0x5c], ecx   0.026s  94,000,000
Run Code Online (Sandbox Code Playgroud)

如果我改变了line()和line(*)的顺序,那么程序将改为

double xx = 20.0;
double yy = 10000.0;
double zz;      
while (IterZNext!=NULL && NextIndex<=NewIndex)
{
    IterZ=IterZNext;
    IterZNext = IterZ->Next;
    if (IterZNext!=NULL)
    {
        zz = xx + yy;
                NextIndex = IterZNext->Index;  // line (**)
                NextIndex1 = IterZNext->Index; // line (*)
        IterZNext->Index;
    }
}
Run Code Online (Sandbox Code Playgroud)

并且程序集的结果更改为

Address Line    Assembly    CPU Time    Instructions Retired
Line (**):
0x1666  560 mov byte ptr [ebp-0x155], 0x1   0.044s  84,000,000
0x166d  560 mov eax, dword ptr [ebp-0x44]   0.006s  2,000,000
0x1670  560 mov ecx, dword ptr [eax+0x8]    0.001s  4,000,000
0x1673  560 mov dword ptr [ebp-0x5c], ecx   1.193s  1,536,000,000

Line (*):
0x1676  561 mov eax, dword ptr [ebp-0x44]   0.052s  128,000,000
0x1679  561 mov ecx, dword ptr [eax+0x8]        
0x167c  561 mov dword ptr [ebp-0x68], ecx   0.034s  112,000,000
Run Code Online (Sandbox Code Playgroud)

在这种情况下,line(*)使用大部分CPU时间(1.245s),而line()仅使用0.086s.

有人能告诉我:(1)为什么要做第一次任务需要这么长时间?请注意,行zz = xx + yy仅使用0.058s.这与缓存未命中有关吗?因为列表中的所有节点都是动态生成的.(2)为什么这两行之间的CPU时间差异很大?

谢谢!

Dav*_*veR 6

所有现代CPU都是超级分类器和无序的 - 这意味着指令实际上并没有按照程序集的顺序执行,并且实际上并没有当前的PC - 飞行和执行中有很多10条指令立刻.

因此,CPU报告的任何采样信息只是CPU执行的一个粗略区域 - 它正在执行采样中断关闭时指示的指令; 但它也在执行所有其他飞行中的!

然而人们已经习惯了(和期望)分析工具来告诉他们究竟哪个单独的指令的CPU当前正在运行-所以当采样中断触发CPU基本上是挑选的许多积极的指令之一是"当前"之一.


lit*_*adv 5

CPU线路缓存可能就是原因.访问[ebp-0x5c]也会进入缓存[ebp-0x68],然后将更快地获取(对于第二种情况,反之亦然).