.NET

2015-04-21 2 views
5

Будет .NET компилятор оптимизирует это:.NET

public MyObject GetNewObject() 
{ 
    var newCurrentObject = myObjectFactory.CreateNew(
           DateTime.Now, 
           "Frank", 
           41, 
           secretPassword); 

    return newCurrentObject; 
} 

выполнить с таким же числом команд/памяти, как это:

public MyObject GetNewObject() 
{ 
    return myObjectFactory.CreateNew(
         DateTime.Now, 
         "Frank", 
         41, 
         secretPassword); 
} 

Или будет локальная переменная результат дополнительное время и память, потраченные на создание ссылки (newObject) в MyObject, уничтожают только следующую строку после того, как она выходит из области видимости.

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

+1

На самом деле этого не произойдет, вы не пытались возвратить 'newObject' в первом блоке. И поскольку это ссылочный тип, когда он возвращает 'new MyObject()' во втором блоке, он просто возвращает ссылку на ранее существовавшее выделение памяти для него, поэтому он не будет создавать/уничтожать и дополнительную переменную. –

+1

@AlexeiLevenkov Не все оптимизации. Некоторые оптимизации выполняются самим компилятором C#. –

+0

Удивительно, почему мой комментарий удален ... Вопрос даже утверждает: * Будет ли ** компилятор ** оптимизировать это *, так почему мы говорим о JIT вместо IL? –

ответ

6

Предполагая, что MyObject является ссылочным типом, для обоих случаев будет создан один и тот же x86. JIT достаточно компетентен в оптимизации скалярных временных рядов и назначений. Это одна из самых основных оптимизаций. Практически любой оптимизатор использует форму SSA внутри, и эта оптимизация почти выпадает из формы SSA.

Предполагая, что MyObject является struct: Я проверил .NET 4.5 JIT и новую оптимизацию RyuJIT для структур. .NET JITs обычно не оптимизируют назначения структур и (локальные) переменные. Код буквально переводится, за исключением одного незначительного случая, который здесь не применяется. Ожидайте полностью буквальный машинный код. Даже если вы скажете a = a; или a.x = 1; a.x = 1;, вы получите именно это как машинный код. Отправьте письмо команде, если структуры важны для вас. Сейчас все еще время, чтобы внести изменения.

3

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

Единственные проблемы возникли бы, если вместо MyObject вы использовали primitive type, например. INT, но тип возвращаемого все равно будет Объект:

public object BoxingOccurs() 
{ 
    int i = 5; 
    return i; // or shortly return 5; 
} 

Примитивный тип будет затем в штучной упаковке, а это означает, что тип значение преобразуется в тип объекта. Примитивный тип обернут и перемещен в управляемую кучу. Подробнее о боксе и распаковке можно найти here.

+4

Я не уверен, как это отвечает на вопрос. Реальное мясо вопроса будет оптимизировано локальное хранилище переменных или нет. Не возвращается ли эта же ссылка или копия. Предоставленный OP смутно сформулировал последнюю строку; но это не ответ на вопрос. –

+0

Ну, у вас есть действительная точка. Фактически происходит то, что оператор (оба из них) создает ссылку в верхней части стека, которая ссылается на экземпляр класса в куче, а затем возвращается эта ссылка. Очень вероятный сценарий заключается в том, что refence хранится в каком-то реестре, и он остается там до тех пор, пока он не понадобится для вычислений. Однако это зависит от машины и зависит от того, как MSIL переведен на ассемблер используемого процессора и как этот процессор обрабатывает эти операции. – Santhos

+0

Вы также должны понимать, что в процессе компиляции и последующего интерпретации кода существует более одной оптимизации, а язык, созданный компиляцией, - это только промежуточный язык, который затем обрабатывается компилятором JIT, который переводит его на машинный язык в время выполнения. – Santhos

0

newObject - это переменная, а не ссылка (переменная содержит ссылку на объект). Я пробовал код (в режиме деблокирования), и компилятор не оптимизирует этот код. Я не уверен, но спецификация может быть даже запрещена, поскольку переменные являются частью отражения, и это может повлиять на ожидаемое поведение. Однако это не значит, что код не будет запущен таким образом из-за JITter, который мог бы его оптимизировать.

+1

* newObject - это переменная, а не ссылка *? Какие? Тогда что такое ссылка? –

+0

