2016-12-20 6 views
1

Я заметил это поведение на # компилятор по умолчанию C, VS 2017 RC EnterpriseВозможно избыточно опкод, когда явно базовый тип отливать

При двойном/поплавок отливают себе известный тип Conv.R8/Conv.R4 излучается. Однако, если объект или тип неплавающей точки забрасываются, ничего не происходит.

Примеры, приведенные ниже, составлены по адресу release. В debug IL аналогичен. Образец C# Код:

private double _Double; 
    private float _Float; 
    private int _Int; 
    private object _Object; 

    private int IntConvertToInt() 
    { 
     int x = (int)_Int; // 
     return x; 
    } 

    private int IntAssignToInt() 
    { 
     int x = _Int; 
     return x; 
    } 

    private float FloatConvertToFloat() 
    { 
     float x = (float)_Float; //Additional OpCode 
     return x; 
    } 

    private float FloatAssignToFloat() 
    { 
     float x = _Float; 
     return x; 
    } 
    private double DoubleConvertToDouble() 
    { 
     double x = (double)_Double; //Additional OpCode 
     return x; 
    } 

    private double DoubleAssignToDouble() 
    { 
     double x = _Double; 
     return x; 
    } 
    private Object ObjectConvertToObject() 
    { 
     Object x = (Object)_Object; 
     return x; 
    } 

    private Object ObjectAssignToObject() 
    { 
     Object x = _Object; 
     return x; 
    } 

Coresponding Il:

 .method private hidebysig 
    instance int32 IntConvertToInt() cil managed 
{ 
    // Method begins at RVA 0x2052 
    // Code size 7 (0x7) 
    .maxstack 8 

    IL_0000: ldarg.0 
    IL_0001: ldfld int32 ConversionTest.Program::_Int 
    IL_0006: ret 
} // end of method Program::IntConvertToInt 

.method private hidebysig 
    instance int32 IntAssignToInt() cil managed 
{ 
    // Method begins at RVA 0x2052 
    // Code size 7 (0x7) 
    .maxstack 8 

    IL_0000: ldarg.0 
    IL_0001: ldfld int32 ConversionTest.Program::_Int 
    IL_0006: ret 
} // end of method Program::IntAssignToInt 

.method private hidebysig 
    instance float32 FloatConvertToFloat() cil managed 
{ 
    // Method begins at RVA 0x205a 
    // Code size 8 (0x8) 
    .maxstack 8 

    IL_0000: ldarg.0 
    IL_0001: ldfld float32 ConversionTest.Program::_Float 
    IL_0006: conv.r4 //here 
    IL_0007: ret 
} // end of method Program::FloatConvertToFloat 

.method private hidebysig 
    instance float32 FloatAssignToFloat() cil managed 
{ 
    // Method begins at RVA 0x2063 
    // Code size 7 (0x7) 
    .maxstack 8 

    IL_0000: ldarg.0 
    IL_0001: ldfld float32 ConversionTest.Program::_Float 
    IL_0006: ret 
} // end of method Program::FloatAssignToFloat 

.method private hidebysig 
    instance float64 DoubleConvertToDouble() cil managed 
{ 
    // Method begins at RVA 0x206b 
    // Code size 8 (0x8) 
    .maxstack 8 

    IL_0000: ldarg.0 
    IL_0001: ldfld float64 ConversionTest.Program::_Double 
    IL_0006: conv.r8 //here 
    IL_0007: ret 
} // end of method Program::DoubleConvertToDouble 

.method private hidebysig 
    instance float64 DoubleAssignToDouble() cil managed 
{ 
    // Method begins at RVA 0x2074 
    // Code size 7 (0x7) 
    .maxstack 8 

    IL_0000: ldarg.0 
    IL_0001: ldfld float64 ConversionTest.Program::_Double 
    IL_0006: ret 
} // end of method Program::DoubleAssignToDouble 

.method private hidebysig 
    instance object ObjectConvertToObject() cil managed 
{ 
    // Method begins at RVA 0x207c 
    // Code size 7 (0x7) 
    .maxstack 8 

    IL_0000: ldarg.0 
    IL_0001: ldfld object ConversionTest.Program::_Object 
    IL_0006: ret 
} // end of method Program::ObjectConvertToObject 

.method private hidebysig 
    instance object ObjectAssignToObject() cil managed 
{ 
    // Method begins at RVA 0x207c 
    // Code size 7 (0x7) 
    .maxstack 8 

    IL_0000: ldarg.0 
    IL_0001: ldfld object ConversionTest.Program::_Object 
    IL_0006: ret 
} 

Почему Conv.R4/Conv.R8 ведет себя таким образом? Является ли этот код операции важным или может быть безопасно удалено/обрезано?

+0

Пожалуйста, разместите объявление обо всех этих полях. –

+0

@ LasseV.Karlsen Не аргумент 'ldfld' уже показывает объявленный тип поля? 'ldfld float32 ConversionTest.Program :: _ Float' указывает на загрузку' float32', а затем его вызов 'conv.r4'. – InBetween

ответ

3

Это link объясняет, что на самом деле происходит. Процитировать наиболее значимую часть:

Спецификация CLI в разделе 12.1.3 диктует точную точность чисел с плавающей точкой, поплавок и двойную, при использовании в местах хранения. Однако он позволяет превышать точность, когда числа с плавающей запятой используются в других местах, таких как стек выполнения, аргументы возвращают значения и т. Д. ... Какая точность используется для среды выполнения и базового оборудования. Эта дополнительная точность может привести к незначительным различиям в оценках с плавающей точкой между разными машинами или временем автономной работы.

Здесь приводятся дополнительные инструкции conv.r4 и conv.r8. Обычно они используются для принудительного использования значений без плавающей запятой в значениях с плавающей запятой. Один из их побочных эффектов, хотя это результирующее значение, будет иметь точную точность, указанную типом. Это означает, что при применении к значению с плавающей запятой в стеке оценки он усекает его до указанной точности.

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

Еще одна интересная информация об этой же ссылке заключается в том, что это не гарантируется спецификацией компилятора C#, и на данный момент это деталь реализации. Вероятно, это не изменится, поскольку это поведение всех предыдущих компиляторов, а не только VS 2017.

+1

Существует обсуждение [здесь] (https://github.com/dotnet/roslyn/issues/6309) о том, когда и когда эти инструкции являются излишними. Также может быть обновление спецификации C#. –

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