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

时间:2015-01-21 17:26:06

标签: vb.net performance logical-operators

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

a = _b AndAlso _c  
_a = a  

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

_a = _b AndAlso _c  

大约需要80个机器周期。此处_a_b_cForm1的私有布尔变量,有问题的语句位于Form1的实例过程中,其中a 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: 1}}是一个局部布尔变量。

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

_a = _b AndAlso _c

任何人都可以解释为什么声明{{1}}需要80个机器周期而不是预测的5个左右?

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

1 个答案:

答案 0 :(得分:12)

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


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

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


但在您的情况下,最重要的是Form类继承自MarshalByRefObject类。继承链是MarshalByRefObject>组分>控制> ScrollableControl> ContainerControl>形式。

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

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