@Sriram Ссылка - это своего рода ценность, которая относится к чему-то. В этом случае к объекту. Это аналогично указателям, но более абстрактным.С другой стороны, переменная - это место хранения, которое содержит значение. В этом случае он содержит ссылку. – IllidanS4

+0

Если вы измените свое первое предложение на что-то подобное, это не будет вводить в заблуждение. 'newObject' - это просто ссылка, а не сам экземпляр объекта. Я нахожу это сбивающим с толку, когда вы говорите, что это переменная без ссылки. Действительно, это ссылка. –

4

В общем, когда вы включаете оптимизацию в компиляторе C# it attempts to minimize the number of temporary/local variable storage slots used in methods. Поэтому вы не должны заботиться о том, чтобы свести к минимуму количество переменных, потому что компилятор сделает это за вас. Без оптимизаций будут некоторые различия, потому что цель состоит в том, чтобы улучшить опыт отладки и сохранить как можно больше информации.

В этом конкретном случае точно такой же IL испускается для обоих методов, когда оптимизация включена.Вот полный фрагмент кода, включая тип значения и ссылочный тип для сравнения:

using System; 

public class C { 

    private static MyStruct NewStructLocal() 
    { 
     var s = new MyStruct(); 
     return s; 
    } 

    private static MyStruct NewStructReturn() 
    { 
     return new MyStruct(); 
    } 

    private static MyClass NewClassLocal() 
    { 
     var s = new MyClass(); 
     return s; 
    } 

    private static MyClass NewClassReturn() 
    { 
     return new MyClass(); 
    } 
} 

public struct MyStruct 
{ 
    public int I; 
} 

public class MyClass 
{ 
    public int I; 
} 

И излучаемый IL является:

.method private hidebysig static 
    valuetype MyStruct NewStructLocal() cil managed 
{ 
    // Method begins at RVA 0x2054 
    // Code size 10 (0xa) 
    .maxstack 1 
    .locals init (
     [0] valuetype MyStruct 
    ) 

    IL_0000: ldloca.s 0 
    IL_0002: initobj MyStruct 
    IL_0008: ldloc.0 
    IL_0009: ret 
} // end of method C::NewStructLocal 

.method private hidebysig static 
    valuetype MyStruct NewStructReturn() cil managed 
{ 
    // Method begins at RVA 0x206c 
    // Code size 10 (0xa) 
    .maxstack 1 
    .locals init (
     [0] valuetype MyStruct 
    ) 

    IL_0000: ldloca.s 0 
    IL_0002: initobj MyStruct 
    IL_0008: ldloc.0 
    IL_0009: ret 
} // end of method C::NewStructReturn 

.method private hidebysig static 
    class MyClass NewClassLocal() cil managed 
{ 
    // Method begins at RVA 0x2082 
    // Code size 6 (0x6) 
    .maxstack 8 

    IL_0000: newobj instance void MyClass::.ctor() 
    IL_0005: ret 
} // end of method C::NewClassLocal 

.method private hidebysig static 
    class MyClass NewClassReturn() cil managed 
{ 
    // Method begins at RVA 0x2082 
    // Code size 6 (0x6) 
    .maxstack 8 

    IL_0000: newobj instance void MyClass::.ctor() 
    IL_0005: ret 
} // end of method C::NewClassReturn 

Таким образом, насколько производительность, то они будут идентичны.

В случае ссылочных типов объект создается, ссылка помещается в верхнюю часть оценочного стека и немедленно возвращается оттуда.

В случае типов значений объявляется локальное хранилище, значение в месте хранения инициализируется, значение снова загружается и возвращается. Это довольно типично для типов значений, которые не являются примитивными типами.

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

Примечание

Этот вопрос несколько изменился, так как я первый написал этот ответ, но я думаю, что все этого анализа до сих пор стоит. В конкретном случае, учитывая, что вы получаете то же IL в обоих методах при компиляции с оптимизацией. Локальная переменная не влияет на испускаемый ИЛ. Теперь, если вы хотите добавить больше локальных переменных для других аргументов, у вас будет другой ИЛ, и вам нужно будет проверить разборку, чтобы определить полную разницу.

+0

Это отличный ответ. Это недействительно, потому что я забыл учитывать оптимизацию, которую может сделать компилятор C#. (На самом деле многие ваши ответы очень ценны, как я только что заметил.) – usr

+0

@usr Спасибо за добрые слова. Теперь я вижу, что вопрос изменился (* sigh *), но я думаю, что мой анализ все еще стоит. –

Смежные вопросы