冗余演员表会得到优化吗?

时间:2011-03-11 21:39:28

标签: c# .net casting jit compiler-optimization

我正在更新一些旧代码,并且已经找到了几个实例,每次需要调用其中一个属性或方法时,都会重复转换相同的对象。例如:

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;

我倾向于修复这种怪异,但考虑到我有限的时间,我不想打扰任何不影响功能或性能的东西。

所以我想知道的是,这些冗余的演员是否被编译器优化了?我试着通过在一个简化的例子中使用ildasm来解决这个问题,但不熟悉IL我只是更加困惑。

更新

到目前为止,共识似乎是a)不,演员表没有优化,但b)虽然可能会有一些小的性能影响,但它不太可能显着,并且c)我应该考虑无论如何要修理它们。如果我有时间的话,我有一天会解决这些问题。同时,我不会担心它们。

谢谢大家!

4 个答案:

答案 0 :(得分:21)

对Release版本中生成的机器代码进行抽查,表明x86抖动不会优化抛弃。

你必须看看这里的大图。您正在分配控件的属性。它们有很多副作用。对于DateTimePicker,分配会导致将消息发送到本机Windows控件。而这反过来又扼杀了这条消息。铸件的成本可以忽略不计副作用的成本。重写作业永远不会在速度方面产生明显的差异,你只能使它快一点点。

继续,在一个懒惰的星期五下午重写代码。但这只是因为它对可读性有害。可读性差的C#代码也会产生效率低下的机器代码并不完全是巧合。

答案 1 :(得分:18)

在调试或发布版本中,它没有与IL进行优化。

简单的C#测试:

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和拆卸。如果它没有被优化,我会非常惊讶。

无论如何,我会引用The Pragmatic Programmer和破碎的窗口定理:当你看到一个破窗口时,修复它。

答案 2 :(得分:6)

没有; FxCop将此标记为性能警告。请参阅此处的信息:http://msdn.microsoft.com/en-us/library/ms182271.aspx

如果您想找到要修复的内容,我建议您在代码上运行。

答案 3 :(得分:1)

我从未听说过或看到过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();

在我的机器上,在发布下运行,我可以获得大约350k的冗余冗余和280k的自动优化。所以不,看起来CLR没有针对此进行优化。