Оптимизируются ли избыточные приведения? - PullRequest
45 голосов
/ 12 марта 2011

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

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, я только запутался.

ОБНОВЛЕНИЕ

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

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

Ответы [ 4 ]

21 голосов
/ 12 марта 2011

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

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

Продолжайте и переписывайте код ленивым пятничным днем.Но только потому, что это упадок на удобочитаемость.То, что плохо читаемый код C # также производит плохо оптимизированный машинный код, не является простым совпадением.

18 голосов
/ 12 марта 2011

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

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

6 голосов
/ 12 марта 2011

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

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

1 голос
/ 12 марта 2011

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

...