而且OrElse也可能异​​常缓慢

Eri*_*ith 15 vb.net performance logical-operators

我正在用VB.NET 2010编写一个计算密集型程序,我希望优化速度.如果操作的结果被分配给类级变量,我发现运算符AndAlso并且OrElse异常缓慢.例如,虽然声明

a = _b AndAlso _c  
_a = a  
Run Code Online (Sandbox Code Playgroud)

在编译的exe中,它们之间需要大约6个机器周期,单个语句

_a = _b AndAlso _c  
Run Code Online (Sandbox Code Playgroud)

大约需要80个机器周期.这里_a,_b并且_c是私有布尔变量Form1,并且所讨论的语句在实例过程中Form1,其中a是一个局部布尔变量.

我无法找到为什么单一陈述需要这么长时间.我已经使用NetReflector探索了它,直到CIL代码的水平,看起来很好:

Instruction               Explanation                              Stack  
00: ldarg.0               Push Me (ref to current inst of Form1)   Me  
01: ldarg.0               Push Me                                  Me, Me  
02: ldfld bool Form1::_b  Pop Me, read _b and push it              _b, Me  
07: brfalse.s 11          Pop _b; if false, branch to 11           Me  
09: ldarg.0               (_b true) Push Me                        Me, Me  
0a: ldfld bool Form1::_c  (_b true) Pop Me, read _c and push it    _c, Me  
0f: brtrue.s 14           (_b true) Pop _c; if true, branch to 14  Me  
11: ldc.i4.0              (_b, _c not both true) Push result 0     result, Me  
12: br.s 15               Jump unconditionally to 15               result, Me  
-----  
14: ldc.i4.1              (_b, _c both true) Push result 1         result, Me  
15: stfld bool Form1::_a  Pop result and Me; write result to _a    (empty)  
1a:
Run Code Online (Sandbox Code Playgroud)

任何人都可以阐明为什么声明_a = _b AndAlso _c需要80个机器周期而不是预测的5个左右?

我正在使用带有.NET 4.0和Visual Studio Express 2010的Windows XP.我使用我自己的一个简单的脏片段来测量时间,它基本上使用一个Stopwatch对象来定时For-Next循环,包含有关代码的1000次迭代将它与空For-Next循环进行比较; 它包括两个循环中的一个无用指令,浪费几个周期并防止处理器停止.原油但足够我的目的.

Han*_*ant 12

这里有两个因素使得代码变慢.您无法从IL中看到这一点,只有机器代码可以为您提供见解.


首先是与AndAlso运算符关联的一般.它是一个短路运算符,如果左侧操作数的计算结果为False,则不会对右侧操作数进行求值.这需要机器代码中的分支.分支是一个处理器可以做的,它必须在最慢的事情之一在分支前面,以避免冲洗管道的风险.如果它猜错了那么它将会受到重大打击.这篇文章很好地介绍了.如果a变量是高度随机的,并且分支因此预测不佳,则典型的性能损失约为500%.

您可以通过使用And运算符来避免此风险,它不需要机器代码中的分支.它只是一条指令,由处理器实现.没有必要在这样的表达式中使用AndAlso,如果右侧操作数得到评估,没有任何问题.这里不适用,但即使IL显示分支,抖动仍可能使机器代码无分支,并带有CMOV指令(条件移动).


但在您的情况下,最重要的是Form类继承自MarshalByRefObject类.继承链是MarshalByRefObject> Component> Control> ScrollableControl> ContainerControl> Form.

MBRO由Just-in-Time编译器专门处理,代码可能与类对象的代理一起使用,真实对象存在于另一个AppDomain或另一台机器中.对于几乎任何类的成员,代理对抖动都是透明的,它们是作为简单的方法调用实现的.除了字段之外,它们不能被代理,因为对字段的访问是通过内存读/写来完成的,而不是方法调用.如果抖动无法证明该对象是本地的,那么它将被强制使用名为JIT_GetFieldXxx()和JIT_SetFieldXxx()的辅助方法调用CLR.CLR知道对象引用是代理还是真实交易并处理差异.开销非常大,80个周期听起来很合适.

只要变量是Form类的成员,就没有什么可以做的.将它们转移到帮助程序类是解决方法.

  • 精彩!非常感谢.当我将变量移动到辅助类时,指令平均需要3½个循环而不是80个循环. (3认同)
  • 好结果.Q + A不能比这更好.请更新您的问题,并就您的衡量方法花一些时间,没有足够的程序员这样做. (2认同)