Оптимизированы ли избыточные роли?

Я обновляю некоторый старый код и обнаружил несколько экземпляров, когда один и тот же объект бросается повторно каждый раз, когда нужно вызвать один из его свойств или методов. Пример:

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. Я только больше смутился.

UPDATE

До сих пор консенсус, по-видимому, заключался в том, что a) нет, броски не оптимизированы, но b), хотя в результате может быть небольшое снижение производительности, это маловероятно, и c) я должен рассмотреть фиксируя их в любом случае. Я сошел на сторону решения исправить это когда-нибудь, если у меня будет время. Между тем, я не буду беспокоиться о них.

Спасибо всем!

Ответ 1

Он не оптимизирован от IL в отладочных или релизных сборках.

простой тест С#:

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 и разборки. Я был бы очень удивлен, если бы он не был оптимизирован.

Независимо от того, я приведу Pragmatic Programmer и теорему разбитого окна: когда вы видите разбитое окно, исправьте его.

Ответ 2

Точечная проверка генерируемого машинного кода в сборке Release показывает, что джиттер x86 не оптимизирует отмену.

Здесь вы должны посмотреть на общую картину. Вы назначаете свойства элемента управления. У них тонна побочных эффектов. В случае DateTimePicker присваивание приводит к отправке сообщения в собственный элемент управления Windows. Это, в свою очередь, хрустит в сообщении. Стоимость броска незначительна для стоимости побочных эффектов. Переписывание заданий никогда не будет иметь заметной разницы в скорости, вы только делаете это на долю процента быстрее.

Идем дальше и переписываем код в ленивый день в пятницу. Но только потому, что это простота для удобочитаемости. Этот плохо читаемый код С# также создает плохо оптимизированный машинный код, не совсем совпадающий.

Ответ 3

Нет; FxCop обозначает это предупреждение о производительности. См. Информацию здесь: http://msdn.microsoft.com/en-us/library/ms182271.aspx

Я бы рекомендовал запустить это над вашим кодом, если вы хотите найти что-то исправлять.

Ответ 4

Я никогда не слышал или не видел избыточных оптимизаций броска на 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();

На моей машине, работающей под освобождением, я постоянно получаю около 350 тыс. тиков для избыточной избыточности и 280 тыс. тиков для самообучения. Нет, похоже, что CLR не оптимизирует для этого.