Подтвердить что ты не робот

Может ли оператор С# '' страдать при оптимизации режима выпуска на .NET 4?

Ниже приведена простая испытательная арматура. Он успешно работает в сборках Debug и не работает в версиях Release (VS2010,.NET4, x64):

[TestFixture]
public sealed class Test
{
    [Test]
    public void TestChecker()
    {
        var checker = new Checker();
        Assert.That(checker.IsDateTime(DateTime.Now), Is.True);
    }
}

public class Checker
{
    public bool IsDateTime(object o)
    {
        return o is DateTime;
    }
}

Кажется, что оптимизация кода вызывает некоторый хаос; если я отключу его в сборке Release, он также работает. Это было довольно странно для меня. Ниже я использовал ILDASM, чтобы разобрать 2 версии сборки:

Отладка IL:

.method public hidebysig instance bool IsDateTime(object o) cil managed
{
  // Code size       15 (0xf)
  .maxstack  2
  .locals init (bool V_0)
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  isinst     [mscorlib]System.DateTime
  IL_0007:  ldnull
  IL_0008:  cgt.un
  IL_000a:  stloc.0
  IL_000b:  br.s       IL_000d
  IL_000d:  ldloc.0
  IL_000e:  ret
} // end of method Validator::IsValid

Релиз IL:

.method public hidebysig instance bool IsDateTime(object o) cil managed
{
  // Code size       10 (0xa)
  .maxstack  8
  IL_0000:  ldarg.1
  IL_0001:  isinst     [mscorlib]System.DateTime
  IL_0006:  ldnull
  IL_0007:  cgt.un
  IL_0009:  ret
} // end of method Validator::IsValid

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

Спасибо заранее.

4b9b3361

Ответ 1

Эта ошибка уже появилась в этом СО-вопросе Джейкобом Стэнли. Джейкоб уже сообщил об ошибке, и Microsoft подтвердила, что это действительно ошибка в CLR JR. Microsoft сказала следующее:

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

Еще раз спасибо за сообщение о проблеме.

Вы должны иметь возможность обойти ошибку, добавив следующий атрибут в TestChecker():

[MethodImpl(MethodImplOptions.NoInlining)]

Ответ 2

Это не связано с компилятором С#, IL идентичен. Вы обнаружили ошибку в оптимизаторе джиттера .NET 4.0. Вы можете воспроизвести его в Visual Studio. Tools + Options, Debugging, General, отключите опцию "Подавить оптимизацию JIT при загрузке модуля" и запустите сборку Release, чтобы воспроизвести сбой.

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

Чистое обходное решение не так просто, вы можете сделать это, подавив вставку. Вот так:

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
    public bool IsDateTime(object o) {
        return o is DateTime;
    }

Вы можете сообщить об ошибке на веб-сайте connect.microsoft.com. Дайте мне знать, если вы этого не хотите, и я позабочусь об этом.


Ничего, это было уже сделано. Он не был исправлен в релизе технического обслуживания, который был включен в VS2010 SP1.


Эта ошибка исправлена, я больше не могу ее воспроизвести. Моя текущая версия clrjit.dll 4.0.30319.237 от 17 мая 2011 года. Я не могу точно сказать, какое обновление отремонтировано. 5 августа 2011 года я получил обновление для системы безопасности, которое обновило clrjit.dll до версии 235 с датой 12 апреля, что было бы самым ранним.

Ответ 3

Хранилище и загрузка по существу являются целыми до тех пор, пока управление потоком не идет, но, вероятно, массирует некоторые кэши процессора каким-то образом. Фактический поток просто загружает аргумент в стек, проверяет, является ли он экземпляром (который возвращает null или экземпляр), толкает нуль в стек и сравнивает (больше, чем), что приводит к тому, что в стеке остается логическое значение.

Теперь, что делает JITter, это совсем другая история (и будет зависеть от того, какую платформу вы используете. JITTER будет делать всевозможные сумасшедшие вещи во имя производительности (наша команда недавно получила удар, потому что оптимизация хвоста измененный для оптимизации границ домена, который разбил GetCallingAssembly()). Возможно, JITter вставляет IsDateTime, замечая, что нет способа, которым он не может быть DateTime, и просто нажимает true на стек.

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

Я понимаю, что не отвечает, почему ваш код ломается.

Ответ 4

Для справки я проверил с моно

  • Mono JIT-компилятор версии 2.6.7 (Debian 2.6.7-3ubuntu1)
  • Mono JIT компилятор версии 2.8.2 (sehe/d1c74ad пт 18 фев 21:46:52 CET 2011)

Оба не представили никаких проблем. Вот IL с оптимизацией в 2.8.2

.method public hidebysig 
       instance default bool IsDateTime (object o)  cil managed 
{
    // Method begins at RVA 0x2130
    // Code size 10 (0xa)
    .maxstack 8
    IL_0000:  ldarg.1 
    IL_0001:  isinst [mscorlib]System.DateTime
    IL_0006:  ldnull 
    IL_0007:  cgt.un 
    IL_0009:  ret 
} // end of method Checker::IsDateTime

Без оптимизаций точно такое же

Вот результат монодробированного кода для этого IL:

00000130 <TestData_Checker_IsDateTime_object>:
     130:       55                      push   %ebp
     131:       8b ec                   mov    %esp,%ebp
     133:       53                      push   %ebx
     134:       56                      push   %esi
     135:       83 ec 10                sub    $0x10,%esp
     138:       e8 00 00 00 00          call   13d <TestData_Checker_IsDateTime_object+0xd>
     13d:       5b                      pop    %ebx
     13e:       81 c3 03 00 00 00       add    $0x3,%ebx
     144:       8b 45 0c                mov    0xc(%ebp),%eax
     147:       89 45 f4                mov    %eax,-0xc(%ebp)
     14a:       8b 75 0c                mov    0xc(%ebp),%esi
     14d:       83 7d 0c 00             cmpl   $0x0,0xc(%ebp)
     151:       74 1a                   je     16d <TestData_Checker_IsDateTime_object+0x3d>
     153:       8b 45 f4                mov    -0xc(%ebp),%eax
     156:       8b 00                   mov    (%eax),%eax
     158:       8b 00                   mov    (%eax),%eax
     15a:       8b 40 08                mov    0x8(%eax),%eax
     15d:       8b 48 08                mov    0x8(%eax),%ecx
     160:       8b 93 10 00 00 00       mov    0x10(%ebx),%edx
     166:       33 c0                   xor    %eax,%eax
     168:       3b ca                   cmp    %edx,%ecx
     16a:       0f 45 f0                cmovne %eax,%esi
     16d:       85 f6                   test   %esi,%esi
     16f:       0f 97 c0                seta   %al
     172:       0f b6 c0                movzbl %al,%eax
     175:       8d 65 f8                lea    -0x8(%ebp),%esp
     178:       5e                      pop    %esi
     179:       5b                      pop    %ebx
     17a:       c9                      leave  
     17b:       c3                      ret    
     17c:       8d 74 26 00             lea    0x0(%esi,%eiz,1),%esi