Ошибка компилятора JIT .Net?

Результат следующего кода отличается, если он запущен с отладчиком в фоновом режиме или без него. Разница только там, если включена оптимизация.

Вот результат:

-> с оптимизацией: 1000 2008 3016 1001 2009 3007 ...

-> без оптимизации (как и ожидалось) 1000 1008 1016 1001 1009 1017 ...

Код:

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace OptimizerTest
{   
    public class Test
    {
        int dummy;

        public void TestFunction(int stepWidth)
        // stepWidth must be a parameter
        {
            for (int step = 0; step < stepWidth; step++)
            {
                dummy = step + 1000;
                // addition with constant ( same value as later !)
                for (int x = 0; x < 20; x += stepWidth)
                {
                    int index = x + 1000 + step;
                    // constant must be same as above and ?!?! 
                    // int index = x + step + 1000; works !!!!!
                    Console.Write("\n\r" + index);
                }
            }
        }

        [MethodImpl(MethodImplOptions.NoOptimization)]
        public void TestFunctionNoOptimization(int stepWidth)
        {
            for (int step = 0; step < stepWidth; step++)
            {
                dummy = step + 1000;
                for (int x = 0; x < 20; x += stepWidth)
                {
                    int index = x + 1000 + step;                        
                    Console.Write("\n\r" + index);
                }
            }
        }
    }

    class Program
    {
        /// <summary>
        /// Result differs from Start with F5 to Ctrl-F5
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            Test test = new Test();
            Console.Write("\n\r---------\n\roptimized result\n\r-------------" );
            test.TestFunction(8);
            Console.Write("\n\r---------\n\rnot optimized result\n\r-------------");
            test.TestFunctionNoOptimization(8);
            Console.Write("\n\r---------\n\rpress any key");
            Console.ReadKey();
        }
    }
}

Поведение ошибки зависит от количества итераций внутреннего цикла (x ‹5 все работает нормально). Очень интересно то, что не происходит, когда я использую

   int index = x + step + 1000; 

вместо

   int index = x + 1000 + step; 

Я работаю с Visual Studio 2010 SP1 и пробовал его с .NET Framework от 2.0 до 4.0.3. Я всегда видел один и тот же результат.

Кто-нибудь знает об этом баге или может воспроизвести?


