vb.net – AndAlso OrElse可能会异常缓慢
我正在使用VB.NET 2010编写一个计算密集型程序,我希望优化速度.我发现如果将操作结果分配给类级别的变量,则运算符AndAlso和OrElse异常慢.例如,虽然语句
a = _b AndAlso _c _a = a 在它们之间的编译exe中,大约有6个机器周期,单个语句 _a = _b AndAlso _c 大约需要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: 任何人都可以说明为什么这个语句_a = _b AndAlso _c需要80个机器周期,而不是预测的5个左右? 我使用Windows XP与.NET 4.0和Visual Studio Express 2010.我用一个坦白的脏片段测量了我自己的时间,它基本上使用一个Stopwatch对象来对一个For-Next循环进行1000次迭代,包含有关代码,将其与空For-Next循环进行比较;它在两个循环中都包含一个无用的指令,以浪费几个周期并防止处理器停滞.粗鲁但足够我的目的.
这里有两个因素使得这个代码变慢.您不能从IL看到这一点,只有机器代码可以给你洞察力.
首先是与AndAlso运算符关联的一般.它是一个短路操作符,如果左侧操作数的计算结果为False,则右侧的操作数不会得到评估.这需要机器代码中的分支.分支是处理器可以做的最慢的事情之一,它必须在分支机构前面猜测,以避免冲洗管道的风险.如果它猜测错了,那将会是一个重大的打击.非常好的覆盖在this post.如果一个变量是高度随机的,并且分支因此预测不足,典型的perf损失约为500%. 您可以通过使用And运算符来避免这种风险,而不需要机器代码中的分支.它只是一个指令,AND由处理器实现.在这样一个表达式中,没有任何意义上的赞同,如果右手操作数得到评估,没有任何问题.这里不适用,但即使IL显示分支,则抖动仍可能使CMOV指令(条件移动)使机器代码无分支. 但在你的情况下最重要的是Form类继承自MarshalByRefObject类.继承链是MarshalByRefObject>成分>控制> ScrollableControl> ContainerControl>形成. MBRO由Just-in-Time编译器特别处理,代码可能正在使用类对象的代理,而真实对象生活在另一个AppDomain或另一台机器中.代理对于几乎任何类型的成员的抖动都是透明的,它们被实现为简单的方法调用.除了字段,它们不能被代理,因为对存储器读/写而不是方法调用进行了一个字段的访问.如果抖动不能证明该对象是本地的,则使用JIT_GetFieldXxx()和JIT_SetFieldXxx()的帮助方法强制调用该CLR. CLR知道对象引用是代理还是真正的交易,并处理差异.开销是相当大的,关于正确的80个循环. 只要变量是Form类的成员,就没有太多的可以做到这一点.将它们转换为辅助类是解决方法. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |