跟踪位操作的优化机会?

你觉得函数haswon有优化的余地吗(见下文)?

我认识到将参数类型从__int64更改为unsigned __int64使得函数更快,因此我可能仍然有机会进行优化。

更详细:我正在写一个连接四游戏。 最近我使用了Profiler Very Sleepy,并认识到函数haswon使用了大部分的cpu-time。 该function使用连接四板的位板表示给一个玩家。 我在fourstones基准测试的源头找到了这个function。 位板表示如下:

 . . . . . . . TOP 5 12 19 26 33 40 47 4 11 18 25 32 39 46 3 10 17 24 31 38 45 2 9 16 23 30 37 44 1 8 15 22 29 36 43 0 7 14 21 28 35 42 BOTTOM 

function:

 // return whether newboard includes a win bool haswon(unsigned __int64 newboard) { unsigned __int64 y = newboard & (newboard >> 6); if (y & (y >> 2 * 6)) // check \ diagonal return true; y = newboard & (newboard >> 7); if (y & (y >> 2 * 7)) // check horizontal - return true; y = newboard & (newboard >> 8); if (y & (y >> 2 * 8)) // check / diagonal return true; y = newboard & (newboard >> 1); if (y & (y >> 2)) // check vertical | return true; return false; } 

谢谢!

编辑: CPU是x86,32位架构,我正在使用Visual Studio 2008 Express Edition中的编译器。 优化标志是/ O2 / Oi / GL。

我尝试了Ben Jackson建议的函数haswon2。 来自Microsoft Compiler的程序集,以及发行版本(/ O2 / Oi / GL)的默认优化标志,几乎没有显示运行时差异。 看起来VC-Compiler与gcc相比无法利用它不能严格按顺序评估每个条件。

结果: haswon原文: 赢了

来自Ben Jackson的haswon2: haswon2

编辑2:haswon的集会:

 00401A10 mov eax,dword ptr [esp+4] 00401A14 mov ecx,dword ptr [esp+8] 00401A18 push ebx 00401A19 push esi 00401A1A push edi 00401A1B mov edx,eax 00401A1D mov edi,ecx 00401A1F shrd edx,edi,6 00401A23 mov esi,edx 00401A25 shr edi,6 00401A28 and esi,eax 00401A2A and edi,ecx 00401A2C mov edx,esi 00401A2E mov ebx,edi 00401A30 shrd edx,ebx,0Ch 00401A34 shr ebx,0Ch 00401A37 and edx,esi 00401A39 and ebx,edi 00401A3B or edx,ebx 00401A3D je `anonymous namespace'::haswon+35h (401A45h) 00401A3F mov al,1 00401A41 pop edi 00401A42 pop esi 00401A43 pop ebx 00401A44 ret 00401A45 mov edx,eax 00401A47 mov edi,ecx 00401A49 shrd edx,edi,7 00401A4D mov esi,edx 00401A4F shr edi,7 00401A52 and esi,eax 00401A54 and edi,ecx 00401A56 mov edx,esi 00401A58 mov ebx,edi 00401A5A shrd edx,ebx,0Eh 00401A5E shr ebx,0Eh 00401A61 and edx,esi 00401A63 and ebx,edi 00401A65 or edx,ebx 00401A67 jne `anonymous namespace'::haswon+2Fh (401A3Fh) 00401A69 mov edx,eax 00401A6B mov edi,ecx 00401A6D shrd edx,edi,8 00401A71 mov esi,edx 00401A73 shr edi,8 00401A76 and esi,eax 00401A78 and edi,ecx 00401A7A mov edx,esi 00401A7C mov ebx,edi 00401A7E shrd edx,ebx,10h 00401A82 shr ebx,10h 00401A85 and edx,esi 00401A87 and ebx,edi 00401A89 or edx,ebx 00401A8B jne `anonymous namespace'::haswon+2Fh (401A3Fh) 00401A8D mov edx,eax 00401A8F mov esi,ecx 00401A91 shrd edx,esi,1 00401A95 shr esi,1 00401A97 and esi,ecx 00401A99 and edx,eax 00401A9B mov eax,edx 00401A9D mov ecx,esi 00401A9F shrd eax,ecx,2 00401AA3 shr ecx,2 00401AA6 and eax,edx 00401AA8 and ecx,esi 00401AAA or eax,ecx 00401AAC jne `anonymous namespace'::haswon+2Fh (401A3Fh) 00401AAE pop edi 00401AAF pop esi 00401AB0 xor al,al 00401AB2 pop ebx 00401AB3 ret 

这个版本背后的想法是避免严格的测试顺序(中间返回强制编译器按顺序逐个评估条件)以及与多个if语句关联的分支:

 // return whether newboard includes a win bool haswon2(uint64_t newboard) { uint64_t y = newboard & (newboard >> 6); uint64_t z = newboard & (newboard >> 7); uint64_t w = newboard & (newboard >> 8); uint64_t x = newboard & (newboard >> 1); return (y & (y >> 2 * 6)) | // check \ diagonal (z & (z >> 2 * 7)) | // check horizontal - (w & (w >> 2 * 8)) | // check / diagonal (x & (x >> 2)); // check vertical | } 

通过适当的优化级别,您可以将w,x,y和z视为移位值的“别名”。 这意味着最终的return语句会将整个操作抛入一个大汤中供编译器使用。 在我的系统上,此版本仅占原始运行时的65%(包括每次生成随机位置的开销)。 如果董事会主要是非赢家,它可能会赢得更大的比例。

看一下每个的反汇编(来自gcc -O3 ),原始版本实际上更短,所以很可能在紧密内循环中缺少分支确实有帮助。