person Jan    schedule 20.12.2013    source источник
comment
Я получаю те же результаты, что и ваш пример без оптимизации с VS 2012 для обеих функций, поэтому не могу воспроизвести.   -  person Vlad    schedule 20.12.2013
comment
Попробуйте использовать ILSpy для декомпиляции созданной библиотеки DLL и посмотрите, что у вас получится. (Или поместите свою dll где-нибудь, чтобы мы могли ее посмотреть, мне любопытно: D)   -  person Rawling    schedule 20.12.2013
comment
ILSpy возвращает мне то, что я вложил. Кажется, все в порядке.   -  person Jan    schedule 20.12.2013
comment
@UweKeim Я хочу отличаться: D   -  person Rawling    schedule 20.12.2013
comment
то же самое для не может быть воспроизведено в 2012 году. Также то же самое для размещения exe где-нибудь, чтобы его можно было протестировать на других машинах и / или исследуемой IL.   -  person Chris    schedule 20.12.2013
comment
@UweKeim: Не стесняйтесь указать, какую ошибку совершил Ян, если хотите. ;-)   -  person Chris    schedule 20.12.2013
comment
Мы тестировали код на разных ПК в нашей компании. мы скомпилировали его в разных Studios 2010 (но с той же конфигурацией), но у нас всегда был один и тот же неправильный результат :-(. Мой обходной путь (этап обмена и 1000) работает в моем основном приложении (где 1000 - это параметр функции) нормально ... но я хотел бы знать, что не так.Это происходит только тогда, когда программа запускается без отладки!   -  person Jan    schedule 20.12.2013
comment
Я получаю тот же результат, что и вы, с VS 2010 SP1 на сборках x64, но он отлично работает со сборками x86.   -  person StevieB    schedule 20.12.2013
comment
Я дважды проверил настройки своего проекта и увидел, что целевые настройки платформы - x64. С x86 все в порядке, но с x64 не работает. tx для теста   -  person Jan    schedule 20.12.2013
comment
Эта ошибка действительно воспроизводится (на 64-битном оборудовании) с VS2013, нацеленным на .NET 4.5.1, если конфигурация - Release, а цель - x64. Он также воспроизводится с Release и target Any CPU, если параметр Prefer 32-bit не выбран (не отмечен). Он не будет воспроизводиться при компиляции для 32-битной версии.   -  person Jeppe Stig Nielsen    schedule 20.12.2013


Ответы (1)


Да, это определенно ошибка оптимизатора джиттера. Причина, по которой у других пользователей SO возникают проблемы с воспроизведением этого, заключается в том, что эта ошибка проявляется только в джиттере x64. Вы должны установить цель платформы проекта на AnyCPU, снимите отметку с опции «Предпочитать 32-битную» на VS2012 и более поздних версиях.

Я недостаточно внимательно изучил основную причину, но, похоже, она не пытается устранить распространенное подвыражение step + 1000. Устранение подвыражений - одна из стандартных оптимизаций джиттера. Но он неправильно включает код выражения внутри цикла вместо того, чтобы держать его вне цикла, как написано. Например, вы увидите, что ошибка исчезла, когда вы напишете:

  dummy = step + 999;

Эта ошибка все еще присутствует в последней версии .NET 4.5.1 (clrjit.dll, v4.0.30319.34003 на моей машине), также присутствует в джиттере v2 (mscorjit.dll, v2.0.50727.7905 на моей машине).

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

  int index = x + dummy;  

Об этом следует сообщить в Microsoft, вы можете сделать это, разместив отчет об ошибке на сайте connect.microsoft.com. Если вы не хотите тратить время, дайте мне знать, и я позабочусь об этом.

person Hans Passant    schedule 20.12.2013
comment
Об этом действительно следует сообщить. Однако я сомневаюсь, что они это исправят, учитывая, что это, по-видимому, первый раз, когда он появляется, и учитывая, что они все равно работают над новым джитом. Кто-нибудь пробовал это с рюДжитом? (блоги .msdn.com / b / dotnet / archive / 2013/11/18 /). - person Kris Vandermotten; 20.12.2013
comment
tx. Сообщите, пожалуйста, Microsoft. Правильно ли я понял, что ошибка есть и в VS2012 x64? - person Jan; 20.12.2013
comment
@Hans Можно ли дать нам какой-нибудь псевдокод, описывающий некорректный вывод джиттера? Я изо всех сил пытаюсь решить это сам. - person Rawling; 20.12.2013
comment
Просто переместите назначение фиктивной переменной внутри цикла и используйте index = x + dummy для логического воспроизведения. - person Hans Passant; 20.12.2013
comment
Да, в RyuJIT этого не происходит. Насколько это ужасно, если мы просто сосредоточимся на подготовке RyuJIT и не решим проблему? (Серьезно, исправлять что-то в JIT64 неприятно :-) - person Kevin Frei; 11.02.2014
comment
RyuJIT был создан на основе кодовой базы джиттера x86. Так что, наверное, этой проблемы нет. Не то чтобы воодушевляюще, кстати, у джиттера x64 в целом есть лучший оптимизатор. - person Hans Passant; 11.02.2014
comment
@Hans, я полностью согласен с вашей оценкой RyuJIT CTP1 по сравнению с JIT64 в части оптимизации. CTP2 скоро появится и должен помочь вам передумать. Когда я говорю о подготовке RyuJIT, я имею в виду, что RyuJIT лучше, чем JIT64, как по скорости компиляции (готово), так и по возможностям оптимизации (большой прогресс). - person Kevin Frei; 11.02.2014