2013-03-14 3 views
2

У меня есть простой для цикла с доступом к массиву, написанный с использованием ILGenerator. Когда метод создается с помощью этого точного кода, я открываю разборку, и все в порядке, проверка границ массива не выполняется.Проверка границ массива в DynamicAssembly работает только тогда, когда оценочный стек пуст

Но когда я впервые поместил экземпляр другого класса в стек оценки, затем запустил цикл, он проверяет границы массива. Я бегу от выпуска.

Любая идея, почему? Я уже прочитал сообщение в блоге о проверках границ массивов: http://blogs.msdn.com/b/clrcodegeneration/archive/2009/08/13/array-bounds-check-elimination-in-the-clr.aspx

 // Uncomment this to enable bound checks, type of arg0 is some my class 
     //il.Emit(OpCodes.Ldarg_0); 

     var startLbl = il.DefineLabel(); 
     var testLbl = il.DefineLabel(); 
     var index = il.DeclareLocal(typeof(Int32)); 
     var arr = il.DeclareLocal(typeof(Int32).MakeArrayType()); 

     // arr = new int[4]; 
     il.Emit(OpCodes.Ldc_I4_4); 
     il.Emit(OpCodes.Newarr, typeof(Int32)); 
     il.Emit(OpCodes.Stloc, arr); 

     // Index = 0 
     il.Emit(OpCodes.Ldc_I4_0); // Push index 
     il.Emit(OpCodes.Stloc, index); // Pop index, store 

     il.Emit(OpCodes.Br_S, testLbl); // Go to test 

     // Begin for 
     il.MarkLabel(startLbl); 

     // Load array, index 
     il.Emit(OpCodes.Ldloc, arr); 
     il.Emit(OpCodes.Ldloc, index); 

     // Now on stack: array, index 
     // Load element 
     il.Emit(OpCodes.Ldelem_I4); 
     // Nothing here now, later some function call 
     il.Emit(OpCodes.Pop); 

     // Index++ 
     il.Emit(OpCodes.Ldloc, index); 
     il.Emit(OpCodes.Ldc_I4_1); 
     il.Emit(OpCodes.Add); 
     il.Emit(OpCodes.Stloc, index); 

     il.MarkLabel(testLbl); 
     // Load index, count, test for end 
     il.Emit(OpCodes.Ldloc, index); 
     il.Emit(OpCodes.Ldloc, arr); 
     il.Emit(OpCodes.Ldlen); // Push len 
     il.Emit(OpCodes.Conv_I4); // Push len 
     il.Emit(OpCodes.Blt_S, startLbl); 
     // End for 

     // Remove instance added on top 
     //il.Emit(OpCodes.Pop); 

Как сгенерировать IL-код, лучше держать экземпляры классов на стеке вычислений или в локальном переменных?

E.g. Я получаю экземпляр, просматриваю поля, для каждого поля делаю что-нибудь и возвращаю. Я просто сохранил экземпляр в стеке и назвал Emit (OpCodes.Dup) перед чтением следующего поля. Но это кажется неправильным (по крайней мере, для случая, упомянутого выше).

Любые статьи/сообщения в блоге о генерации (эффективный/хорошо сформированный) код IL оценен.

ответ

2

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

Из-за того, что я видел в ILSpy, csc предпочитает также местных жителей, хотя я должен признать, что, когда я смотрел на IL, а не декомпилировал C#, в основном это был код отладки. Поскольку JIT, вероятно, написана с ожиданием, что он будет в основном работать над выходом компиляторов Microsoft, было бы не удивительно, если бы он не распознал конструкции цикла, которые не соответствовали тому, что испускали их компиляторы. Очень правдоподобно, что дополнительная запись в стеке препятствует способности JIT распознавать, что она может устранить проверку границ.

0

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

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

Дрожание может иметь очень трудное время с кодом, который является недействительным (например, неправильный стек, как unboxed T на стек ожидается boxed T на общий код, который работает только с объектом.) И особенно непроверяемый, что не является нормальным узором. (например, небезопасный код, который никогда не произойдет через C# или C++/CLI). Вы всегда должны пытаться иметь 0 ошибок в peverify, если вы их не ожидаете. (например, calli)

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

  • Нет связанных вопросов^_^