VS2017编译器发出2个除法/余数对的除法指令

时间:2018-07-23 11:25:17

标签: c assembly optimization visual-c++ x86-64

我正在努力提高C程序的运行速度,同时尝试避免使用内联汇编。与最初的性能相比,该程序与100倍的提速相差不远,这意味着改进变得越来越难。

在检查VS2017生成的汇编代码(在x64模式下,使用/ O2编译器优化)时,我注意到程序中的一个热点对应于以下代码(匿名):

bool j, k = false;

do
{
    j = false;

    for (i = 0; i < c->a; i++)
    {
        int x, y, z;

        x = c->b[i];

        y = (x/c->d) + 1;
        z = (x % c->d) + 1;

        if (c->e[y][z] == 0)
        {
            c->b[i--] = c->b[--c->a];
            continue;
        }
        else if (c->e[y][z] + c->g[y][z] == c->h[y][z] - '0')
        {
            f(c, y, z);
            k = true;
            j = true;
            c->b[i--] = c->b[--c->a];
            continue;
        }
    }
} while (j);

return k;

我特别要注意以下语句(c-> d是一个整数):

    y = (x/c->d) + 1;
    z = (x % c->d) + 1;

在两种情况下,这是除法,其后是余数,使用相同的参数。由于x86 / x64中的除法指令同时返回除法和余数,因此我希望可以将其编译为单个除法指令。然而,编译器的输出基本上会执行两次操作,包括从内存中重新加载c-> d:

00007FF790061FA0 42 8B 04 1F          mov         eax,dword ptr [rdi+r11]  
00007FF790061FA4 99                   cdq  
00007FF790061FA5 F7 7E 28             idiv        eax,dword ptr [rsi+28h]  
00007FF790061FA8 4C 63 D0             movsxd      r10,eax  
00007FF790061FAB 42 8B 04 1F          mov         eax,dword ptr [rdi+r11]  
00007FF790061FAF 99                   cdq  
00007FF790061FB0 F7 7E 28             idiv        eax,dword ptr [rsi+28h]  
... a few instructions later ...
00007FF790061FC3 4C 63 D2             movsxd      r10,edx

我尝试对代码进行各种转换,以使其未能发出一条除法指令。我得到的最接近的是将上面的C代码块转换为:

    y = (x/c->d);
    z = (x % c->d);
    y++;

这导致发出以下代码:

00007FF7942B1FA0 42 8B 04 17          mov         eax,dword ptr [rdi+r10]  
00007FF7942B1FA4 99                   cdq  
00007FF7942B1FA5 F7 7E 28             idiv        eax,dword ptr [rsi+28h]  
00007FF7942B1FA8 4C 63 F2             movsxd      r14,edx  
00007FF7942B1FAB 44 8D 60 01          lea         r12d,[rax+1]  
00007FF7942B1FAF 48 98                cdqe  
00007FF7942B1FB1 48 8D 14 C5 08 00 00 00 lea         rdx,[rax*8+8]  

不幸的是,也没有添加z++,代码是错误的,并且我的程序无法正常工作。在z++之后加回y++后,会再次发出汇编代码的第一个版本。

在这种情况下,我可以使用哪种代码转换或编译器标志来强制VS2017发出有效代码?

2 个答案:

答案 0 :(得分:2)

如果您对它进行显式编码以仅按如下方式加载c->d一次;

   int d = c->d ;
   y = (x / d) + 1;
   z = (x % d) + 1;

生成一个除法运算。

答案 1 :(得分:0)

我发现有一个可能解决该问题的方法,尽管它非常丑陋。

由于同时增加yz似乎是导致原始代码段出现问题的原因:

    x = c->b[i];
    y = (x/c->d) + 1;
    z = (x % c->d) + 1;

我修改了x,以便通过向y添加c->d来获得相同的结果,但是没有显式的增量,并且自{单个增量似乎不会阻止所需代码的生成:

z

这会为我生成带有一个 x = c->b[i] + c->d; y = x/c->d; z = (x % c->d) + 1; 的代码:

IDIV