我正在努力提高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发出有效代码?
答案 0 :(得分:2)
如果您对它进行显式编码以仅按如下方式加载c->d
一次;
int d = c->d ;
y = (x / d) + 1;
z = (x % d) + 1;
生成一个除法运算。
答案 1 :(得分:0)
我发现有一个可能解决该问题的方法,尽管它非常丑陋。
由于同时增加y
和z
似乎是导致原始代码段出现问题的原因:
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