这个C循环可以进一步优化吗?

3

我尖叫了出来。 这真的让人思考。

我害怕想象如果我选择“追求规模而非速度”会发生什么。

背景设置:Visual Studio 2010

<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>

如何实现:

for (i = 0; i < some_num; i++)
{
    one += buf[i] * buf[i];     
    two += buf[i] * buf[off+i];
}

translate to this:

131:    for (i = 0; i < some_num; i++)
132:    {
133:        one += buf[i] * buf[i];
00404B40  movss       xmm0,dword ptr [eax-4]
00404B45  movss       xmm7,dword ptr [esp+18h]
00404B4B  movss       xmm2,dword ptr [eax]
00404B4F  cvtps2pd    xmm3,xmm2
00404B52  movss       xmm4,dword ptr [eax+4]
00404B57  cvtps2pd    xmm1,xmm0
00404B5A  mulsd       xmm3,xmm3
00404B5E  movss       xmm6,dword ptr [eax+8]
00404B63  mulsd       xmm1,xmm1
00404B67  cvtps2pd    xmm5,xmm4
00404B6A  mulsd       xmm5,xmm5
00404B6E  cvtps2pd    xmm7,xmm7
00404B71  addsd       xmm1,xmm7
00404B75  cvtpd2ps    xmm1,xmm1
00404B79  cvtss2sd    xmm1,xmm1
00404B7D  addsd       xmm1,xmm3
00404B81  xorps       xmm3,xmm3
00404B84  cvtpd2ps    xmm1,xmm1
00404B88  cvtss2sd    xmm1,xmm1
00404B8C  addsd       xmm1,xmm5
00404B90  cvtpd2ps    xmm1,xmm1
00404B94  cvtss2sd    xmm3,xmm1
   134:        two += buf[i] * buf[off+i];
00404B98  cvtps2pd    xmm0,xmm0
00404B9B  cvtps2pd    xmm2,xmm2
00404B9E  cvtps2pd    xmm1,xmm6
00404BA1  mulsd       xmm1,xmm1
00404BA5  addsd       xmm3,xmm1
00404BA9  xorps       xmm1,xmm1
00404BAC  cvtpd2ps    xmm1,xmm3
00404BB0  cvtps2pd    xmm5,xmm1
00404BB3  movss       xmm1,dword ptr [eax+0Ch]
00404BB8  cvtps2pd    xmm3,xmm1
00404BBB  mulsd       xmm3,xmm3
00404BBF  addsd       xmm5,xmm3
00404BC3  xorps       xmm3,xmm3
00404BC6  cvtpd2ps    xmm3,xmm5
00404BCA  cvtps2pd    xmm5,xmm3
00404BCD  movss       xmm3,dword ptr [eax+10h]
00404BD2  cvtps2pd    xmm3,xmm3
00404BD5  mulsd       xmm3,xmm3
00404BD9  addsd       xmm5,xmm3
00404BDD  xorps       xmm3,xmm3
00404BE0  cvtpd2ps    xmm3,xmm5
00404BE4  cvtps2pd    xmm5,xmm3
00404BE7  movss       xmm3,dword ptr [eax+14h]
00404BEC  cvtps2pd    xmm3,xmm3
00404BEF  mulsd       xmm3,xmm3
00404BF3  addsd       xmm5,xmm3
00404BF7  xorps       xmm3,xmm3
00404BFA  cvtpd2ps    xmm3,xmm5
00404BFE  cvtps2pd    xmm5,xmm3
00404C01  movss       xmm3,dword ptr [eax+18h]
00404C06  cvtps2pd    xmm3,xmm3
00404C09  mulsd       xmm3,xmm3
00404C0D  addsd       xmm5,xmm3
00404C11  xorps       xmm3,xmm3
00404C14  cvtpd2ps    xmm3,xmm5
00404C18  movss       dword ptr [esp+18h],xmm3
00404C1E  movss       xmm3,dword ptr [ecx-4]
00404C23  cvtps2pd    xmm3,xmm3
00404C26  mulsd       xmm3,xmm0
00404C2A  movss       xmm0,dword ptr [esp+10h]
00404C30  cvtps2pd    xmm0,xmm0
00404C33  addsd       xmm3,xmm0
00404C37  xorps       xmm0,xmm0
00404C3A  cvtpd2ps    xmm0,xmm3
00404C3E  movss       xmm3,dword ptr [ecx]
00404C42  cvtps2pd    xmm0,xmm0
00404C45  cvtps2pd    xmm3,xmm3
00404C48  mulsd       xmm2,xmm3
00404C4C  addsd       xmm0,xmm2
00404C50  movss       xmm2,dword ptr [ecx+4]
00404C55  cvtpd2ps    xmm0,xmm0
00404C59  cvtss2sd    xmm0,xmm0
00404C5D  cvtps2pd    xmm2,xmm2
00404C60  cvtps2pd    xmm3,xmm4
00404C63  mulsd       xmm2,xmm3
00404C67  addsd       xmm0,xmm2
00404C6B  movss       xmm2,dword ptr [ecx+8]
00404C70  cvtpd2ps    xmm0,xmm0
00404C74  cvtss2sd    xmm0,xmm0
00404C78  cvtps2pd    xmm2,xmm2
00404C7B  cvtps2pd    xmm1,xmm1
00404C7E  cvtps2pd    xmm3,xmm6
00404C81  mulsd       xmm2,xmm3
00404C85  addsd       xmm0,xmm2
00404C89  movss       xmm2,dword ptr [ecx+0Ch]
00404C8E  cvtpd2ps    xmm0,xmm0
00404C92  cvtss2sd    xmm0,xmm0
00404C96  cvtps2pd    xmm2,xmm2
00404C99  mulsd       xmm2,xmm1
00404C9D  addsd       xmm0,xmm2
00404CA1  cvtpd2ps    xmm0,xmm0
00404CA5  xorps       xmm1,xmm1
00404CA8  cvtss2sd    xmm1,xmm0
00404CAC  movss       xmm0,dword ptr [ecx+10h]
00404CB1  cvtps2pd    xmm2,xmm0
00404CB4  movss       xmm0,dword ptr [eax+10h]
00404CB9  cvtps2pd    xmm0,xmm0
00404CBC  mulsd       xmm2,xmm0
00404CC0  addsd       xmm1,xmm2
00404CC4  xorps       xmm0,xmm0
00404CC7  cvtpd2ps    xmm0,xmm1
00404CCB  add         eax,20h
00404CCE  add         ecx,20h
00404CD1  cvtps2pd    xmm1,xmm0
00404CD4  movss       xmm0,dword ptr [ecx-0Ch]
00404CD9  cvtps2pd    xmm2,xmm0
00404CDC  movss       xmm0,dword ptr [eax-0Ch]
00404CE1  cvtps2pd    xmm0,xmm0
00404CE4  mulsd       xmm2,xmm0
00404CE8  addsd       xmm1,xmm2
00404CEC  xorps       xmm0,xmm0
00404CEF  cvtpd2ps    xmm0,xmm1
00404CF3  xorps       xmm1,xmm1
00404CF6  cvtps2pd    xmm1,xmm0
00404CF9  movss       xmm0,dword ptr [ecx-8]
00404CFE  xorps       xmm2,xmm2
00404D01  cvtps2pd    xmm2,xmm0
00404D04  movss       xmm0,dword ptr [eax-8]
00404D09  cvtps2pd    xmm0,xmm0
00404D0C  mulsd       xmm2,xmm0
00404D10  addsd       xmm1,xmm2
00404D14  xorps       xmm0,xmm0
00404D17  cvtpd2ps    xmm0,xmm1
00404D1B  movss       dword ptr [esp+10h],xmm0
00404D21  cmp         eax,offset buf+84h (42D6A4h)
00404D26  jl          gem+290h (404B40h)
   135:    }

