多余的演员是否得到优化?
我正在更新一些旧的代码,并发现了几个实例,其中每次需要调用其某个属性或方法时,同一个对象将被重复投射。 例:
if (recDate != null && recDate > ((System.Windows.Forms.DateTimePicker)ctrl).MinDate) { ((System.Windows.Forms.DateTimePicker)ctrl).CustomFormat = "MM/dd/yyyy"; ((System.Windows.Forms.DateTimePicker)ctrl).Value = recDate; } else { (System.Windows.Forms.DateTimePicker)ctrl).CustomFormat = " "; } ((System.Windows.Forms.DateTimePicker)ctrl).Format = DateTimePickerFormat.Custom;
我的意图是解决这个怪物,但考虑到我有限的时间,我不想打扰任何不影响function或性能的东西。
所以我想知道的是,这些冗余演员是由编译器优化了吗? 我试图通过使用ildasm来简化自己的例子,但是不熟悉IL,我只是更加困惑。
UPDATE
到目前为止,共识似乎是这样的:a)不是,演员阵容不是最优化的,但b)虽然可能会有一些小的performance受到打击,但这不太可能是重要的,c)无论如何我应该考虑修复。 如果我有时间的话,我已经决定解决这些问题了。 同时,我也不会担心。
感谢大家!
在debugging版本或发行版本中,它不会从IL中优化。
简单的C#testing:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace RedundantCastTest { class Program { static object get() { return "asdf"; } static void Main(string[] args) { object obj = get(); if ((string)obj == "asdf") Console.WriteLine("Equal: {0}, len: {1}", obj, ((string)obj).Length); } } }
对应的IL(注意多个castclass
指令):
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint .maxstack 3 .locals init ( [0] object obj, [1] bool CS$4$0000) L_0000: nop L_0001: call object RedundantCastTest.Program::get() L_0006: stloc.0 L_0007: ldloc.0 L_0008: castclass string L_000d: ldstr "asdf" L_0012: call bool [mscorlib]System.String::op_Equality(string, string) L_0017: ldc.i4.0 L_0018: ceq L_001a: stloc.1 L_001b: ldloc.1 L_001c: brtrue.s L_003a L_001e: ldstr "Equal: {0}, len: {1}" L_0023: ldloc.0 L_0024: ldloc.0 L_0025: castclass string L_002a: callvirt instance int32 [mscorlib]System.String::get_Length() L_002f: box int32 L_0034: call void [mscorlib]System.Console::WriteLine(string, object, object) L_0039: nop L_003a: ret }
在发布版本中,也不从IL进行优化:
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint .maxstack 3 .locals init ( [0] object obj) L_0000: call object RedundantCastTest.Program::get() L_0005: stloc.0 L_0006: ldloc.0 L_0007: castclass string L_000c: ldstr "asdf" L_0011: call bool [mscorlib]System.String::op_Equality(string, string) L_0016: brfalse.s L_0033 L_0018: ldstr "Equal: {0}, len: {1}" L_001d: ldloc.0 L_001e: ldloc.0 L_001f: castclass string L_0024: callvirt instance int32 [mscorlib]System.String::get_Length() L_0029: box int32 L_002e: call void [mscorlib]System.Console::WriteLine(string, object, object) L_0033: ret }
这两种情况都意味着在生成本地代码时不会优化演员 – 您需要查看实际的机器组装。 即通过运行ngen和拆卸。 如果没有优化,我会大吃一惊。
无论如何,我会引用“语用程序员”和“破窗”定理:当你看到一个破窗口时,修正它。
在发布版本中生成的机器代码的现场检查表明,x86抖动不会优化丢弃。
尽pipe如此,你必须看看这个大局。 您正在分配控件的属性。 他们有很多的副作用。 在DateTimePicker的情况下,分配会导致消息被发送到本地Windows控件。 这反过来消息的消息。 演员的成本可以忽略副作用的成本。 重写作业永远不会在速度上产生明显的差异,只会使速度提高一个百分点。
继续前进,在慵懒的星期五下午重写代码。 但仅仅因为这是可读性的一个缺陷。 那个可读性差的C#代码也会产生很差的机器码,这不完全是巧合。
没有; FxCop将其标记为性能警告。 在这里看到信息: http : //msdn.microsoft.com/en-us/library/ms182271.aspx
如果你想find需要修复的东西,我build议在代码上运行它。
我从来没有听说过或在CLR上看到多余的演员优化。 让我们尝试一个人为的例子
object number = 5; int iterations = 10000000; int[] storage = new int[iterations]; var sw = Stopwatch.StartNew(); for (int i = 0; i < iterations; i++) { storage[i] = ((int)number) + 1; storage[i] = ((int)number) + 2; storage[i] = ((int)number) + 3; } Console.WriteLine(sw.ElapsedTicks); storage = new int[iterations]; sw = Stopwatch.StartNew(); for (int i = 0; i < iterations; i++) { var j = (int)number; storage[i] = j + 1; storage[i] = j + 2; storage[i] = j + 3; } Console.WriteLine(sw.ElapsedTicks); Console.ReadLine();
在我的机器上,运行在释放,我一直得到大约35万刻度冗余冗余和280K刻度自我优化。 所以不,看起来CLR并没有为此优化。