只是提供信息,根据某些情况下,由于内存/缓存效应,优化“大小”实际上比优化“速度”更快。 - Brian
当它优化“速度”时,难道不应该考虑这些影响吗? - Karl Knechtel
@Karl Knechtel 嗯,VC++ 在运行时怎么知道你的缓存大小或可用内存呢?我猜它仍然会做一些猜测,但是如果没有更多信息,这些猜测几乎不可能完美无缺。 - Voo
3个回答

9
答案是肯定的。Visual Studio目前不支持向量化代码。如果你查看汇编代码,那些都是标量SSE指令。而且你的循环显然可以向量化。
你需要使用一个向量化编译器来获得更好的结果。或者使用内置函数发出向量-SSE指令。
另外你可以尝试以下方法:
将浮点模式从“精确”改为“快速”。编译器会将中间值提升为双精度并进行转换,这会增加很多开销。 http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011/compiler_c/intref_cls/common/intref_bk_intro.htm

2
看起来你在表达式中混合使用了单精度和双精度类型,这会导致很多不必要的转换。如果你解决了这个问题,代码应该会更小更高效。
此外,你可以使用更好的编译器,例如英特尔的ICC,正如@Mysticial已经建议的那样,它可能能够将此循环向量化。
还有一点需要注意——我没有仔细研究代码,但看起来循环已经被展开,所以实际上它可能比起初看来更有效率。

2
实际上,所有的变量看起来都是单精度。但由于浮点模式是“精确”的,所有中间值都会提升为双精度,然后再转换回单精度。 - Mysticial
@Mysticial:是的,你可能是对的——遗憾的是,OP没有包括bufonetwo的声明。 - Paul R
抱歉,所有这些都是32位浮点数。虽然类似Mac的(+*)行 tend to compile into 80-bit precision,我相信。感谢您的建议。通过将代码分离为一个函数并在MAC之前移动索引以及在循环之前进行偏移,我已经将代码性能降至令人满意的水平。 - Djordje V Petrovic
我不能对循环的后半部分使用向量化,因为我相信“off”并不总是可被四整除的。我害怕将其分成两个循环,因为可能会出现流水线停顿(仍然不知道如何预测/计算/防止这种情况)。无论如何,我还是会尝试。 - Djordje V Petrovic
不行。分离并没有起作用。这样做会增加大约25%的开销(以毫秒计)。 - Djordje V Petrovic
1
如果你无法对齐数据,有两种选择:1.使用非对齐加载。它们速度较慢,但可以快速修复。2.缓冲加载。以对齐数量加载它们,然后将它们混合到最终的向量中。(第二种方法往往是最快的,但也最混乱。) - Mysticial

2
请注意:
00404CCB  add         eax,20h
00404CCE  add         ecx,20h

循环展开,以每次处理8个 i 值的方式进行操作。

1
当然,这就解释了大量指令的原因。 - Ville Krumlinde

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接